create-template-html-css 1.6.4 → 1.8.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,660 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Components Gallery - Create Template HTML/CSS</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ :root {
15
+ --primary: #667eea;
16
+ --secondary: #764ba2;
17
+ --success: #48bb78;
18
+ --bg-light: #f7fafc;
19
+ --bg-dark: #1a202c;
20
+ --text-light: #2d3748;
21
+ --text-dark: #e2e8f0;
22
+ }
23
+
24
+ @media (prefers-color-scheme: dark) {
25
+ :root {
26
+ --bg-light: #1a202c;
27
+ --text-light: #e2e8f0;
28
+ }
29
+ }
30
+
31
+ html {
32
+ scroll-behavior: smooth;
33
+ }
34
+
35
+ body {
36
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
37
+ background: var(--bg-light);
38
+ color: var(--text-light);
39
+ line-height: 1.6;
40
+ }
41
+
42
+ .container {
43
+ max-width: 1400px;
44
+ margin: 0 auto;
45
+ padding: 20px;
46
+ }
47
+
48
+ header {
49
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
50
+ color: white;
51
+ padding: 80px 20px;
52
+ text-align: center;
53
+ border-radius: 12px;
54
+ margin-bottom: 50px;
55
+ }
56
+
57
+ h1 {
58
+ font-size: 2.8rem;
59
+ margin-bottom: 15px;
60
+ }
61
+
62
+ .subtitle {
63
+ font-size: 1.2rem;
64
+ opacity: 0.95;
65
+ margin-bottom: 30px;
66
+ }
67
+
68
+ .stats {
69
+ display: grid;
70
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
71
+ gap: 20px;
72
+ margin-top: 30px;
73
+ }
74
+
75
+ .stat {
76
+ text-align: center;
77
+ padding: 15px;
78
+ background: rgba(255, 255, 255, 0.1);
79
+ border-radius: 8px;
80
+ backdrop-filter: blur(10px);
81
+ }
82
+
83
+ .stat-num {
84
+ font-size: 2.5rem;
85
+ font-weight: bold;
86
+ }
87
+
88
+ .stat-label {
89
+ font-size: 0.9rem;
90
+ opacity: 0.9;
91
+ }
92
+
93
+ .controls {
94
+ display: flex;
95
+ gap: 15px;
96
+ margin-bottom: 40px;
97
+ flex-wrap: wrap;
98
+ align-items: center;
99
+ justify-content: center;
100
+ }
101
+
102
+ .search-input,
103
+ .filter-select {
104
+ padding: 12px 16px;
105
+ border: 2px solid #e2e8f0;
106
+ border-radius: 8px;
107
+ font-size: 1rem;
108
+ background: white;
109
+ }
110
+
111
+ .search-input:focus,
112
+ .filter-select:focus {
113
+ outline: none;
114
+ border-color: var(--primary);
115
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
116
+ }
117
+
118
+ .btn {
119
+ padding: 12px 24px;
120
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
121
+ color: white;
122
+ border: none;
123
+ border-radius: 8px;
124
+ cursor: pointer;
125
+ font-weight: 600;
126
+ transition: transform 0.2s, box-shadow 0.2s;
127
+ }
128
+
129
+ .btn:hover {
130
+ transform: translateY(-2px);
131
+ box-shadow: 0 8px 16px rgba(102, 126, 234, 0.3);
132
+ }
133
+
134
+ .gallery {
135
+ display: grid;
136
+ grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
137
+ gap: 25px;
138
+ margin-bottom: 60px;
139
+ }
140
+
141
+ .card {
142
+ background: white;
143
+ border-radius: 12px;
144
+ overflow: hidden;
145
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
146
+ transition: all 0.3s ease;
147
+ border: 2px solid transparent;
148
+ }
149
+
150
+ @media (prefers-color-scheme: dark) {
151
+ .card {
152
+ background: #2d3748;
153
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
154
+ }
155
+ }
156
+
157
+ .card:hover {
158
+ transform: translateY(-8px);
159
+ box-shadow: 0 12px 24px rgba(102, 126, 234, 0.15);
160
+ border-color: var(--primary);
161
+ }
162
+
163
+ .card-header {
164
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
165
+ color: white;
166
+ padding: 20px;
167
+ display: flex;
168
+ justify-content: space-between;
169
+ align-items: center;
170
+ }
171
+
172
+ .card-title {
173
+ font-size: 1.4rem;
174
+ font-weight: 700;
175
+ }
176
+
177
+ .card-id {
178
+ background: rgba(255, 255, 255, 0.2);
179
+ padding: 4px 12px;
180
+ border-radius: 20px;
181
+ font-size: 0.75rem;
182
+ font-weight: 600;
183
+ }
184
+
185
+ .card-body {
186
+ padding: 20px;
187
+ }
188
+
189
+ .category-badge {
190
+ display: inline-block;
191
+ background: rgba(102, 126, 234, 0.1);
192
+ color: var(--primary);
193
+ padding: 6px 14px;
194
+ border-radius: 20px;
195
+ font-size: 0.8rem;
196
+ font-weight: 600;
197
+ margin-bottom: 12px;
198
+ }
199
+
200
+ .description {
201
+ margin-bottom: 16px;
202
+ line-height: 1.5;
203
+ }
204
+
205
+ .features-list {
206
+ list-style: none;
207
+ margin-bottom: 16px;
208
+ }
209
+
210
+ .features-list li {
211
+ padding: 6px 0 6px 24px;
212
+ position: relative;
213
+ font-size: 0.9rem;
214
+ }
215
+
216
+ .features-list li:before {
217
+ content: "✓";
218
+ position: absolute;
219
+ left: 0;
220
+ color: var(--success);
221
+ font-weight: bold;
222
+ }
223
+
224
+ .code-block {
225
+ background: #f7fafc;
226
+ padding: 12px;
227
+ border-radius: 6px;
228
+ font-family: "Courier New", monospace;
229
+ font-size: 0.8rem;
230
+ overflow-x: auto;
231
+ border: 1px solid #e2e8f0;
232
+ position: relative;
233
+ margin-bottom: 8px;
234
+ }
235
+
236
+ @media (prefers-color-scheme: dark) {
237
+ .code-block {
238
+ background: #2d3748;
239
+ color: #a0aec0;
240
+ }
241
+ }
242
+
243
+ .copy-btn {
244
+ position: absolute;
245
+ top: 6px;
246
+ right: 6px;
247
+ padding: 4px 8px;
248
+ background: var(--primary);
249
+ color: white;
250
+ border: none;
251
+ border-radius: 4px;
252
+ cursor: pointer;
253
+ font-size: 0.75rem;
254
+ opacity: 0;
255
+ transition: opacity 0.2s;
256
+ }
257
+
258
+ .code-block:hover .copy-btn {
259
+ opacity: 1;
260
+ }
261
+
262
+ .empty {
263
+ text-align: center;
264
+ padding: 60px 20px;
265
+ color: #718096;
266
+ }
267
+
268
+ .empty h2 {
269
+ margin-bottom: 10px;
270
+ }
271
+
272
+ .color-schemes-section {
273
+ margin: 60px 0;
274
+ padding: 40px;
275
+ background: rgba(102, 126, 234, 0.05);
276
+ border-radius: 12px;
277
+ }
278
+
279
+ .color-schemes-title {
280
+ font-size: 1.8rem;
281
+ margin-bottom: 30px;
282
+ text-align: center;
283
+ }
284
+
285
+ .schemes-grid {
286
+ display: grid;
287
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
288
+ gap: 20px;
289
+ }
290
+
291
+ .scheme-card {
292
+ background: white;
293
+ border-radius: 8px;
294
+ padding: 20px;
295
+ text-align: center;
296
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
297
+ transition: transform 0.2s, box-shadow 0.2s;
298
+ }
299
+
300
+ @media (prefers-color-scheme: dark) {
301
+ .scheme-card {
302
+ background: #2d3748;
303
+ }
304
+ }
305
+
306
+ .scheme-card:hover {
307
+ transform: translateY(-4px);
308
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
309
+ }
310
+
311
+ .scheme-preview {
312
+ display: flex;
313
+ gap: 10px;
314
+ margin-bottom: 15px;
315
+ justify-content: center;
316
+ height: 60px;
317
+ }
318
+
319
+ .color-swatch {
320
+ flex: 1;
321
+ border-radius: 6px;
322
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
323
+ }
324
+
325
+ .scheme-name {
326
+ font-size: 1.1rem;
327
+ font-weight: 700;
328
+ margin-bottom: 8px;
329
+ }
330
+
331
+ .scheme-description {
332
+ font-size: 0.85rem;
333
+ color: #718096;
334
+ line-height: 1.4;
335
+ }
336
+
337
+ footer {
338
+ border-top: 2px solid #e2e8f0;
339
+ padding: 40px;
340
+ text-align: center;
341
+ color: #718096;
342
+ margin-top: 60px;
343
+ }
344
+
345
+ .footer-links {
346
+ display: flex;
347
+ justify-content: center;
348
+ gap: 20px;
349
+ margin-top: 15px;
350
+ flex-wrap: wrap;
351
+ }
352
+
353
+ .footer-links a {
354
+ color: var(--primary);
355
+ text-decoration: none;
356
+ font-weight: 600;
357
+ transition: opacity 0.2s;
358
+ }
359
+
360
+ .footer-links a:hover {
361
+ opacity: 0.7;
362
+ }
363
+
364
+ .hidden {
365
+ display: none !important;
366
+ }
367
+
368
+ @media (max-width: 768px) {
369
+ h1 {
370
+ font-size: 2rem;
371
+ }
372
+
373
+ .gallery {
374
+ grid-template-columns: 1fr;
375
+ }
376
+
377
+ .controls {
378
+ flex-direction: column;
379
+ }
380
+
381
+ .search-input,
382
+ .filter-select {
383
+ width: 100%;
384
+ }
385
+ }
386
+ </style>
387
+ </head>
388
+ <body>
389
+ <div class="container">
390
+ <header>
391
+ <h1>🎨 Component Gallery</h1>
392
+ <p class="subtitle">26 Professional UI Components Ready to Use</p>
393
+ <div class="stats">
394
+ <div class="stat">
395
+ <div class="stat-num">26</div>
396
+ <div class="stat-label">Components</div>
397
+ </div>
398
+ <div class="stat">
399
+ <div class="stat-num">100%</div>
400
+ <div class="stat-label">Responsive</div>
401
+ </div>
402
+ <div class="stat">
403
+ <div class="stat-num">✓</div>
404
+ <div class="stat-label">Dark Mode</div>
405
+ </div>
406
+ <div class="stat">
407
+ <div class="stat-num">∞</div>
408
+ <div class="stat-label">Customizable</div>
409
+ </div>
410
+ </div>
411
+ </header>
412
+
413
+ <div class="controls">
414
+ <input type="text" id="search" class="search-input" placeholder="Search components..." />
415
+ <select id="categoryFilter" class="filter-select">
416
+ <option value="">All Categories</option>
417
+ <option value="basic">Basic Components</option>
418
+ <option value="auth">Authentication</option>
419
+ <option value="loading">Loading</option>
420
+ <option value="animation">Animations</option>
421
+ <option value="grid">Grid Layouts</option>
422
+ <option value="flex">Flexbox Layouts</option>
423
+ <option value="dom">DOM Manipulation</option>
424
+ </select>
425
+ <button class="btn" onclick="window.print()">🖨️ Print Gallery</button>
426
+ </div>
427
+
428
+ <div id="gallery" class="gallery"></div>
429
+ <div id="empty" class="empty hidden">
430
+ <h2>No components found</h2>
431
+ <p>Try adjusting your search or filter</p>
432
+ </div>
433
+
434
+ <!-- Color Schemes Section -->
435
+ <div class="color-schemes-section">
436
+ <h2 class="color-schemes-title">🎨 Color Schemes Preview</h2>
437
+ <div class="schemes-grid" id="colorSchemesGrid"></div>
438
+ </div>
439
+ </div>
440
+
441
+ <footer>
442
+ <p>Made with ❤️ by Create Template HTML/CSS</p>
443
+ <p style="font-size: 0.9rem; margin-top: 10px;">
444
+ All components include HTML, CSS, and JavaScript ready for production use
445
+ </p>
446
+ <div class="footer-links">
447
+ <a href="https://github.com/benshabbat/create-template-html-css">GitHub</a>
448
+ <a href="https://www.npmjs.com/package/create-template-html-css">NPM</a>
449
+ <a href="README.md">Documentation</a>
450
+ </div>
451
+ </footer>
452
+
453
+ <script>
454
+ // Color Schemes Data
455
+ const COLOR_SCHEMES = {
456
+ vibrant: {
457
+ name: "Vibrant",
458
+ primary: "#FF6B6B",
459
+ secondary: "#4ECDC4",
460
+ description: "Bold reds and teals - energetic and modern"
461
+ },
462
+ pastel: {
463
+ name: "Pastel",
464
+ primary: "#FFB3BA",
465
+ secondary: "#FFDFBA",
466
+ description: "Soft pinks and peaches - calm and friendly"
467
+ },
468
+ ocean: {
469
+ name: "Ocean",
470
+ primary: "#0066CC",
471
+ secondary: "#00CCFF",
472
+ description: "Deep blues and cyans - professional and cool"
473
+ },
474
+ sunset: {
475
+ name: "Sunset",
476
+ primary: "#FF6B35",
477
+ secondary: "#FFA500",
478
+ description: "Warm oranges and reds - energetic glow"
479
+ },
480
+ forest: {
481
+ name: "Forest",
482
+ primary: "#2D6A4F",
483
+ secondary: "#40916C",
484
+ description: "Green tones - natural and organic"
485
+ },
486
+ purple: {
487
+ name: "Purple",
488
+ primary: "#7209B7",
489
+ secondary: "#B5179E",
490
+ description: "Deep purples - elegant and premium"
491
+ },
492
+ minimal: {
493
+ name: "Minimal",
494
+ primary: "#1A1A1A",
495
+ secondary: "#666666",
496
+ description: "Grays and blacks - clean and simple"
497
+ },
498
+ coral: {
499
+ name: "Coral",
500
+ primary: "#FF6B9D",
501
+ secondary: "#FFA07A",
502
+ description: "Coral pinks and salmon - warm and playful"
503
+ },
504
+ teal: {
505
+ name: "Teal",
506
+ primary: "#008B8B",
507
+ secondary: "#20B2AA",
508
+ description: "Teal hues - balanced and professional"
509
+ },
510
+ neon: {
511
+ name: "Neon",
512
+ primary: "#FF006E",
513
+ secondary: "#00D9FF",
514
+ description: "Bright neon colors - bold and futuristic"
515
+ }
516
+ };
517
+
518
+ // Render color schemes
519
+ function renderColorSchemes() {
520
+ const grid = document.getElementById("colorSchemesGrid");
521
+ grid.innerHTML = "";
522
+
523
+ Object.entries(COLOR_SCHEMES).forEach(([key, scheme]) => {
524
+ const card = document.createElement("div");
525
+ card.className = "scheme-card";
526
+ card.innerHTML = `
527
+ <div class="scheme-preview">
528
+ <div class="color-swatch" style="background-color: ${scheme.primary};" title="${scheme.primary}"></div>
529
+ <div class="color-swatch" style="background-color: ${scheme.secondary};" title="${scheme.secondary}"></div>
530
+ </div>
531
+ <div class="scheme-name">${scheme.name}</div>
532
+ <div class="scheme-description">${scheme.description}</div>
533
+ <div style="font-size: 0.85rem; color: #666; margin-top: 8px;">
534
+ <div style="margin: 4px 0;">Primary: ${scheme.primary}</div>
535
+ <div style="margin: 4px 0;">Secondary: ${scheme.secondary}</div>
536
+ </div>
537
+ <div style="display: flex; gap: 8px; margin-top: 12px;">
538
+ <button class="copy-btn" onclick="copySchemeCommand('npx', '${key}')" style="flex: 1; font-size: 0.85rem;">
539
+ Copy npx
540
+ </button>
541
+ <button class="copy-btn" onclick="copySchemeCommand('local', '${key}')" style="flex: 1; font-size: 0.85rem; opacity: 0.7;">
542
+ Copy local
543
+ </button>
544
+ </div>
545
+ `;
546
+ grid.appendChild(card);
547
+ });
548
+ }
549
+
550
+ // Copy color scheme command
551
+ function copySchemeCommand(mode, schemeKey) {
552
+ let text;
553
+ if (mode === 'npx') {
554
+ text = `npx create-template create -c button -n my-button --color-scheme ${schemeKey}`;
555
+ } else {
556
+ text = `npm run create -- -c button -n my-button --color-scheme ${schemeKey}`;
557
+ }
558
+ navigator.clipboard.writeText(text);
559
+ event.target.textContent = "✓ Copied!";
560
+ setTimeout(() => (event.target.textContent = `Copy ${mode}`), 2000);
561
+ }
562
+
563
+ const components = [
564
+ { id: "button", name: "Button", cat: "basic", desc: "Versatile button component with multiple styles", feat: ["6 styles", "Hover effects", "Ripple animation"] },
565
+ { id: "card", name: "Card", cat: "basic", desc: "Flexible card with image and content", feat: ["6 variations", "Image support", "Responsive grid"] },
566
+ { id: "form", name: "Form", cat: "basic", desc: "Complete form with validation", feat: ["Field validation", "Custom fields", "Error messages"] },
567
+ { id: "navigation", name: "Navigation", cat: "basic", desc: "Responsive navbar with mobile menu", feat: ["Mobile menu", "Smooth scroll", "Customizable"] },
568
+ { id: "modal", name: "Modal", cat: "basic", desc: "Dialog modal with multiple close options", feat: ["ESC support", "Click outside", "Animations"] },
569
+ { id: "footer", name: "Footer", cat: "basic", desc: "Professional footer section", feat: ["Multi-column", "Social links", "Responsive"] },
570
+ { id: "hero", name: "Hero", cat: "basic", desc: "Eye-catching hero section", feat: ["Gradient bg", "CTA buttons", "Responsive"] },
571
+ { id: "slider", name: "Slider", cat: "basic", desc: "Image carousel with navigation", feat: ["Auto-play", "Controls", "Touch support"] },
572
+ { id: "table", name: "Table", cat: "basic", desc: "Data table with search", feat: ["Search function", "Sortable", "Responsive"] },
573
+ { id: "login", name: "Login Form", cat: "auth", desc: "Professional login form", feat: ["Email validation", "Remember me", "Social buttons"] },
574
+ { id: "register", name: "Register Form", cat: "auth", desc: "Registration with password strength", feat: ["Password strength", "Real-time feedback", "Username validation"] },
575
+ { id: "skeleton", name: "Skeleton", cat: "loading", desc: "Loading placeholders with shimmer", feat: ["Shimmer effect", "Multiple layouts", "Toggle content"] },
576
+ { id: "spinner", name: "Spinner", cat: "loading", desc: "5 loading spinner variations", feat: ["5 styles", "Smooth animation", "Customizable"] },
577
+ { id: "animated-card", name: "Animated Card", cat: "animation", desc: "Interactive cards with hover animations", feat: ["6 animations", "Hover effects", "Content reveal"] },
578
+ { id: "typing-effect", name: "Typing Effect", cat: "animation", desc: "Text typing animation", feat: ["Smooth typing", "Backspace", "Custom speed"] },
579
+ { id: "fade-gallery", name: "Fade Gallery", cat: "animation", desc: "Image gallery with fade", feat: ["Fade effect", "Navigation", "Thumbnails"] },
580
+ { id: "grid-layout", name: "Grid Layout", cat: "grid", desc: "CSS Grid patterns", feat: ["Multiple layouts", "Auto-fit", "Responsive"] },
581
+ { id: "masonry-grid", name: "Masonry Grid", cat: "grid", desc: "Pinterest-style layout", feat: ["CSS columns", "Responsive", "Image support"] },
582
+ { id: "dashboard-grid", name: "Dashboard Grid", cat: "grid", desc: "Admin dashboard with grid", feat: ["Widget layout", "Responsive", "Sidebar"] },
583
+ { id: "flex-layout", name: "Flex Layout", cat: "flex", desc: "Flexbox patterns", feat: ["Flex containers", "Align items", "Wrap options"] },
584
+ { id: "flex-cards", name: "Flex Cards", cat: "flex", desc: "Equal-height card layouts", feat: ["Equal heights", "Flexible", "Responsive"] },
585
+ { id: "flex-dashboard", name: "Flex Dashboard", cat: "flex", desc: "Admin dashboard with flexbox", feat: ["Flexible layout", "Sidebar toggle", "Responsive"] },
586
+ { id: "todo-list", name: "Todo List", cat: "dom", desc: "Interactive todo list", feat: ["Add/delete items", "Check off", "Local storage"] },
587
+ { id: "counter", name: "Counter", cat: "dom", desc: "Click counter with history", feat: ["Increment/decrement", "Reset", "History"] },
588
+ { id: "accordion", name: "Accordion", cat: "dom", desc: "Collapsible accordion", feat: ["Toggle content", "Smooth animation", "Multiple items"] },
589
+ { id: "tabs", name: "Tabs", cat: "dom", desc: "Tabbed content switcher", feat: ["Tab switching", "Active states", "Keyboard support"] },
590
+ ];
591
+
592
+ const gallery = document.getElementById("gallery");
593
+ const emptyDiv = document.getElementById("empty");
594
+ const searchInput = document.getElementById("search");
595
+ const categoryFilter = document.getElementById("categoryFilter");
596
+
597
+ function render(filtered) {
598
+ gallery.innerHTML = "";
599
+ if (filtered.length === 0) {
600
+ gallery.classList.add("hidden");
601
+ emptyDiv.classList.remove("hidden");
602
+ return;
603
+ }
604
+ gallery.classList.remove("hidden");
605
+ emptyDiv.classList.add("hidden");
606
+
607
+ filtered.forEach((c) => {
608
+ const card = document.createElement("div");
609
+ card.className = "card";
610
+ card.innerHTML = `
611
+ <div class="card-header">
612
+ <span class="card-title">${c.name}</span>
613
+ <span class="card-id">${c.id}</span>
614
+ </div>
615
+ <div class="card-body">
616
+ <span class="category-badge">${c.cat.toUpperCase()}</span>
617
+ <p class="description">${c.desc}</p>
618
+ <ul class="features-list">${c.feat.map((f) => `<li>${f}</li>`).join("")}</ul>
619
+ <div style="display: flex; gap: 10px; margin-top: 12px;">
620
+ <div class="code-block" style="flex: 1;">
621
+ npx create-template create -c ${c.id}
622
+ <button class="copy-btn" onclick="copy(this)">Copy</button>
623
+ </div>
624
+ <div class="code-block" style="flex: 1; opacity: 0.7;">
625
+ npm run create -- -c ${c.id}
626
+ <button class="copy-btn" onclick="copy(this)">Copy</button>
627
+ </div>
628
+ </div>
629
+ </div>
630
+ `;
631
+ gallery.appendChild(card);
632
+ });
633
+ }
634
+
635
+ function copy(btn) {
636
+ const text = btn.parentElement.textContent.trim().split("\n")[0];
637
+ navigator.clipboard.writeText(text);
638
+ btn.textContent = "✓ Copied!";
639
+ setTimeout(() => (btn.textContent = "Copy"), 2000);
640
+ }
641
+
642
+ function filter() {
643
+ const search = searchInput.value.toLowerCase();
644
+ const cat = categoryFilter.value;
645
+ render(
646
+ components.filter(
647
+ (c) =>
648
+ (c.name.toLowerCase().includes(search) || c.desc.toLowerCase().includes(search) || c.feat.some((f) => f.toLowerCase().includes(search))) &&
649
+ (!cat || c.cat === cat),
650
+ ),
651
+ );
652
+ }
653
+
654
+ searchInput.addEventListener("input", filter);
655
+ categoryFilter.addEventListener("change", filter);
656
+ render(components);
657
+ renderColorSchemes();
658
+ </script>
659
+ </body>
660
+ </html>