@fsegurai/marked-extended-tabs 15.1.0 → 17.0.0-beta.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.
@@ -0,0 +1,462 @@
1
+ <!-- SECTION:CUSTOM_INTRO -->
2
+ ---
3
+
4
+ This extension enables you to create interactive tabbed content sections with support for custom icons, animations, and
5
+ nested Markdown content. All tabs are implemented using CSS-only interactions, providing excellent performance and
6
+ accessibility.
7
+
8
+ <!-- SECTION:CUSTOM_TOC_USAGE -->
9
+
10
+ - [Aliases](#aliases)
11
+ - [Configuration Options](#configuration-options)
12
+ - [Animation Types](#animation-types)
13
+ - [Advanced Examples](#advanced-examples)
14
+
15
+ <!-- SECTION:CUSTOM_BASIC_USAGE -->
16
+ **`tabs` is the identifier for the extended tabs block, and `tab` for individual tab items.**
17
+
18
+ <!-- SECTION:CUSTOM_USAGE_EXAMPLE -->
19
+ const exampleMarkdown = `
20
+ ::::tabs
21
+ :::tab{label="JavaScript" active="true"}
22
+ \`\`\`javascript
23
+ console.log("Hello from JavaScript!");
24
+ \`\`\`
25
+ :::tabend
26
+
27
+ :::tab{label="Python" icon="🐍"}
28
+ \`\`\`python
29
+ print("Hello from Python!")
30
+ \`\`\`
31
+ :::tabend
32
+
33
+ :::tab{label="HTML" icon="🌐"}
34
+ \`\`\`html
35
+ <h1>Hello from HTML!</h1>
36
+ \`\`\`
37
+ :::tabend
38
+ ::::tabsend
39
+ `;
40
+
41
+ marked.parse(exampleMarkdown);
42
+
43
+ <!-- SECTION:CUSTOM_USAGE_NOTES -->
44
+ The extension supports **nested Markdown content** within tabs, including code blocks, tables, lists, and even other
45
+ marked extensions. Tabs use a CSS-only implementation for optimal performance and can be styled to match your design
46
+ system.
47
+
48
+ ### Styling Your Tabs
49
+
50
+ This extension injects minimal structural CSS for functionality. Visual styling is completely separated for maximum
51
+ flexibility.
52
+
53
+ #### Generated HTML Structure
54
+
55
+ ```html
56
+
57
+ <div class="marked-extended-tabs-container" id="tabs-{id}">
58
+ <nav class="marked-extended-tabs-nav" role="tablist">
59
+ <input type="radio" name="tabs-{id}" id="tab-{id}-0" class="marked-extended-tabs-input" checked>
60
+ <label for="tab-{id}-0" class="marked-extended-tabs-label" role="tab">
61
+ <span class="marked-extended-tabs-icon">🔧</span>
62
+ <span class="marked-extended-tabs-label-text">JavaScript</span>
63
+ </label>
64
+ <!-- More tabs... -->
65
+ </nav>
66
+ <div class="marked-extended-tabs-content">
67
+ <div class="marked-extended-tabs-content-pane" role="tabpanel">
68
+ <!-- Tab 1 content -->
69
+ </div>
70
+ <div class="marked-extended-tabs-content-pane" role="tabpanel">
71
+ <!-- Tab 2 content -->
72
+ </div>
73
+ </div>
74
+ </div>
75
+ ```
76
+
77
+ #### CSS Classes Reference
78
+
79
+ | Class | Purpose | Element |
80
+ |-----------------------------------------------------|----------------------|----------------|
81
+ | `.marked-extended-tabs-container` | Main wrapper | Container |
82
+ | `.marked-extended-tabs-nav` | Tab navigation bar | Nav |
83
+ | `.marked-extended-tabs-input` | Hidden radio input | Input (hidden) |
84
+ | `.marked-extended-tabs-label` | Tab button/label | Label |
85
+ | `.marked-extended-tabs-label[aria-selected="true"]` | Active tab | Active Label |
86
+ | `.marked-extended-tabs-icon` | Tab icon | Span |
87
+ | `.marked-extended-tabs-label-text` | Tab text | Span |
88
+ | `.marked-extended-tabs-content` | Content area wrapper | Div |
89
+ | `.marked-extended-tabs-content-pane` | Individual tab pane | Div |
90
+
91
+ #### Complete Styling Example
92
+
93
+ ```css
94
+ /* Container */
95
+ .marked-extended-tabs-container {
96
+ border: 1px solid #ddd;
97
+ border-radius: 8px;
98
+ background: #fff;
99
+ overflow: hidden;
100
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
101
+ }
102
+
103
+ /* Navigation Bar */
104
+ .marked-extended-tabs-nav {
105
+ display: flex;
106
+ background: #f5f5f5;
107
+ border-bottom: 1px solid #ddd;
108
+ overflow-x: auto;
109
+ scrollbar-width: thin;
110
+ }
111
+
112
+ /* Hide radio inputs */
113
+ .marked-extended-tabs-input {
114
+ position: absolute;
115
+ opacity: 0;
116
+ pointer-events: none;
117
+ }
118
+
119
+ /* Tab Labels (Buttons) */
120
+ .marked-extended-tabs-label {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 0.5rem;
124
+ padding: 0.75rem 1.5rem;
125
+ cursor: pointer;
126
+ border-bottom: 2px solid transparent;
127
+ background: transparent;
128
+ transition: all 0.2s ease;
129
+ font-weight: 500;
130
+ color: #666;
131
+ white-space: nowrap;
132
+ }
133
+
134
+ .marked-extended-tabs-label:hover {
135
+ background: #e8e8e8;
136
+ color: #333;
137
+ }
138
+
139
+ /* Active Tab */
140
+ .marked-extended-tabs-label[aria-selected="true"] {
141
+ background: #fff;
142
+ border-bottom-color: #0066cc;
143
+ color: #0066cc;
144
+ font-weight: 600;
145
+ }
146
+
147
+ /* Tab Icon */
148
+ .marked-extended-tabs-icon {
149
+ font-size: 1.2rem;
150
+ opacity: 0.8;
151
+ }
152
+
153
+ /* Content Area */
154
+ .marked-extended-tabs-content {
155
+ padding: 1.5rem;
156
+ background: #fff;
157
+ }
158
+
159
+ .marked-extended-tabs-content-pane {
160
+ display: none;
161
+ }
162
+
163
+ /* Show active pane (controlled by radio input) */
164
+ .marked-extended-tabs-input:checked + .marked-extended-tabs-label + * .marked-extended-tabs-content-pane:nth-of-type(1),
165
+ #tab-id-0:checked ~ .marked-extended-tabs-content .marked-extended-tabs-content-pane:nth-of-type(1) {
166
+ display: block;
167
+ animation: tab-fade-in 0.3s ease;
168
+ }
169
+
170
+ /* Fade animation */
171
+ @keyframes tab-fade-in {
172
+ from {
173
+ opacity: 0;
174
+ transform: translateY(10px);
175
+ }
176
+ to {
177
+ opacity: 1;
178
+ transform: translateY(0);
179
+ }
180
+ }
181
+ ```
182
+
183
+ #### Variant: Compact Tabs
184
+
185
+ ```css
186
+ .marked-extended-tabs-container.tabs-compact .marked-extended-tabs-label {
187
+ padding: 0.5rem 0.75rem;
188
+ font-size: 0.875rem;
189
+ }
190
+
191
+ .marked-extended-tabs-container.tabs-compact .marked-extended-tabs-content {
192
+ padding: 0.75rem;
193
+ }
194
+ ```
195
+
196
+ #### Variant: Vertical Tabs
197
+
198
+ ```css
199
+ .marked-extended-tabs-container.tabs-vertical {
200
+ display: flex;
201
+ }
202
+
203
+ .marked-extended-tabs-container.tabs-vertical .marked-extended-tabs-nav {
204
+ flex-direction: column;
205
+ min-width: 200px;
206
+ border-right: 1px solid #ddd;
207
+ border-bottom: none;
208
+ }
209
+
210
+ .marked-extended-tabs-container.tabs-vertical .marked-extended-tabs-label {
211
+ border-bottom: none;
212
+ border-right: 2px solid transparent;
213
+ justify-content: flex-start;
214
+ }
215
+
216
+ .marked-extended-tabs-container.tabs-vertical .marked-extended-tabs-label[aria-selected="true"] {
217
+ border-right-color: #0066cc;
218
+ border-bottom: none;
219
+ }
220
+
221
+ .marked-extended-tabs-container.tabs-vertical .marked-extended-tabs-content {
222
+ flex: 1;
223
+ }
224
+ ```
225
+
226
+ #### Variant: Pills Style
227
+
228
+ ```css
229
+ .marked-extended-tabs-container.tabs-pills .marked-extended-tabs-nav {
230
+ gap: 0.5rem;
231
+ padding: 0.5rem;
232
+ background: #f8f9fa;
233
+ border-bottom: none;
234
+ }
235
+
236
+ .marked-extended-tabs-container.tabs-pills .marked-extended-tabs-label {
237
+ border-radius: 20px;
238
+ border: none;
239
+ padding: 0.5rem 1rem;
240
+ }
241
+
242
+ .marked-extended-tabs-container.tabs-pills .marked-extended-tabs-label:hover {
243
+ background: #e9ecef;
244
+ }
245
+
246
+ .marked-extended-tabs-container.tabs-pills .marked-extended-tabs-label[aria-selected="true"] {
247
+ background: #0066cc;
248
+ color: white;
249
+ border: none;
250
+ }
251
+ ```
252
+
253
+ #### Dark Mode Support
254
+
255
+ ```css
256
+ /* Light theme */
257
+ body.light .marked-extended-tabs-container {
258
+ background: #ffffff;
259
+ border-color: #ddd;
260
+ }
261
+
262
+ body.light .marked-extended-tabs-nav {
263
+ background: #f5f5f5;
264
+ border-bottom-color: #ddd;
265
+ }
266
+
267
+ body.light .marked-extended-tabs-label {
268
+ color: #333;
269
+ }
270
+
271
+ body.light .marked-extended-tabs-label:hover {
272
+ background: #e8e8e8;
273
+ }
274
+
275
+ body.light .marked-extended-tabs-content {
276
+ background: #ffffff;
277
+ }
278
+
279
+ /* Dark theme */
280
+ body.dark .marked-extended-tabs-container {
281
+ background: #22272e;
282
+ border-color: #444c56;
283
+ }
284
+
285
+ body.dark .marked-extended-tabs-nav {
286
+ background: #2d333b;
287
+ border-bottom-color: #444c56;
288
+ }
289
+
290
+ body.dark .marked-extended-tabs-label {
291
+ color: #d1d5da;
292
+ }
293
+
294
+ body.dark .marked-extended-tabs-label:hover {
295
+ background: #444c56;
296
+ }
297
+
298
+ body.dark .marked-extended-tabs-label[aria-selected="true"] {
299
+ background: #3b4551;
300
+ color: #58a6ff;
301
+ }
302
+
303
+ body.dark .marked-extended-tabs-content {
304
+ background: #22272e;
305
+ color: #d1d5da;
306
+ }
307
+ ```
308
+
309
+ #### Accessibility Enhancements
310
+
311
+ ```css
312
+ /* Focus styles for keyboard navigation */
313
+ .marked-extended-tabs-input:focus + .marked-extended-tabs-label {
314
+ outline: 2px solid #0066cc;
315
+ outline-offset: 2px;
316
+ }
317
+
318
+ /* Remove outline for mouse users */
319
+ .marked-extended-tabs-input:focus:not(:focus-visible) + .marked-extended-tabs-label {
320
+ outline: none;
321
+ }
322
+ ```
323
+
324
+ #### Copy Demo Theme
325
+
326
+ For complete styling with all
327
+ variants: [tabs-theme.css](https://github.com/fsegurai/marked-extensions/blob/main/demo/styles/extensions/tabs-theme.css)
328
+
329
+ Check the [demo](https://fsegurai.github.io/marked-extensions) to see all tab variants in action.
330
+
331
+ <!-- SECTION:CUSTOM_SECTIONS -->
332
+
333
+ ### Aliases
334
+
335
+ The `tabs` block can be rendered using alternative aliases for start and end blocks:
336
+
337
+ * Start Aliases
338
+ - `:tbs`
339
+ - `:tabs`
340
+ * End Aliases
341
+ - `:tbsend`
342
+ - `:tabsend`
343
+
344
+ Individual tabs can use these aliases:
345
+
346
+ * Tab Start Aliases
347
+ - `:tab`
348
+ * Tab End Aliases
349
+ - `:tabend`
350
+
351
+ ### Configuration Options
352
+
353
+ The marked-extended-tabs extension accepts the following configuration options:
354
+
355
+ - `className`: The base CSS class name for tabs container. Defaults to 'marked-extended-tabs-container'.
356
+ - `persistSelection`: Whether to remember tab selection across page reloads. Defaults to false.
357
+ - `animation`: Animation type for tab transitions. Defaults to 'fade'. See [Animation Types](#animation-types).
358
+ - `autoActivate`: Automatically activate the first tab if none is explicitly active. Defaults to true.
359
+ - `template`: A custom HTML template for the tabs structure. Defaults to the built-in template.
360
+ - `customizeToken`: A function that allows you to customize the token object before rendering. Defaults to null.
361
+ - `injectStyles`: Whether to inject default CSS styles. Defaults to true.
362
+
363
+ Tab syntax parameters:
364
+
365
+ - `label`: The display text for the tab header. Defaults to 'Tab N' where N is the tab number.
366
+ - `active`: Whether this tab is active by default ("true" or "false"). Defaults to "false".
367
+ - `icon`: An optional icon (emoji or Unicode) to display in the tab header. No default.
368
+
369
+ ### Animation Types
370
+
371
+ The extension supports three animation types:
372
+
373
+ - `'fade'`: Smooth fade in/out transition between tabs (default)
374
+ - `'slide'`: Horizontal sliding animation between tab contents
375
+ - `'none'`: No animation, instant tab switching
376
+
377
+ Example with custom animation:
378
+
379
+ ```javascript
380
+ marked.use(markedExtendedTabs({
381
+ animation: 'slide',
382
+ autoActivate: true
383
+ }));
384
+ ```
385
+
386
+ ### Advanced Examples
387
+
388
+ #### Tabs with Icons and Mixed Content
389
+
390
+ ```markdown
391
+ ::::tabs
392
+ :::tab{label="Overview" icon="📋" active="true"}
393
+
394
+ ## Project Overview
395
+
396
+ This project demonstrates the power of **nested Markdown** within tabs.
397
+
398
+ - ✅ Code highlighting
399
+ - ✅ Tables and lists
400
+ - ✅ Images and links
401
+ - ✅ Other extensions
402
+
403
+ [Learn more →](https://example.com)
404
+ :::tabend
405
+
406
+ :::tab{label="API Reference" icon="⚙️"}
407
+
408
+ ### REST API Endpoints
409
+
410
+ | Method | Endpoint | Description |
411
+ |--------|----------|-------------|
412
+ | GET | `/api/users` | List all users |
413
+ | POST | `/api/users` | Create user |
414
+ | PUT | `/api/users/{id}` | Update user |
415
+
416
+ > **Note**: All endpoints require authentication.
417
+ :::tabend
418
+
419
+ :::tab{label="Examples" icon="💻"}
420
+
421
+ ### Code Examples
422
+
423
+ JavaScript implementation:
424
+ \`\`\`javascript
425
+ const api = new APIClient({
426
+ baseUrl: 'https://api.example.com',
427
+ apiKey: 'your-key-here'
428
+ });
429
+
430
+ const users = await api.get('/users');
431
+ console.log(users);
432
+ \`\`\`
433
+
434
+ Python implementation:
435
+ \`\`\`python
436
+ import requests
437
+
438
+ response = requests.get('https://api.example.com/users')
439
+ users = response.json()
440
+ print(users)
441
+ \`\`\`
442
+ :::tabend
443
+ ::::tabsend
444
+ ```
445
+
446
+ #### Configuration Example
447
+
448
+ ```javascript
449
+ import {marked} from "marked";
450
+ import markedExtendedTabs from "@fsegurai/marked-extended-tabs";
451
+
452
+ marked.use(markedExtendedTabs({
453
+ className: 'my-custom-tabs',
454
+ animation: 'slide',
455
+ persistSelection: true,
456
+ autoActivate: true,
457
+ customizeToken: (token) => {
458
+ // Add custom logic here
459
+ console.log('Processing tabs token:', token);
460
+ }
461
+ }));
462
+ ```