create-template-html-css 1.4.2 → 1.5.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/CHANGELOG.md +23 -0
- package/README.md +20 -13
- package/bin/cli.js +23 -10
- package/package.json +1 -1
- package/src/generator.js +13 -3
- package/src/inserter.js +1 -1
- package/templates/accordion/index.html +67 -0
- package/templates/accordion/script.js +29 -0
- package/templates/accordion/style.css +133 -0
- package/templates/counter/index.html +46 -0
- package/templates/counter/script.js +88 -0
- package/templates/counter/style.css +164 -0
- package/templates/tabs/index.html +83 -0
- package/templates/tabs/script.js +46 -0
- package/templates/tabs/style.css +173 -0
- package/templates/todo-list/index.html +45 -0
- package/templates/todo-list/script.js +69 -0
- package/templates/todo-list/style.css +138 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.5.0] - 2026-01-31
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **4 New DOM Manipulation Templates**:
|
|
12
|
+
- **Todo List**: Interactive task management with add/remove items, checkboxes for completion status, and real-time statistics
|
|
13
|
+
- **Counter**: Increment/decrement counter with adjustable step sizes, color-changing display, and change history
|
|
14
|
+
- **Accordion**: Collapsible FAQ-style sections with smooth animations and toggle functionality
|
|
15
|
+
- **Tabs**: Multi-section content switcher with keyboard navigation (arrow keys) and fade-in animations
|
|
16
|
+
- All new templates include automatic `<script src="script.js"></script>` tag in generated HTML
|
|
17
|
+
- Enhanced component selection in CLI with new DOM Manipulation Examples category
|
|
18
|
+
|
|
19
|
+
### Improved
|
|
20
|
+
- JavaScript file is now always automatically included when creating templates (no confirmation prompt)
|
|
21
|
+
- Better template organization in CLI with category separators
|
|
22
|
+
- Updated component list command to show all 23 available templates
|
|
23
|
+
|
|
24
|
+
## [1.4.3] - 2025-01-29
|
|
25
|
+
|
|
26
|
+
### Documentation
|
|
27
|
+
- Enhanced README with detailed card template variations
|
|
28
|
+
- Added comprehensive card features and interactions documentation
|
|
29
|
+
- Improved visual presentation of template capabilities
|
|
30
|
+
|
|
8
31
|
## [1.4.2] - 2025-01-29
|
|
9
32
|
|
|
10
33
|
### Enhanced
|
package/README.md
CHANGED
|
@@ -26,10 +26,11 @@ A powerful CLI library to create HTML+CSS element templates. Generate styled UI
|
|
|
26
26
|
- 🌈 **Stunning Gradients** - Colorful and attractive designs
|
|
27
27
|
- ⚡ **Animations** - Smooth and professional effects
|
|
28
28
|
- 🔧 **Customizable** - Easy to edit and modify the code
|
|
29
|
-
- 📦 **
|
|
29
|
+
- 📦 **23 Templates** - Button, Card, Form, Navigation, Modal, Footer, Hero, Slider, Table, Spinner, Animated Cards, Typing Effect, Fade Gallery, Grid Layouts, Masonry Grid, Dashboard, Flexbox Layouts, Todo List, Counter, Accordion, Tabs
|
|
30
30
|
- 🎯 **Two Modes** - Create new projects or insert into existing HTML pages
|
|
31
31
|
- 🔒 **Secure** - Input validation and path protection
|
|
32
32
|
- 📚 **Well Documented** - Comprehensive guides and examples
|
|
33
|
+
- 🎪 **DOM Manipulation Examples** - Interactive components demonstrating JavaScript DOM manipulation techniques
|
|
33
34
|
|
|
34
35
|
## 📦 Installation
|
|
35
36
|
|
|
@@ -161,21 +162,27 @@ A collection of styled button components with multiple variations:
|
|
|
161
162
|
- Beautiful gradients
|
|
162
163
|
- Click animations
|
|
163
164
|
|
|
164
|
-
### 2. Card
|
|
165
|
+
### 2. Card ✨
|
|
165
166
|
|
|
166
|
-
Responsive card component
|
|
167
|
+
Responsive card component with 6 professional variations:
|
|
167
168
|
|
|
168
|
-
**
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
169
|
+
**Variations:**
|
|
170
|
+
- **Modern Card** - Featured item with badge, category, rating, and date metadata
|
|
171
|
+
- **Premium Card** - Pricing card with feature list and gradient styling
|
|
172
|
+
- **Blog Card** - Content card with tags, author info, and comment counts
|
|
173
|
+
- **Minimal Card** - Clean, simplified design for minimal content
|
|
174
|
+
- **User Profile Card** - Avatar-based card with social media links
|
|
175
|
+
- **Interactive Card** - Action buttons for like, share, and save functionality
|
|
173
176
|
|
|
174
177
|
**Features:**
|
|
178
|
+
- Rich metadata displays (ratings, dates, comments, authors)
|
|
179
|
+
- Animated action buttons with state management
|
|
180
|
+
- Tag-based filtering system
|
|
181
|
+
- Toast notifications
|
|
182
|
+
- Professional gradients and animations
|
|
175
183
|
- Hover image zoom effect
|
|
176
184
|
- Advanced shadow effects
|
|
177
|
-
-
|
|
178
|
-
- Flexible content
|
|
185
|
+
- Fully responsive grid layout
|
|
179
186
|
|
|
180
187
|
### 3. Form
|
|
181
188
|
|
|
@@ -609,9 +616,9 @@ The only requirement is to include a copy of the license and copyright notice.
|
|
|
609
616
|
|
|
610
617
|
## 👨💻 Author
|
|
611
618
|
|
|
612
|
-
**
|
|
619
|
+
**DavidChen Benshabbat**
|
|
613
620
|
- GitHub: [@benshabbat](https://github.com/benshabbat)
|
|
614
|
-
- Email:
|
|
621
|
+
- Email: benshabbat27@gmail.com
|
|
615
622
|
|
|
616
623
|
## 🔗 Links
|
|
617
624
|
|
|
@@ -635,6 +642,6 @@ If you find this project helpful, please consider:
|
|
|
635
642
|
|
|
636
643
|
---
|
|
637
644
|
|
|
638
|
-
Made with ❤️ by
|
|
645
|
+
Made with ❤️ by DavidChen Benshabbat
|
|
639
646
|
|
|
640
647
|
**Happy coding! 🚀**
|
package/bin/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ const chalk = require('chalk');
|
|
|
11
11
|
program
|
|
12
12
|
.name('create-template')
|
|
13
13
|
.description(chalk.cyan('🎨 Create HTML/CSS UI component templates in seconds'))
|
|
14
|
-
.version('1.
|
|
14
|
+
.version('1.5.0');
|
|
15
15
|
|
|
16
16
|
// Add intro message
|
|
17
17
|
program.on('--help', () => {
|
|
@@ -57,7 +57,12 @@ program
|
|
|
57
57
|
new inquirer.Separator(chalk.gray('─ Flexbox Layouts')),
|
|
58
58
|
{ name: 'Flex Layout (Flexbox Patterns)', value: 'flex-layout' },
|
|
59
59
|
{ name: 'Flex Cards (Equal-height cards)', value: 'flex-cards' },
|
|
60
|
-
{ name: 'Flex Dashboard (Flexbox Admin)', value: 'flex-dashboard' }
|
|
60
|
+
{ name: 'Flex Dashboard (Flexbox Admin)', value: 'flex-dashboard' },
|
|
61
|
+
new inquirer.Separator(chalk.gray('─ DOM Manipulation Examples')),
|
|
62
|
+
{ name: 'Todo List (Add/Remove Items)', value: 'todo-list' },
|
|
63
|
+
{ name: 'Counter (Click Handlers)', value: 'counter' },
|
|
64
|
+
{ name: 'Accordion (Toggle Content)', value: 'accordion' },
|
|
65
|
+
{ name: 'Tabs (Switch Sections)', value: 'tabs' }
|
|
61
66
|
]
|
|
62
67
|
},
|
|
63
68
|
{
|
|
@@ -78,14 +83,11 @@ program
|
|
|
78
83
|
return true;
|
|
79
84
|
}
|
|
80
85
|
},
|
|
81
|
-
{
|
|
82
|
-
type: 'confirm',
|
|
83
|
-
name: 'includeJs',
|
|
84
|
-
message: 'Include JavaScript file?',
|
|
85
|
-
default: true
|
|
86
|
-
}
|
|
87
86
|
]);
|
|
88
87
|
|
|
88
|
+
// Always include JavaScript file
|
|
89
|
+
answers.includeJs = true;
|
|
90
|
+
|
|
89
91
|
await generateTemplate(answers);
|
|
90
92
|
|
|
91
93
|
console.log('\n' + chalk.green('✓ Template created successfully!'));
|
|
@@ -155,7 +157,12 @@ program
|
|
|
155
157
|
new inquirer.Separator(chalk.gray('─ Flexbox Layouts')),
|
|
156
158
|
{ name: 'Flex Layout', value: 'flex-layout' },
|
|
157
159
|
{ name: 'Flex Cards', value: 'flex-cards' },
|
|
158
|
-
{ name: 'Flex Dashboard', value: 'flex-dashboard' }
|
|
160
|
+
{ name: 'Flex Dashboard', value: 'flex-dashboard' },
|
|
161
|
+
new inquirer.Separator(chalk.gray('─ DOM Manipulation')),
|
|
162
|
+
{ name: 'Todo List', value: 'todo-list' },
|
|
163
|
+
{ name: 'Counter', value: 'counter' },
|
|
164
|
+
{ name: 'Accordion', value: 'accordion' },
|
|
165
|
+
{ name: 'Tabs', value: 'tabs' }
|
|
159
166
|
]
|
|
160
167
|
},
|
|
161
168
|
{
|
|
@@ -194,7 +201,7 @@ program
|
|
|
194
201
|
.command('list')
|
|
195
202
|
.description(chalk.green('List all available templates'))
|
|
196
203
|
.action(() => {
|
|
197
|
-
console.log('\n' + chalk.blue('📦 Available Components (
|
|
204
|
+
console.log('\n' + chalk.blue('📦 Available Components (23 total)\n'));
|
|
198
205
|
|
|
199
206
|
console.log(chalk.yellow('━ Basic Components (9)'));
|
|
200
207
|
console.log(' button Styled button component');
|
|
@@ -223,6 +230,12 @@ program
|
|
|
223
230
|
console.log(' flex-cards Equal-height card layouts');
|
|
224
231
|
console.log(' flex-dashboard Complete admin dashboard (Flexbox)');
|
|
225
232
|
|
|
233
|
+
console.log('\n' + chalk.red('━ DOM Manipulation Examples (4)'));
|
|
234
|
+
console.log(' todo-list Interactive todo list with add/remove');
|
|
235
|
+
console.log(' counter Click counter with history tracking');
|
|
236
|
+
console.log(' accordion Collapsible accordion component');
|
|
237
|
+
console.log(' tabs Tabbed content switcher');
|
|
238
|
+
|
|
226
239
|
console.log('\n' + chalk.gray('Usage:'));
|
|
227
240
|
console.log(' create-template create Create a new component');
|
|
228
241
|
console.log(' create-template insert Insert into HTML file');
|
package/package.json
CHANGED
package/src/generator.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs').promises;
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
4
|
// Security: Validate component name against whitelist
|
|
5
|
-
const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero', 'slider', 'table', 'spinner', 'animated-card', 'typing-effect', 'fade-gallery', 'grid-layout', 'masonry-grid', 'dashboard-grid', 'flex-layout', 'flex-cards', 'flex-dashboard'];
|
|
5
|
+
const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero', 'slider', 'table', 'spinner', 'animated-card', 'typing-effect', 'fade-gallery', 'grid-layout', 'masonry-grid', 'dashboard-grid', 'flex-layout', 'flex-cards', 'flex-dashboard', 'todo-list', 'counter', 'accordion', 'tabs'];
|
|
6
6
|
|
|
7
7
|
// Security: Sanitize filename to prevent path traversal
|
|
8
8
|
function sanitizeFilename(filename) {
|
|
@@ -40,8 +40,18 @@ async function generateTemplate(options) {
|
|
|
40
40
|
const templateDir = path.join(__dirname, '..', 'templates', component);
|
|
41
41
|
|
|
42
42
|
// Copy HTML file
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
let htmlContent = await fs.readFile(path.join(templateDir, 'index.html'), 'utf-8');
|
|
44
|
+
|
|
45
|
+
// Replace placeholder name
|
|
46
|
+
htmlContent = htmlContent.replace(/{{name}}/g, safeName);
|
|
47
|
+
|
|
48
|
+
// Add script tag if JavaScript is included
|
|
49
|
+
if (includeJs) {
|
|
50
|
+
// Insert script tag before closing </body> tag
|
|
51
|
+
htmlContent = htmlContent.replace('</body>', ' <script src="script.js"></script>\n</body>');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await fs.writeFile(path.join(outputDir, 'index.html'), htmlContent);
|
|
45
55
|
|
|
46
56
|
// Copy CSS file
|
|
47
57
|
const cssContent = await fs.readFile(path.join(templateDir, 'style.css'), 'utf-8');
|
package/src/inserter.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs').promises;
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
4
|
// Security: Validate component name against whitelist
|
|
5
|
-
const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero', 'slider', 'table', 'spinner', 'animated-card', 'typing-effect', 'fade-gallery', 'grid-layout', 'masonry-grid', 'dashboard-grid', 'flex-layout', 'flex-cards', 'flex-dashboard'];
|
|
5
|
+
const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero', 'slider', 'table', 'spinner', 'animated-card', 'typing-effect', 'fade-gallery', 'grid-layout', 'masonry-grid', 'dashboard-grid', 'flex-layout', 'flex-cards', 'flex-dashboard', 'todo-list', 'counter', 'accordion', 'tabs'];
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Extracts indentation from a line
|
|
@@ -0,0 +1,67 @@
|
|
|
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>{{name}} - Accordion Component</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<div class="accordion-wrapper">
|
|
12
|
+
<h1>Frequently Asked Questions</h1>
|
|
13
|
+
|
|
14
|
+
<div class="accordion">
|
|
15
|
+
<div class="accordion-item">
|
|
16
|
+
<button class="accordion-header">
|
|
17
|
+
<span>What is this accordion component?</span>
|
|
18
|
+
<span class="icon">+</span>
|
|
19
|
+
</button>
|
|
20
|
+
<div class="accordion-content">
|
|
21
|
+
<div class="accordion-body">
|
|
22
|
+
This is a fully functional accordion component that uses DOM manipulation to toggle content visibility. Click on any header to expand or collapse the content.
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="accordion-item">
|
|
28
|
+
<button class="accordion-header">
|
|
29
|
+
<span>How does it work?</span>
|
|
30
|
+
<span class="icon">+</span>
|
|
31
|
+
</button>
|
|
32
|
+
<div class="accordion-content">
|
|
33
|
+
<div class="accordion-body">
|
|
34
|
+
The accordion uses JavaScript to toggle classes on DOM elements. When you click a header, it adds or removes the 'active' class to expand or collapse the content section.
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="accordion-item">
|
|
40
|
+
<button class="accordion-header">
|
|
41
|
+
<span>Can I customize the content?</span>
|
|
42
|
+
<span class="icon">+</span>
|
|
43
|
+
</button>
|
|
44
|
+
<div class="accordion-content">
|
|
45
|
+
<div class="accordion-body">
|
|
46
|
+
Absolutely! You can easily modify the HTML to add your own questions and answers. The styling and functionality will work with any content you add.
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div class="accordion-item">
|
|
52
|
+
<button class="accordion-header">
|
|
53
|
+
<span>Is it mobile responsive?</span>
|
|
54
|
+
<span class="icon">+</span>
|
|
55
|
+
</button>
|
|
56
|
+
<div class="accordion-content">
|
|
57
|
+
<div class="accordion-body">
|
|
58
|
+
Yes! This accordion is fully responsive and works great on all screen sizes - desktop, tablet, and mobile devices.
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
<script src="script.js"></script>
|
|
66
|
+
</body>
|
|
67
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Accordion Component
|
|
2
|
+
|
|
3
|
+
const accordionHeaders = document.querySelectorAll('.accordion-header');
|
|
4
|
+
|
|
5
|
+
// Toggle accordion
|
|
6
|
+
function toggleAccordion(e) {
|
|
7
|
+
const header = e.currentTarget;
|
|
8
|
+
const item = header.parentElement;
|
|
9
|
+
|
|
10
|
+
// Close all other items
|
|
11
|
+
document.querySelectorAll('.accordion-item').forEach(accordionItem => {
|
|
12
|
+
if (accordionItem !== item) {
|
|
13
|
+
accordionItem.classList.remove('active');
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Toggle current item
|
|
18
|
+
item.classList.toggle('active');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Add event listeners
|
|
22
|
+
accordionHeaders.forEach(header => {
|
|
23
|
+
header.addEventListener('click', toggleAccordion);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Optional: Open first item by default
|
|
27
|
+
if (accordionHeaders.length > 0) {
|
|
28
|
+
accordionHeaders[0].parentElement.classList.add('active');
|
|
29
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
9
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
padding: 20px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.container {
|
|
18
|
+
width: 100%;
|
|
19
|
+
max-width: 600px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.accordion-wrapper {
|
|
23
|
+
background: white;
|
|
24
|
+
border-radius: 12px;
|
|
25
|
+
padding: 40px;
|
|
26
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.accordion-wrapper h1 {
|
|
30
|
+
color: #333;
|
|
31
|
+
margin-bottom: 30px;
|
|
32
|
+
text-align: center;
|
|
33
|
+
font-size: 28px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.accordion {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
gap: 12px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.accordion-item {
|
|
43
|
+
border: 2px solid #e0e0e0;
|
|
44
|
+
border-radius: 8px;
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
transition: all 0.3s;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.accordion-item.active {
|
|
50
|
+
border-color: #667eea;
|
|
51
|
+
background: #f8f9ff;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.accordion-header {
|
|
55
|
+
width: 100%;
|
|
56
|
+
padding: 20px;
|
|
57
|
+
background: white;
|
|
58
|
+
border: none;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
display: flex;
|
|
61
|
+
justify-content: space-between;
|
|
62
|
+
align-items: center;
|
|
63
|
+
font-size: 16px;
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
color: #333;
|
|
66
|
+
transition: all 0.3s;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.accordion-item.active .accordion-header {
|
|
70
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
71
|
+
color: white;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.accordion-header:hover {
|
|
75
|
+
background: #f5f5f5;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.accordion-item.active .accordion-header:hover {
|
|
79
|
+
background: linear-gradient(135deg, #5568d3 0%, #6a3f99 100%);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.accordion-header .icon {
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
justify-content: center;
|
|
86
|
+
width: 24px;
|
|
87
|
+
height: 24px;
|
|
88
|
+
font-size: 18px;
|
|
89
|
+
font-weight: bold;
|
|
90
|
+
transition: transform 0.3s;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.accordion-item.active .accordion-header .icon {
|
|
94
|
+
transform: rotate(45deg);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.accordion-content {
|
|
98
|
+
max-height: 0;
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
transition: max-height 0.3s ease;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.accordion-item.active .accordion-content {
|
|
104
|
+
max-height: 500px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.accordion-body {
|
|
108
|
+
padding: 20px;
|
|
109
|
+
color: #666;
|
|
110
|
+
line-height: 1.6;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Responsive */
|
|
114
|
+
@media (max-width: 600px) {
|
|
115
|
+
.accordion-wrapper {
|
|
116
|
+
padding: 20px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.accordion-wrapper h1 {
|
|
120
|
+
font-size: 22px;
|
|
121
|
+
margin-bottom: 20px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.accordion-header {
|
|
125
|
+
padding: 15px;
|
|
126
|
+
font-size: 14px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.accordion-body {
|
|
130
|
+
padding: 15px;
|
|
131
|
+
font-size: 14px;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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>{{name}} - Counter App</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<div class="counter-card">
|
|
12
|
+
<h1>Counter</h1>
|
|
13
|
+
|
|
14
|
+
<div class="counter-display">
|
|
15
|
+
<span id="counterValue">0</span>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="button-group">
|
|
19
|
+
<button id="decrementBtn" class="btn btn-danger">−</button>
|
|
20
|
+
<button id="resetBtn" class="btn btn-secondary">Reset</button>
|
|
21
|
+
<button id="incrementBtn" class="btn btn-success">+</button>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="stats">
|
|
25
|
+
<div class="stat-item">
|
|
26
|
+
<label>Step Size:</label>
|
|
27
|
+
<select id="stepSize" class="step-select">
|
|
28
|
+
<option value="1">1</option>
|
|
29
|
+
<option value="5">5</option>
|
|
30
|
+
<option value="10">10</option>
|
|
31
|
+
<option value="100">100</option>
|
|
32
|
+
</select>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="history">
|
|
37
|
+
<h3>History</h3>
|
|
38
|
+
<div id="historyList" class="history-list">
|
|
39
|
+
<p class="empty">No changes yet</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<script src="script.js"></script>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Counter Application with History
|
|
2
|
+
|
|
3
|
+
let counter = 0;
|
|
4
|
+
const counterValue = document.getElementById('counterValue');
|
|
5
|
+
const incrementBtn = document.getElementById('incrementBtn');
|
|
6
|
+
const decrementBtn = document.getElementById('decrementBtn');
|
|
7
|
+
const resetBtn = document.getElementById('resetBtn');
|
|
8
|
+
const stepSelect = document.getElementById('stepSize');
|
|
9
|
+
const historyList = document.getElementById('historyList');
|
|
10
|
+
|
|
11
|
+
// Get step size
|
|
12
|
+
function getStepSize() {
|
|
13
|
+
return parseInt(stepSelect.value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Update display
|
|
17
|
+
function updateDisplay() {
|
|
18
|
+
counterValue.textContent = counter;
|
|
19
|
+
|
|
20
|
+
// Change color based on value
|
|
21
|
+
const display = counterValue.parentElement;
|
|
22
|
+
if (counter > 0) {
|
|
23
|
+
display.style.background = 'linear-gradient(135deg, #10b981 0%, #059669 100%)';
|
|
24
|
+
} else if (counter < 0) {
|
|
25
|
+
display.style.background = 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)';
|
|
26
|
+
} else {
|
|
27
|
+
display.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Add to history
|
|
32
|
+
function addToHistory(action, value) {
|
|
33
|
+
const now = new Date().toLocaleTimeString();
|
|
34
|
+
const item = document.createElement('div');
|
|
35
|
+
item.className = 'history-item';
|
|
36
|
+
item.textContent = `${now} - ${action} (${value > 0 ? '+' : ''}${value})`;
|
|
37
|
+
|
|
38
|
+
// Remove empty message if exists
|
|
39
|
+
if (historyList.querySelector('.empty')) {
|
|
40
|
+
historyList.innerHTML = '';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
historyList.insertBefore(item, historyList.firstChild);
|
|
44
|
+
|
|
45
|
+
// Keep only last 10 items
|
|
46
|
+
while (historyList.children.length > 10) {
|
|
47
|
+
historyList.removeChild(historyList.lastChild);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Increment
|
|
52
|
+
function increment() {
|
|
53
|
+
const step = getStepSize();
|
|
54
|
+
counter += step;
|
|
55
|
+
updateDisplay();
|
|
56
|
+
addToHistory('Increment', step);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Decrement
|
|
60
|
+
function decrement() {
|
|
61
|
+
const step = getStepSize();
|
|
62
|
+
counter -= step;
|
|
63
|
+
updateDisplay();
|
|
64
|
+
addToHistory('Decrement', -step);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Reset
|
|
68
|
+
function reset() {
|
|
69
|
+
const oldValue = counter;
|
|
70
|
+
counter = 0;
|
|
71
|
+
updateDisplay();
|
|
72
|
+
addToHistory('Reset', -oldValue);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Event listeners
|
|
76
|
+
incrementBtn.addEventListener('click', increment);
|
|
77
|
+
decrementBtn.addEventListener('click', decrement);
|
|
78
|
+
resetBtn.addEventListener('click', reset);
|
|
79
|
+
|
|
80
|
+
// Keyboard support
|
|
81
|
+
document.addEventListener('keydown', (e) => {
|
|
82
|
+
if (e.key === 'ArrowUp' || e.key === '+') increment();
|
|
83
|
+
if (e.key === 'ArrowDown' || e.key === '-') decrement();
|
|
84
|
+
if (e.key === '0' || e.key === 'r') reset();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Initialize
|
|
88
|
+
updateDisplay();
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
9
|
+
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
padding: 20px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.container {
|
|
18
|
+
width: 100%;
|
|
19
|
+
max-width: 400px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.counter-card {
|
|
23
|
+
background: white;
|
|
24
|
+
border-radius: 16px;
|
|
25
|
+
padding: 40px 30px;
|
|
26
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.counter-card h1 {
|
|
30
|
+
text-align: center;
|
|
31
|
+
color: #333;
|
|
32
|
+
margin-bottom: 30px;
|
|
33
|
+
font-size: 28px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.counter-display {
|
|
37
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
38
|
+
color: white;
|
|
39
|
+
border-radius: 12px;
|
|
40
|
+
padding: 40px;
|
|
41
|
+
text-align: center;
|
|
42
|
+
margin-bottom: 30px;
|
|
43
|
+
font-size: 64px;
|
|
44
|
+
font-weight: bold;
|
|
45
|
+
min-height: 140px;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.button-group {
|
|
53
|
+
display: flex;
|
|
54
|
+
gap: 12px;
|
|
55
|
+
margin-bottom: 25px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.btn {
|
|
59
|
+
flex: 1;
|
|
60
|
+
padding: 15px;
|
|
61
|
+
border: none;
|
|
62
|
+
border-radius: 8px;
|
|
63
|
+
font-size: 18px;
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
transition: all 0.3s;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.btn-success {
|
|
70
|
+
background: #10b981;
|
|
71
|
+
color: white;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.btn-success:hover {
|
|
75
|
+
background: #059669;
|
|
76
|
+
transform: translateY(-2px);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.btn-danger {
|
|
80
|
+
background: #ef4444;
|
|
81
|
+
color: white;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.btn-danger:hover {
|
|
85
|
+
background: #dc2626;
|
|
86
|
+
transform: translateY(-2px);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.btn-secondary {
|
|
90
|
+
background: #6b7280;
|
|
91
|
+
color: white;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.btn-secondary:hover {
|
|
95
|
+
background: #4b5563;
|
|
96
|
+
transform: translateY(-2px);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.stats {
|
|
100
|
+
background: #f3f4f6;
|
|
101
|
+
border-radius: 8px;
|
|
102
|
+
padding: 15px;
|
|
103
|
+
margin-bottom: 20px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.stat-item {
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: space-between;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.stat-item label {
|
|
113
|
+
color: #666;
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.step-select {
|
|
118
|
+
padding: 8px 12px;
|
|
119
|
+
border: 2px solid #e5e7eb;
|
|
120
|
+
border-radius: 6px;
|
|
121
|
+
font-size: 14px;
|
|
122
|
+
cursor: pointer;
|
|
123
|
+
background: white;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.step-select:focus {
|
|
127
|
+
outline: none;
|
|
128
|
+
border-color: #667eea;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.history {
|
|
132
|
+
border-top: 2px solid #e5e7eb;
|
|
133
|
+
padding-top: 20px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.history h3 {
|
|
137
|
+
color: #333;
|
|
138
|
+
margin-bottom: 12px;
|
|
139
|
+
font-size: 14px;
|
|
140
|
+
text-transform: uppercase;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.history-list {
|
|
144
|
+
max-height: 120px;
|
|
145
|
+
overflow-y: auto;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.history-item {
|
|
149
|
+
padding: 8px;
|
|
150
|
+
background: #f9fafb;
|
|
151
|
+
border-radius: 6px;
|
|
152
|
+
font-size: 13px;
|
|
153
|
+
color: #666;
|
|
154
|
+
margin-bottom: 8px;
|
|
155
|
+
border-left: 3px solid #667eea;
|
|
156
|
+
padding-left: 12px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.empty {
|
|
160
|
+
text-align: center;
|
|
161
|
+
color: #999;
|
|
162
|
+
font-size: 13px;
|
|
163
|
+
padding: 15px;
|
|
164
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
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>{{name}} - Tabs Component</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<div class="tabs-wrapper">
|
|
12
|
+
<h1>Tabbed Content</h1>
|
|
13
|
+
|
|
14
|
+
<div class="tabs">
|
|
15
|
+
<div class="tab-buttons">
|
|
16
|
+
<button class="tab-button active" data-tab="tab1">
|
|
17
|
+
<span class="icon">📋</span> Overview
|
|
18
|
+
</button>
|
|
19
|
+
<button class="tab-button" data-tab="tab2">
|
|
20
|
+
<span class="icon">⚙️</span> Features
|
|
21
|
+
</button>
|
|
22
|
+
<button class="tab-button" data-tab="tab3">
|
|
23
|
+
<span class="icon">💬</span> Reviews
|
|
24
|
+
</button>
|
|
25
|
+
<button class="tab-button" data-tab="tab4">
|
|
26
|
+
<span class="icon">ℹ️</span> About
|
|
27
|
+
</button>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="tab-contents">
|
|
31
|
+
<div class="tab-content active" id="tab1">
|
|
32
|
+
<h2>Overview</h2>
|
|
33
|
+
<p>This is a fully functional tabs component that uses DOM manipulation to switch between different content sections. Click on the tab buttons above to switch content.</p>
|
|
34
|
+
<ul>
|
|
35
|
+
<li>Easy to implement</li>
|
|
36
|
+
<li>Responsive design</li>
|
|
37
|
+
<li>Smooth transitions</li>
|
|
38
|
+
<li>Clean and modern styling</li>
|
|
39
|
+
</ul>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="tab-content" id="tab2">
|
|
43
|
+
<h2>Features</h2>
|
|
44
|
+
<p>Our tabs component comes with many great features:</p>
|
|
45
|
+
<ul>
|
|
46
|
+
<li>🎨 Customizable colors and styles</li>
|
|
47
|
+
<li>📱 Mobile responsive layout</li>
|
|
48
|
+
<li>⌨️ Keyboard navigation support</li>
|
|
49
|
+
<li>♿ Accessible to screen readers</li>
|
|
50
|
+
<li>⚡ Fast and smooth animations</li>
|
|
51
|
+
<li>🔧 Easy to customize and extend</li>
|
|
52
|
+
</ul>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="tab-content" id="tab3">
|
|
56
|
+
<h2>Reviews</h2>
|
|
57
|
+
<div class="review">
|
|
58
|
+
<p><strong>⭐⭐⭐⭐⭐</strong> Perfect component!</p>
|
|
59
|
+
<p>"This tabs component is exactly what I needed for my project. Easy to use and looks great!"</p>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="review">
|
|
62
|
+
<p><strong>⭐⭐⭐⭐⭐</strong> Highly recommended</p>
|
|
63
|
+
<p>"Great functionality and clean code. Highly recommend to anyone building web applications."</p>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="tab-content" id="tab4">
|
|
68
|
+
<h2>About</h2>
|
|
69
|
+
<p>This tabs component was created to demonstrate DOM manipulation techniques in JavaScript. It shows how to:</p>
|
|
70
|
+
<ul>
|
|
71
|
+
<li>Select DOM elements</li>
|
|
72
|
+
<li>Add/remove CSS classes</li>
|
|
73
|
+
<li>Handle events dynamically</li>
|
|
74
|
+
<li>Create interactive user interfaces</li>
|
|
75
|
+
</ul>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<script src="script.js"></script>
|
|
82
|
+
</body>
|
|
83
|
+
</html>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Tabs Component
|
|
2
|
+
|
|
3
|
+
const tabButtons = document.querySelectorAll('.tab-button');
|
|
4
|
+
const tabContents = document.querySelectorAll('.tab-content');
|
|
5
|
+
|
|
6
|
+
// Switch tab
|
|
7
|
+
function switchTab(e) {
|
|
8
|
+
const button = e.currentTarget;
|
|
9
|
+
const tabId = button.getAttribute('data-tab');
|
|
10
|
+
|
|
11
|
+
// Remove active class from all buttons
|
|
12
|
+
tabButtons.forEach(btn => btn.classList.remove('active'));
|
|
13
|
+
|
|
14
|
+
// Add active class to clicked button
|
|
15
|
+
button.classList.add('active');
|
|
16
|
+
|
|
17
|
+
// Hide all content
|
|
18
|
+
tabContents.forEach(content => content.classList.remove('active'));
|
|
19
|
+
|
|
20
|
+
// Show selected content
|
|
21
|
+
const selectedContent = document.getElementById(tabId);
|
|
22
|
+
if (selectedContent) {
|
|
23
|
+
selectedContent.classList.add('active');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Add event listeners
|
|
28
|
+
tabButtons.forEach(button => {
|
|
29
|
+
button.addEventListener('click', switchTab);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Keyboard navigation
|
|
33
|
+
document.addEventListener('keydown', (e) => {
|
|
34
|
+
const activeButton = document.querySelector('.tab-button.active');
|
|
35
|
+
const activeIndex = Array.from(tabButtons).indexOf(activeButton);
|
|
36
|
+
|
|
37
|
+
if (e.key === 'ArrowRight') {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
const nextIndex = (activeIndex + 1) % tabButtons.length;
|
|
40
|
+
tabButtons[nextIndex].click();
|
|
41
|
+
} else if (e.key === 'ArrowLeft') {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
const prevIndex = (activeIndex - 1 + tabButtons.length) % tabButtons.length;
|
|
44
|
+
tabButtons[prevIndex].click();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
9
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
padding: 20px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.container {
|
|
18
|
+
width: 100%;
|
|
19
|
+
max-width: 700px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.tabs-wrapper {
|
|
23
|
+
background: white;
|
|
24
|
+
border-radius: 12px;
|
|
25
|
+
padding: 40px;
|
|
26
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.tabs-wrapper h1 {
|
|
30
|
+
color: #333;
|
|
31
|
+
margin-bottom: 30px;
|
|
32
|
+
text-align: center;
|
|
33
|
+
font-size: 28px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.tab-buttons {
|
|
37
|
+
display: flex;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
border-bottom: 2px solid #e0e0e0;
|
|
40
|
+
margin-bottom: 30px;
|
|
41
|
+
flex-wrap: wrap;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.tab-button {
|
|
45
|
+
padding: 15px 20px;
|
|
46
|
+
background: none;
|
|
47
|
+
border: none;
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
color: #666;
|
|
50
|
+
font-size: 14px;
|
|
51
|
+
font-weight: 600;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
position: relative;
|
|
54
|
+
transition: all 0.3s;
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 8px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.tab-button:hover {
|
|
61
|
+
color: #333;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.tab-button.active {
|
|
65
|
+
color: #667eea;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.tab-button.active::after {
|
|
69
|
+
content: '';
|
|
70
|
+
position: absolute;
|
|
71
|
+
bottom: -2px;
|
|
72
|
+
left: 0;
|
|
73
|
+
right: 0;
|
|
74
|
+
height: 2px;
|
|
75
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.tab-button .icon {
|
|
79
|
+
font-size: 18px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.tab-contents {
|
|
83
|
+
position: relative;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.tab-content {
|
|
87
|
+
display: none;
|
|
88
|
+
animation: fadeIn 0.3s ease-in;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.tab-content.active {
|
|
92
|
+
display: block;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@keyframes fadeIn {
|
|
96
|
+
from {
|
|
97
|
+
opacity: 0;
|
|
98
|
+
transform: translateY(10px);
|
|
99
|
+
}
|
|
100
|
+
to {
|
|
101
|
+
opacity: 1;
|
|
102
|
+
transform: translateY(0);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.tab-content h2 {
|
|
107
|
+
color: #333;
|
|
108
|
+
margin-bottom: 15px;
|
|
109
|
+
font-size: 24px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.tab-content p {
|
|
113
|
+
color: #666;
|
|
114
|
+
line-height: 1.6;
|
|
115
|
+
margin-bottom: 15px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.tab-content ul {
|
|
119
|
+
list-style-position: inside;
|
|
120
|
+
color: #666;
|
|
121
|
+
line-height: 1.8;
|
|
122
|
+
margin-bottom: 15px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.tab-content ul li {
|
|
126
|
+
margin-bottom: 8px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.review {
|
|
130
|
+
background: #f8f9fa;
|
|
131
|
+
padding: 15px;
|
|
132
|
+
border-radius: 8px;
|
|
133
|
+
margin-bottom: 15px;
|
|
134
|
+
border-left: 4px solid #667eea;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.review p {
|
|
138
|
+
margin-bottom: 8px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.review p:first-child {
|
|
142
|
+
font-weight: 600;
|
|
143
|
+
color: #333;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Responsive */
|
|
147
|
+
@media (max-width: 600px) {
|
|
148
|
+
.tabs-wrapper {
|
|
149
|
+
padding: 20px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.tab-buttons {
|
|
153
|
+
gap: 4px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.tab-button {
|
|
157
|
+
padding: 12px 14px;
|
|
158
|
+
font-size: 12px;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.tab-button .icon {
|
|
162
|
+
font-size: 16px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.tabs-wrapper h1 {
|
|
166
|
+
font-size: 22px;
|
|
167
|
+
margin-bottom: 20px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.tab-content h2 {
|
|
171
|
+
font-size: 18px;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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>{{name}} - Todo List</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<div class="todo-app">
|
|
12
|
+
<h1>My Tasks</h1>
|
|
13
|
+
|
|
14
|
+
<div class="input-container">
|
|
15
|
+
<input
|
|
16
|
+
type="text"
|
|
17
|
+
id="todoInput"
|
|
18
|
+
class="todo-input"
|
|
19
|
+
placeholder="Add a new task..."
|
|
20
|
+
>
|
|
21
|
+
<button id="addBtn" class="add-btn">Add Task</button>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<ul id="todoList" class="todo-list">
|
|
25
|
+
<li class="todo-item">
|
|
26
|
+
<input type="checkbox" class="todo-checkbox">
|
|
27
|
+
<span class="todo-text">Sample task 1</span>
|
|
28
|
+
<button class="delete-btn">✕</button>
|
|
29
|
+
</li>
|
|
30
|
+
<li class="todo-item">
|
|
31
|
+
<input type="checkbox" class="todo-checkbox">
|
|
32
|
+
<span class="todo-text">Sample task 2</span>
|
|
33
|
+
<button class="delete-btn">✕</button>
|
|
34
|
+
</li>
|
|
35
|
+
</ul>
|
|
36
|
+
|
|
37
|
+
<div class="stats">
|
|
38
|
+
<span>Total: <strong id="totalCount">2</strong></span>
|
|
39
|
+
<span>Completed: <strong id="completedCount">0</strong></span>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<script src="script.js"></script>
|
|
44
|
+
</body>
|
|
45
|
+
</html>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Todo List Application
|
|
2
|
+
|
|
3
|
+
const todoInput = document.getElementById('todoInput');
|
|
4
|
+
const addBtn = document.getElementById('addBtn');
|
|
5
|
+
const todoList = document.getElementById('todoList');
|
|
6
|
+
const totalCount = document.getElementById('totalCount');
|
|
7
|
+
const completedCount = document.getElementById('completedCount');
|
|
8
|
+
|
|
9
|
+
// Add new task
|
|
10
|
+
function addTodo() {
|
|
11
|
+
const text = todoInput.value.trim();
|
|
12
|
+
|
|
13
|
+
if (text === '') {
|
|
14
|
+
alert('Please enter a task!');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Create new todo item
|
|
19
|
+
const li = document.createElement('li');
|
|
20
|
+
li.className = 'todo-item';
|
|
21
|
+
li.innerHTML = `
|
|
22
|
+
<input type="checkbox" class="todo-checkbox">
|
|
23
|
+
<span class="todo-text">${text}</span>
|
|
24
|
+
<button class="delete-btn">✕</button>
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
todoList.appendChild(li);
|
|
28
|
+
todoInput.value = '';
|
|
29
|
+
todoInput.focus();
|
|
30
|
+
|
|
31
|
+
updateStats();
|
|
32
|
+
attachEventListeners();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Update statistics
|
|
36
|
+
function updateStats() {
|
|
37
|
+
const items = document.querySelectorAll('.todo-item');
|
|
38
|
+
const completed = document.querySelectorAll('.todo-item.completed');
|
|
39
|
+
|
|
40
|
+
totalCount.textContent = items.length;
|
|
41
|
+
completedCount.textContent = completed.length;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Attach event listeners to new items
|
|
45
|
+
function attachEventListeners() {
|
|
46
|
+
document.querySelectorAll('.todo-checkbox').forEach(checkbox => {
|
|
47
|
+
checkbox.onclick = (e) => {
|
|
48
|
+
e.target.closest('.todo-item').classList.toggle('completed');
|
|
49
|
+
updateStats();
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
document.querySelectorAll('.delete-btn').forEach(btn => {
|
|
54
|
+
btn.onclick = (e) => {
|
|
55
|
+
e.target.closest('.todo-item').remove();
|
|
56
|
+
updateStats();
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Event listeners
|
|
62
|
+
addBtn.addEventListener('click', addTodo);
|
|
63
|
+
todoInput.addEventListener('keypress', (e) => {
|
|
64
|
+
if (e.key === 'Enter') addTodo();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Initialize
|
|
68
|
+
attachEventListeners();
|
|
69
|
+
updateStats();
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
9
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
padding: 20px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.container {
|
|
18
|
+
width: 100%;
|
|
19
|
+
max-width: 500px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.todo-app {
|
|
23
|
+
background: white;
|
|
24
|
+
border-radius: 12px;
|
|
25
|
+
padding: 30px;
|
|
26
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.todo-app h1 {
|
|
30
|
+
color: #333;
|
|
31
|
+
margin-bottom: 25px;
|
|
32
|
+
text-align: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.input-container {
|
|
36
|
+
display: flex;
|
|
37
|
+
gap: 10px;
|
|
38
|
+
margin-bottom: 25px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.todo-input {
|
|
42
|
+
flex: 1;
|
|
43
|
+
padding: 12px;
|
|
44
|
+
border: 2px solid #e0e0e0;
|
|
45
|
+
border-radius: 8px;
|
|
46
|
+
font-size: 16px;
|
|
47
|
+
transition: border-color 0.3s;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.todo-input:focus {
|
|
51
|
+
outline: none;
|
|
52
|
+
border-color: #667eea;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.add-btn {
|
|
56
|
+
padding: 12px 25px;
|
|
57
|
+
background: #667eea;
|
|
58
|
+
color: white;
|
|
59
|
+
border: none;
|
|
60
|
+
border-radius: 8px;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
transition: background 0.3s;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.add-btn:hover {
|
|
67
|
+
background: #5568d3;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.todo-list {
|
|
71
|
+
list-style: none;
|
|
72
|
+
margin-bottom: 20px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.todo-item {
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
padding: 15px;
|
|
79
|
+
background: #f8f9fa;
|
|
80
|
+
border-radius: 8px;
|
|
81
|
+
margin-bottom: 10px;
|
|
82
|
+
transition: all 0.3s;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.todo-item:hover {
|
|
86
|
+
background: #e9ecef;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.todo-item.completed .todo-text {
|
|
90
|
+
text-decoration: line-through;
|
|
91
|
+
color: #999;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.todo-checkbox {
|
|
95
|
+
width: 20px;
|
|
96
|
+
height: 20px;
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
margin-right: 15px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.todo-text {
|
|
102
|
+
flex: 1;
|
|
103
|
+
color: #333;
|
|
104
|
+
font-size: 16px;
|
|
105
|
+
word-break: break-word;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.delete-btn {
|
|
109
|
+
background: #ff6b6b;
|
|
110
|
+
color: white;
|
|
111
|
+
border: none;
|
|
112
|
+
padding: 8px 12px;
|
|
113
|
+
border-radius: 6px;
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
font-size: 16px;
|
|
116
|
+
transition: background 0.3s;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.delete-btn:hover {
|
|
120
|
+
background: #ff5252;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.stats {
|
|
124
|
+
display: flex;
|
|
125
|
+
justify-content: space-around;
|
|
126
|
+
padding-top: 20px;
|
|
127
|
+
border-top: 2px solid #e0e0e0;
|
|
128
|
+
color: #666;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.stats span {
|
|
132
|
+
font-size: 14px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.stats strong {
|
|
136
|
+
color: #667eea;
|
|
137
|
+
font-size: 18px;
|
|
138
|
+
}
|