create-template-html-css 1.4.0 → 1.4.2

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 CHANGED
@@ -5,6 +5,41 @@ 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.4.2] - 2025-01-29
9
+
10
+ ### Enhanced
11
+ - **Card Template Improvements**:
12
+ - 6 professional card variations: Modern (featured), Premium (pricing), Blog (tags), Minimal (clean), User Profile (avatar), Interactive (action buttons)
13
+ - Rich interactions: like/save buttons, social links, tag filtering, toast notifications
14
+ - Professional gradient styling and animations
15
+ - Enhanced badges, metadata displays (ratings, dates, comments, authors)
16
+ - Improved hover effects and button animations with ripple effect
17
+ - Better mobile responsiveness
18
+
19
+ ## [1.4.1] - 2025-01-29
20
+
21
+ ### Improved
22
+ - **Enhanced Insert Feature**:
23
+ - Better indentation handling - respects existing HTML file formatting
24
+ - Improved duplicate detection - prevents inserting same component twice
25
+ - Unique component IDs - all styles/scripts get unique identifiers
26
+ - File validation - checks if HTML file exists before insertion
27
+ - All 19 components now available in insert command (was only 9 basic ones)
28
+
29
+ - **Improved CLI**:
30
+ - Better visual design with emojis and separators
31
+ - Organized component lists with categories
32
+ - File existence validation before insertion
33
+ - Enhanced help text and examples
34
+ - Better status messages with clear summaries
35
+ - Separated files as default for CSS insertion
36
+
37
+ ### Technical Details
38
+ - Simplified indentation detection using HTML file's existing format
39
+ - Added `path` and `fs` imports for better file handling
40
+ - Improved error messages for better debugging
41
+ - Extended insert command with all template categories
42
+
8
43
  ## [1.4.0] - 2025-01-18
9
44
 
10
45
  ### Added
package/INSERT-DEMO.md CHANGED
@@ -2,7 +2,16 @@
2
2
 
3
3
  ## How to Use the Insert Command
4
4
 
5
- The `insert` command allows you to add pre-styled components to your existing HTML pages without creating new projects.
5
+ The `insert` command allows you to add pre-styled components to your existing HTML pages without creating new projects. Components are smartly integrated with proper indentation and ID attributes for easy customization.
6
+
7
+ ## How It Works
8
+
9
+ When inserting a component:
10
+ 1. ✅ Detects existing indentation in your HTML file
11
+ 2. ✅ Respects your code formatting style
12
+ 3. ✅ Adds unique IDs to styles and scripts (e.g., `button-styles`, `button-script`)
13
+ 4. ✅ Prevents duplicate insertions (warns if component already exists)
14
+ 5. ✅ Validates HTML structure (checks for `<head>` and `<body>` tags)
6
15
 
7
16
  ## Example Usage
8
17
 
@@ -132,7 +141,9 @@ npm run insert
132
141
 
133
142
  ## Available Components
134
143
 
135
- All 7 templates are available for insertion:
144
+ All 19 templates are available for insertion:
145
+
146
+ ### Basic Components
136
147
  - Button - Styled button variations
137
148
  - Card - Product/content cards
138
149
  - Form - Contact/input forms
@@ -140,3 +151,21 @@ All 7 templates are available for insertion:
140
151
  - Modal - Dialog popups
141
152
  - Footer - Page footer
142
153
  - Hero - Hero sections with CTA
154
+ - Slider - Image carousel
155
+ - Table - Data tables with features
156
+
157
+ ### Animation Templates
158
+ - Spinner - Loading animations
159
+ - Animated Card - Interactive card effects
160
+ - Typing Effect - Text animations
161
+ - Fade Gallery - Image gallery with effects
162
+
163
+ ### Grid Layouts (CSS Grid)
164
+ - Grid Layout - CSS Grid examples
165
+ - Masonry Grid - Pinterest-style layout
166
+ - Dashboard Grid - Admin dashboard
167
+
168
+ ### Flexbox Layouts
169
+ - Flex Layout - Flexbox patterns
170
+ - Flex Cards - Equal-height cards
171
+ - Flex Dashboard - Admin dashboard with Flexbox
package/bin/cli.js CHANGED
@@ -2,26 +2,40 @@
2
2
 
3
3
  const { program } = require('commander');
4
4
  const inquirer = require('inquirer').default || require('inquirer');
5
+ const path = require('path');
6
+ const fs = require('fs').promises;
5
7
  const { generateTemplate } = require('../src/generator');
6
8
  const { insertComponent } = require('../src/inserter');
7
9
  const chalk = require('chalk');
8
10
 
9
11
  program
10
12
  .name('create-template')
11
- .description('CLI tool to generate HTML and CSS templates')
12
- .version('1.0.0');
13
+ .description(chalk.cyan('🎨 Create HTML/CSS UI component templates in seconds'))
14
+ .version('1.4.0');
15
+
16
+ // Add intro message
17
+ program.on('--help', () => {
18
+ console.log('\n' + chalk.cyan('Examples:'));
19
+ console.log(' $ create-template create # Create a new template');
20
+ console.log(' $ create-template insert # Insert into existing HTML');
21
+ console.log(' $ create-template list # List all templates');
22
+ console.log('');
23
+ });
13
24
 
14
25
  program
15
26
  .command('create')
16
- .description('Create a new HTML/CSS template')
27
+ .description(chalk.green('Create a new HTML/CSS template component'))
17
28
  .action(async () => {
18
29
  try {
30
+ console.log(chalk.cyan('\n✨ Creating a new template component...\n'));
31
+
19
32
  const answers = await inquirer.prompt([
20
33
  {
21
34
  type: 'list',
22
35
  name: 'component',
23
36
  message: 'What component would you like to create?',
24
37
  choices: [
38
+ new inquirer.Separator(chalk.gray('─ Basic Components')),
25
39
  { name: 'Button', value: 'button' },
26
40
  { name: 'Card', value: 'card' },
27
41
  { name: 'Form', value: 'form' },
@@ -31,15 +45,18 @@ program
31
45
  { name: 'Hero Section', value: 'hero' },
32
46
  { name: 'Slider', value: 'slider' },
33
47
  { name: 'Table', value: 'table' },
48
+ new inquirer.Separator(chalk.gray('─ Animation Templates')),
34
49
  { name: 'Spinner (Loading Animations)', value: 'spinner' },
35
50
  { name: 'Animated Card (Interactive Cards)', value: 'animated-card' },
36
51
  { name: 'Typing Effect (Text Animations)', value: 'typing-effect' },
37
52
  { name: 'Fade Gallery (Image Gallery)', value: 'fade-gallery' },
38
- { name: 'Grid Layout (CSS Grid)', value: 'grid-layout' },
53
+ new inquirer.Separator(chalk.gray('─ Grid Layouts (CSS Grid)')),
54
+ { name: 'Grid Layout', value: 'grid-layout' },
39
55
  { name: 'Masonry Grid (Pinterest-style)', value: 'masonry-grid' },
40
56
  { name: 'Dashboard Grid (Admin Panel)', value: 'dashboard-grid' },
57
+ new inquirer.Separator(chalk.gray('─ Flexbox Layouts')),
41
58
  { name: 'Flex Layout (Flexbox Patterns)', value: 'flex-layout' },
42
- { name: 'Flex Cards (Flexbox Cards)', value: 'flex-cards' },
59
+ { name: 'Flex Cards (Equal-height cards)', value: 'flex-cards' },
43
60
  { name: 'Flex Dashboard (Flexbox Admin)', value: 'flex-dashboard' }
44
61
  ]
45
62
  },
@@ -65,37 +82,50 @@ program
65
82
  type: 'confirm',
66
83
  name: 'includeJs',
67
84
  message: 'Include JavaScript file?',
68
- default: false
85
+ default: true
69
86
  }
70
87
  ]);
71
88
 
72
89
  await generateTemplate(answers);
73
- console.log(chalk.green('✓ Template created successfully!'));
90
+
91
+ console.log('\n' + chalk.green('✓ Template created successfully!'));
92
+ console.log(chalk.gray(` Location: ./${answers.name}/`));
93
+ console.log(chalk.gray(` Files: index.html, style.css${answers.includeJs ? ', script.js' : ''}`));
94
+ console.log('');
74
95
  } catch (error) {
75
- console.error(chalk.red('Error creating template:'), error.message);
96
+ console.error(chalk.red('Error:'), error.message);
76
97
  process.exit(1);
77
98
  }
78
99
  });
79
100
 
80
101
  program
81
102
  .command('insert')
82
- .description('Insert a component into an existing HTML page')
103
+ .description(chalk.green('Insert a component into an existing HTML page'))
83
104
  .action(async () => {
84
105
  try {
106
+ console.log(chalk.cyan('\n🚀 Inserting component into HTML file...\n'));
107
+
85
108
  const answers = await inquirer.prompt([
86
109
  {
87
110
  type: 'input',
88
111
  name: 'targetFile',
89
112
  message: 'Enter the path to your HTML file:',
90
113
  default: 'index.html',
91
- validate: (input) => {
114
+ validate: async (input) => {
92
115
  if (!input || input.trim().length === 0) {
93
116
  return 'Please enter a file path';
94
117
  }
95
118
  if (!input.toLowerCase().endsWith('.html')) {
96
119
  return 'File must be an HTML file (.html)';
97
120
  }
98
- return true;
121
+
122
+ // Check if file exists
123
+ try {
124
+ await fs.access(path.resolve(process.cwd(), input));
125
+ return true;
126
+ } catch {
127
+ return `File not found: ${input}`;
128
+ }
99
129
  }
100
130
  },
101
131
  {
@@ -103,6 +133,7 @@ program
103
133
  name: 'component',
104
134
  message: 'Which component would you like to insert?',
105
135
  choices: [
136
+ new inquirer.Separator(chalk.gray('─ Basic Components')),
106
137
  { name: 'Button', value: 'button' },
107
138
  { name: 'Card', value: 'card' },
108
139
  { name: 'Form', value: 'form' },
@@ -111,78 +142,106 @@ program
111
142
  { name: 'Footer', value: 'footer' },
112
143
  { name: 'Hero Section', value: 'hero' },
113
144
  { name: 'Slider', value: 'slider' },
114
- { name: 'Table', value: 'table' }
145
+ { name: 'Table', value: 'table' },
146
+ new inquirer.Separator(chalk.gray('─ Animation Templates')),
147
+ { name: 'Spinner', value: 'spinner' },
148
+ { name: 'Animated Card', value: 'animated-card' },
149
+ { name: 'Typing Effect', value: 'typing-effect' },
150
+ { name: 'Fade Gallery', value: 'fade-gallery' },
151
+ new inquirer.Separator(chalk.gray('─ Grid Layouts')),
152
+ { name: 'Grid Layout', value: 'grid-layout' },
153
+ { name: 'Masonry Grid', value: 'masonry-grid' },
154
+ { name: 'Dashboard Grid', value: 'dashboard-grid' },
155
+ new inquirer.Separator(chalk.gray('─ Flexbox Layouts')),
156
+ { name: 'Flex Layout', value: 'flex-layout' },
157
+ { name: 'Flex Cards', value: 'flex-cards' },
158
+ { name: 'Flex Dashboard', value: 'flex-dashboard' }
115
159
  ]
116
160
  },
117
- {
118
- type: 'list',
119
- name: 'styleMode',
120
- message: 'How should the CSS be added?',
121
- choices: [
122
- { name: 'Inline (inside <style> tag)', value: 'inline' },
123
- { name: 'Separate file', value: 'separate' },
124
- { name: 'Skip (I\'ll add it manually)', value: 'skip' }
125
- ],
126
- default: 'inline'
127
- },
128
161
  {
129
162
  type: 'list',
130
163
  name: 'scriptMode',
131
164
  message: 'How should the JavaScript be added?',
132
165
  choices: [
166
+ { name: 'Separate file (recommended)', value: 'separate' },
133
167
  { name: 'Inline (inside <script> tag)', value: 'inline' },
134
- { name: 'Separate file', value: 'separate' },
135
168
  { name: 'Skip (I\'ll add it manually)', value: 'skip' }
136
169
  ],
137
- default: 'inline'
170
+ default: 'separate'
138
171
  }
139
172
  ]);
140
173
 
174
+ // CSS is always separate (external)
175
+ answers.styleMode = 'separate';
176
+
141
177
  const result = await insertComponent(answers);
142
- console.log(chalk.green('\n✓ Component inserted successfully!'));
143
- console.log(chalk.cyan(` File: ${result.targetFile}`));
144
- console.log(chalk.cyan(` Component: ${result.component}`));
145
- console.log(chalk.cyan(` CSS: ${result.styleMode}`));
146
- console.log(chalk.cyan(` JS: ${result.scriptMode}`));
178
+
179
+ console.log('\n' + chalk.green('✓ Component inserted successfully!'));
180
+ console.log(chalk.cyan(' Summary:'));
181
+ console.log(chalk.gray(` File: ${path.relative(process.cwd(), result.targetFile)}`));
182
+ console.log(chalk.gray(` Component: ${chalk.bold(result.component)}`));
183
+ console.log(chalk.gray(` CSS: ${chalk.yellow('external file')}`));
184
+ console.log(chalk.gray(` JS: ${chalk.yellow(result.scriptMode)}`));
185
+ console.log(chalk.gray(`\n Component IDs: ${result.component}-styles, ${result.component}-script`));
186
+ console.log('');
147
187
  } catch (error) {
148
- console.error(chalk.red('Error inserting component:'), error.message);
188
+ console.error('\n' + chalk.red('Error:'), error.message);
149
189
  process.exit(1);
150
190
  }
151
191
  });
152
192
 
153
193
  program
154
194
  .command('list')
155
- .description('List all available templates')
195
+ .description(chalk.green('List all available templates'))
156
196
  .action(() => {
157
- console.log(chalk.blue('\n📦 Available templates (19):\n'));
158
- console.log(chalk.yellow('Basic Components:'));
159
- console.log(' Button - Styled button component');
160
- console.log(' Card - Card component with image and content');
161
- console.log(' Form - Form with input fields');
162
- console.log(' Navigation - Navigation bar');
163
- console.log(' Modal - Modal dialog');
164
- console.log(' Footer - Footer section');
165
- console.log(' Hero - Hero section with CTA');
166
- console.log(' Slider - Image carousel with navigation');
167
- console.log(' Table - Data table with search and filtering');
168
- console.log(chalk.yellow('\nAnimation Templates:'));
169
- console.log(' • Spinner - 5 loading spinner variations');
170
- console.log(' Animated Card - 6 interactive card animations');
171
- console.log(' Typing Effect - Text typing animations');
172
- console.log(' • Fade Gallery - Image gallery with fade effects');
173
- console.log(chalk.yellow('\nGrid Layouts (CSS Grid):'));
174
- console.log(' Grid Layout - CSS Grid examples');
175
- console.log(' • Masonry Grid - Pinterest-style layout');
176
- console.log(' Dashboard Grid - Complete admin dashboard');
177
- console.log(chalk.yellow('\nFlexbox Layouts:'));
178
- console.log(' • Flex Layout - Flexbox patterns and examples');
179
- console.log(' • Flex Cards - Equal-height card layouts');
180
- console.log(' • Flex Dashboard - Admin dashboard with Flexbox');
197
+ console.log('\n' + chalk.blue('📦 Available Components (19 total)\n'));
198
+
199
+ console.log(chalk.yellow(' Basic Components (9)'));
200
+ console.log(' button Styled button component');
201
+ console.log(' card Card component with image and content');
202
+ console.log(' form Form with input fields and validation');
203
+ console.log(' navigation Responsive navigation bar');
204
+ console.log(' modal Modal dialog component');
205
+ console.log(' footer Footer section');
206
+ console.log(' hero Hero section with CTA button');
207
+ console.log(' slider Image carousel with navigation');
208
+ console.log(' table Data table with search and filtering');
209
+
210
+ console.log('\n' + chalk.magenta('━ Animation Templates (4)'));
211
+ console.log(' spinner 5 loading spinner variations');
212
+ console.log(' animated-card 6 interactive card animations');
213
+ console.log(' typing-effect Text typing animations');
214
+ console.log(' fade-gallery Image gallery with fade effects');
215
+
216
+ console.log('\n' + chalk.cyan('━ Grid Layouts (3)'));
217
+ console.log(' grid-layout CSS Grid patterns and examples');
218
+ console.log(' masonry-grid Pinterest-style masonry layout');
219
+ console.log(' dashboard-grid Complete admin dashboard (Grid)');
220
+
221
+ console.log('\n' + chalk.green('━ Flexbox Layouts (3)'));
222
+ console.log(' flex-layout Flexbox patterns and examples');
223
+ console.log(' flex-cards Equal-height card layouts');
224
+ console.log(' flex-dashboard Complete admin dashboard (Flexbox)');
225
+
226
+ console.log('\n' + chalk.gray('Usage:'));
227
+ console.log(' create-template create Create a new component');
228
+ console.log(' create-template insert Insert into HTML file');
181
229
  console.log('');
182
230
  });
183
231
 
184
232
  program.parse(process.argv);
185
233
 
186
234
  if (!process.argv.slice(2).length) {
187
- program.outputHelp();
235
+ console.log('\n' + chalk.cyan('🎨 Create HTML/CSS UI Templates\n'));
236
+ console.log(chalk.white('Usage: create-template [command]') + '\n');
237
+ console.log(chalk.yellow('Commands:'));
238
+ console.log(' create Create a new template component');
239
+ console.log(' insert Insert component into existing HTML file');
240
+ console.log(' list Show all available templates');
241
+ console.log(' help Display help information\n');
242
+ console.log(chalk.gray('Examples:'));
243
+ console.log(' $ create-template create # Interactive template creation');
244
+ console.log(' $ create-template insert # Interactive component insertion');
245
+ console.log(' $ create-template list # View all 19 templates');
246
+ console.log(' $ create-template --help # Show full help\n');
188
247
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-template-html-css",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "CLI tool to generate HTML and CSS templates for common UI elements",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/inserter.js CHANGED
@@ -4,6 +4,31 @@ const path = require('path');
4
4
  // Security: Validate component name against whitelist
5
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'];
6
6
 
7
+ /**
8
+ * Extracts indentation from a line
9
+ */
10
+ function getIndentation(line) {
11
+ const match = line.match(/^(\s*)/);
12
+ return match ? match[1] : '';
13
+ }
14
+
15
+ /**
16
+ * Checks if a component is already inserted in the HTML
17
+ */
18
+ function isComponentAlreadyInserted(htmlContent, component) {
19
+ const commentPattern = new RegExp(`<!-- ${component.toUpperCase()} Component -->`, 'i');
20
+ return commentPattern.test(htmlContent);
21
+ }
22
+
23
+ /**
24
+ * Gets the indentation level used in an HTML file
25
+ */
26
+ function getHtmlIndentation(htmlContent) {
27
+ // Look for any indented line to determine the standard indentation
28
+ const match = htmlContent.match(/\n(\s+)\S/);
29
+ return match ? match[1] : ' '; // default to 4 spaces
30
+ }
31
+
7
32
  async function insertComponent(options) {
8
33
  const { component, targetFile, styleMode, scriptMode } = options;
9
34
 
@@ -23,12 +48,26 @@ async function insertComponent(options) {
23
48
  // Read target HTML file
24
49
  let htmlContent = await fs.readFile(targetPath, 'utf-8');
25
50
 
51
+ // Check if component is already inserted
52
+ if (isComponentAlreadyInserted(htmlContent, component)) {
53
+ throw new Error(`Component "${component}" is already inserted in this file`);
54
+ }
55
+
56
+ // Validate HTML structure
57
+ if (!htmlContent.includes('</body>')) {
58
+ throw new Error('Target HTML file does not have a closing </body> tag');
59
+ }
60
+
61
+ if (!htmlContent.includes('</head>')) {
62
+ throw new Error('Target HTML file does not have a </head> tag');
63
+ }
64
+
26
65
  // Get component templates
27
66
  const templateDir = path.join(__dirname, '..', 'templates', component);
28
67
  const componentHtml = await fs.readFile(path.join(templateDir, 'index.html'), 'utf-8');
29
68
  const componentCss = await fs.readFile(path.join(templateDir, 'style.css'), 'utf-8');
30
69
 
31
- // Extract component body content (between <body> tags, excluding container if needed)
70
+ // Extract component body content
32
71
  const bodyMatch = componentHtml.match(/<body[^>]*>([\s\S]*)<\/body>/i);
33
72
  if (!bodyMatch) {
34
73
  throw new Error('Invalid component template structure');
@@ -36,31 +75,36 @@ async function insertComponent(options) {
36
75
 
37
76
  let componentBody = bodyMatch[1].trim();
38
77
 
78
+ // Get indentation used in the HTML file
79
+ const baseIndent = getHtmlIndentation(htmlContent);
80
+
81
+ // Normalize component body indentation
82
+ const lines = componentBody.split('\n').map(line => {
83
+ if (line.trim() === '') return '';
84
+ return baseIndent + line.trim();
85
+ }).join('\n');
86
+ componentBody = lines;
87
+
39
88
  // Insert component HTML before closing </body> tag
40
- if (htmlContent.includes('</body>')) {
41
- htmlContent = htmlContent.replace('</body>', `\n <!-- ${component.toUpperCase()} Component -->\n${componentBody}\n</body>`);
42
- } else {
43
- throw new Error('Target HTML file does not have a closing </body> tag');
44
- }
89
+ htmlContent = htmlContent.replace('</body>', `${baseIndent}<!-- ${component.toUpperCase()} Component -->\n${componentBody}\n\n</body>`);
45
90
 
46
91
  // Handle CSS
47
92
  if (styleMode === 'inline') {
48
- // Add CSS in <style> tag before </head>
49
- const styleTag = `\n <style>\n /* ${component.toUpperCase()} Component Styles */\n${componentCss}\n </style>`;
50
- if (htmlContent.includes('</head>')) {
51
- htmlContent = htmlContent.replace('</head>', `${styleTag}\n</head>`);
52
- }
93
+ // Normalize CSS indentation
94
+ const normalizedCss = componentCss.split('\n').map(line => {
95
+ if (line.trim() === '') return '';
96
+ return baseIndent + ' ' + line.trim();
97
+ }).join('\n');
98
+
99
+ htmlContent = htmlContent.replace('</head>', `${baseIndent}<style id="${component}-styles">\n${baseIndent} /* ${component.toUpperCase()} Component Styles */\n${normalizedCss}\n${baseIndent}</style>\n</head>`);
53
100
  } else if (styleMode === 'separate') {
54
101
  // Create separate CSS file
55
102
  const cssFileName = `${component}-component.css`;
56
103
  const cssPath = path.join(path.dirname(targetPath), cssFileName);
57
- await fs.writeFile(cssPath, `/* ${component.toUpperCase()} Component Styles */\n${componentCss}`);
104
+ await fs.writeFile(cssPath, `/* ${component.toUpperCase()} Component Styles */\n\n${componentCss}`);
58
105
 
59
106
  // Add link to CSS file
60
- const linkTag = `\n <link rel="stylesheet" href="${cssFileName}">`;
61
- if (htmlContent.includes('</head>')) {
62
- htmlContent = htmlContent.replace('</head>', `${linkTag}\n</head>`);
63
- }
107
+ htmlContent = htmlContent.replace('</head>', `${baseIndent}<link rel="stylesheet" href="${cssFileName}">\n</head>`);
64
108
  }
65
109
 
66
110
  // Handle JavaScript
@@ -68,18 +112,21 @@ async function insertComponent(options) {
68
112
  const componentJs = await fs.readFile(path.join(templateDir, 'script.js'), 'utf-8');
69
113
 
70
114
  if (scriptMode === 'inline') {
71
- // Add JS in <script> tag before </body>
72
- const scriptTag = `\n <script>\n // ${component.toUpperCase()} Component Script\n${componentJs}\n </script>\n`;
73
- htmlContent = htmlContent.replace('</body>', `${scriptTag}</body>`);
115
+ // Normalize JS indentation
116
+ const normalizedJs = componentJs.split('\n').map(line => {
117
+ if (line.trim() === '') return '';
118
+ return baseIndent + ' ' + line.trim();
119
+ }).join('\n');
120
+
121
+ htmlContent = htmlContent.replace('</body>', `${baseIndent}<script id="${component}-script">\n${baseIndent} // ${component.toUpperCase()} Component Script\n${normalizedJs}\n${baseIndent}</script>\n</body>`);
74
122
  } else if (scriptMode === 'separate') {
75
123
  // Create separate JS file
76
124
  const jsFileName = `${component}-component.js`;
77
125
  const jsPath = path.join(path.dirname(targetPath), jsFileName);
78
- await fs.writeFile(jsPath, `// ${component.toUpperCase()} Component Script\n${componentJs}`);
126
+ await fs.writeFile(jsPath, `// ${component.toUpperCase()} Component Script\n\n${componentJs}`);
79
127
 
80
128
  // Add script tag
81
- const scriptTag = `\n <script src="${jsFileName}"></script>\n`;
82
- htmlContent = htmlContent.replace('</body>', `${scriptTag}</body>`);
129
+ htmlContent = htmlContent.replace('</body>', `${baseIndent}<script src="${jsFileName}" id="${component}-script"></script>\n</body>`);
83
130
  }
84
131
  } catch (error) {
85
132
  // No JavaScript file for this component, skip
@@ -9,44 +9,134 @@
9
9
  <body>
10
10
  <div class="container">
11
11
  <h1>Card Components</h1>
12
+ <p class="subtitle">Explore different card variations and designs</p>
12
13
 
13
14
  <div class="cards-grid">
14
- <!-- Card 1 -->
15
+ <!-- Card 1 - Basic -->
15
16
  <div class="card">
16
- <img src="https://via.placeholder.com/300x200" alt="Card Image" class="card-img">
17
+ <div class="card-header">
18
+ <img src="https://via.placeholder.com/300x200/667eea" alt="Card Image" class="card-img">
19
+ <span class="card-badge">Featured</span>
20
+ </div>
17
21
  <div class="card-body">
18
- <h2 class="card-title">Card Title</h2>
22
+ <div class="card-category">Technology</div>
23
+ <h2 class="card-title">Modern Card Design</h2>
19
24
  <p class="card-text">
20
- This is a card description. Here you can add more information about the content.
25
+ This is a beautifully designed card with rich information and interactive elements.
21
26
  </p>
22
- <button class="card-btn">Read More</button>
27
+ <div class="card-meta">
28
+ <span class="meta-item">⭐ 4.5</span>
29
+ <span class="meta-item">📅 Jan 29, 2026</span>
30
+ </div>
31
+ <button class="card-btn">Learn More</button>
23
32
  </div>
24
33
  </div>
25
34
 
26
- <!-- Card 2 -->
27
- <div class="card">
28
- <img src="https://via.placeholder.com/300x200/667eea" alt="Card Image" class="card-img">
35
+ <!-- Card 2 - With Price -->
36
+ <div class="card card-premium">
37
+ <div class="card-header">
38
+ <img src="https://via.placeholder.com/300x200/764ba2" alt="Premium Card" class="card-img">
39
+ <span class="card-badge premium">Popular</span>
40
+ </div>
29
41
  <div class="card-body">
30
- <h2 class="card-title">Styled Card</h2>
42
+ <div class="card-category">Services</div>
43
+ <h2 class="card-title">Premium Package</h2>
44
+ <div class="card-price">$29.99/mo</div>
31
45
  <p class="card-text">
32
- Clean and modern design with shadows and animations.
46
+ Get exclusive access to premium features and priority support.
33
47
  </p>
34
- <button class="card-btn">Learn More</button>
48
+ <div class="card-features">
49
+ <span class="feature">✓ Advanced Tools</span>
50
+ <span class="feature">✓ Priority Support</span>
51
+ <span class="feature">✓ Analytics</span>
52
+ </div>
53
+ <button class="card-btn btn-premium">Subscribe Now</button>
35
54
  </div>
36
55
  </div>
37
56
 
38
- <!-- Card 3 -->
57
+ <!-- Card 3 - With Tags -->
39
58
  <div class="card">
40
- <img src="https://via.placeholder.com/300x200/764ba2" alt="Card Image" class="card-img">
59
+ <div class="card-header">
60
+ <img src="https://via.placeholder.com/300x200/43e97b" alt="Blog Card" class="card-img">
61
+ <span class="card-badge secondary">New</span>
62
+ </div>
63
+ <div class="card-body">
64
+ <div class="card-category">Blog</div>
65
+ <h2 class="card-title">Latest Updates</h2>
66
+ <p class="card-text">
67
+ Discover the newest features and improvements in our latest release.
68
+ </p>
69
+ <div class="card-tags">
70
+ <span class="tag">CSS</span>
71
+ <span class="tag">Design</span>
72
+ <span class="tag">UI</span>
73
+ </div>
74
+ <div class="card-meta">
75
+ <span class="meta-item">👤 By Admin</span>
76
+ <span class="meta-item">💬 12 Comments</span>
77
+ </div>
78
+ <button class="card-btn">Read Article</button>
79
+ </div>
80
+ </div>
81
+
82
+ <!-- Card 4 - Minimal -->
83
+ <div class="card card-minimal">
84
+ <div class="card-header">
85
+ <img src="https://via.placeholder.com/300x200/fa709a" alt="Minimal Card" class="card-img">
86
+ </div>
87
+ <div class="card-body">
88
+ <h2 class="card-title">Clean & Simple</h2>
89
+ <p class="card-text">
90
+ A minimalist approach to card design focusing on essential information.
91
+ </p>
92
+ <button class="card-btn">Explore</button>
93
+ </div>
94
+ </div>
95
+
96
+ <!-- Card 5 - With Avatar -->
97
+ <div class="card card-user">
98
+ <div class="card-header">
99
+ <img src="https://via.placeholder.com/300x200/feca57" alt="User Card" class="card-img">
100
+ </div>
41
101
  <div class="card-body">
42
- <h2 class="card-title">Third Card</h2>
102
+ <div class="card-avatar">👨‍💼</div>
103
+ <h2 class="card-title">John Developer</h2>
104
+ <p class="card-subtitle">Full Stack Developer</p>
43
105
  <p class="card-text">
44
- More interesting content displayed in an attractive and pleasant way.
106
+ Passionate about creating beautiful and functional web experiences.
45
107
  </p>
46
- <button class="card-btn">Get Started</button>
108
+ <div class="card-social">
109
+ <a href="#" class="social-btn">GitHub</a>
110
+ <a href="#" class="social-btn">Twitter</a>
111
+ <a href="#" class="social-btn">LinkedIn</a>
112
+ </div>
113
+ </div>
114
+ </div>
115
+
116
+ <!-- Card 6 - Interactive -->
117
+ <div class="card card-interactive">
118
+ <div class="card-header">
119
+ <img src="https://via.placeholder.com/300x200/48dbfb" alt="Interactive Card" class="card-img">
120
+ <span class="card-badge interactive">Hot</span>
121
+ </div>
122
+ <div class="card-body">
123
+ <div class="card-category">Trending</div>
124
+ <h2 class="card-title">Interactive Components</h2>
125
+ <p class="card-text">
126
+ Engage your users with interactive and dynamic card elements.
127
+ </p>
128
+ <div class="card-actions">
129
+ <button class="action-btn" data-action="like">❤️ Like</button>
130
+ <button class="action-btn" data-action="share">🔗 Share</button>
131
+ <button class="action-btn" data-action="save">⭐ Save</button>
132
+ </div>
47
133
  </div>
48
134
  </div>
49
135
  </div>
50
136
  </div>
137
+
138
+ <script src="script.js"></script>
139
+ </body>
140
+ </html>
51
141
  </body>
52
142
  </html>
@@ -1,9 +1,122 @@
1
- // Card interactions
2
- document.querySelectorAll('.card-btn').forEach(button => {
3
- button.addEventListener('click', function() {
4
- const card = this.closest('.card');
5
- const title = card.querySelector('.card-title').textContent;
6
- console.log(`Card clicked: ${title}`);
7
- alert(`Clicked on: ${title}`);
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ // Action buttons functionality
3
+ const actionBtns = document.querySelectorAll('.action-btn');
4
+ actionBtns.forEach(btn => {
5
+ btn.addEventListener('click', function(e) {
6
+ e.stopPropagation();
7
+ const action = this.getAttribute('data-action');
8
+ const card = this.closest('.card');
9
+
10
+ if (action === 'like') {
11
+ this.classList.toggle('liked');
12
+ this.textContent = this.classList.contains('liked') ? '❤️ Liked' : '🤍 Like';
13
+ } else if (action === 'share') {
14
+ showNotification('Shared!');
15
+ } else if (action === 'save') {
16
+ this.classList.toggle('saved');
17
+ this.textContent = this.classList.contains('saved') ? '✓ Saved' : '🔖 Save';
18
+ }
19
+ });
8
20
  });
21
+
22
+ // Social buttons functionality
23
+ const socialBtns = document.querySelectorAll('.social-btn');
24
+ socialBtns.forEach(btn => {
25
+ btn.addEventListener('click', function(e) {
26
+ e.preventDefault();
27
+ const platform = this.getAttribute('data-platform');
28
+ showNotification(`Opening ${platform}...`);
29
+ });
30
+ });
31
+
32
+ // Tags click functionality
33
+ const tags = document.querySelectorAll('.tag');
34
+ tags.forEach(tag => {
35
+ tag.style.cursor = 'pointer';
36
+ tag.addEventListener('click', function() {
37
+ showNotification(`Filtered by: ${this.textContent}`);
38
+ });
39
+ });
40
+
41
+ // Card click animations
42
+ const cards = document.querySelectorAll('.card');
43
+ cards.forEach(card => {
44
+ card.addEventListener('mousedown', function() {
45
+ this.style.transition = 'all 0.1s ease';
46
+ });
47
+ card.addEventListener('mouseup', function() {
48
+ this.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
49
+ });
50
+ });
51
+
52
+ // Categories click
53
+ const categories = document.querySelectorAll('.card-category');
54
+ categories.forEach(cat => {
55
+ cat.style.cursor = 'pointer';
56
+ cat.addEventListener('click', function(e) {
57
+ e.stopPropagation();
58
+ showNotification(`Category: ${this.textContent}`);
59
+ });
60
+ });
61
+
62
+ // Notification system
63
+ function showNotification(message) {
64
+ const notification = document.createElement('div');
65
+ notification.style.cssText = `
66
+ position: fixed;
67
+ top: 20px;
68
+ right: 20px;
69
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
70
+ color: white;
71
+ padding: 15px 25px;
72
+ border-radius: 8px;
73
+ font-weight: 600;
74
+ z-index: 9999;
75
+ animation: slideInRight 0.3s ease;
76
+ box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
77
+ `;
78
+ notification.textContent = message;
79
+ document.body.appendChild(notification);
80
+
81
+ setTimeout(() => {
82
+ notification.style.animation = 'slideOutRight 0.3s ease';
83
+ setTimeout(() => notification.remove(), 300);
84
+ }, 2000);
85
+ }
86
+
87
+ // Add animation styles
88
+ if (!document.getElementById('card-animations')) {
89
+ const style = document.createElement('style');
90
+ style.id = 'card-animations';
91
+ style.textContent = `
92
+ @keyframes slideInRight {
93
+ from {
94
+ opacity: 0;
95
+ transform: translateX(100px);
96
+ }
97
+ to {
98
+ opacity: 1;
99
+ transform: translateX(0);
100
+ }
101
+ }
102
+ @keyframes slideOutRight {
103
+ from {
104
+ opacity: 1;
105
+ transform: translateX(0);
106
+ }
107
+ to {
108
+ opacity: 0;
109
+ transform: translateX(100px);
110
+ }
111
+ }
112
+ .action-btn.liked, .action-btn.saved {
113
+ border-color: #667eea;
114
+ background: rgba(102, 126, 234, 0.1);
115
+ color: #667eea;
116
+ }
117
+ `;
118
+ document.head.appendChild(style);
119
+ }
120
+
121
+ console.log('✨ Card interactions loaded successfully!');
9
122
  });
@@ -6,9 +6,9 @@
6
6
 
7
7
  body {
8
8
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
9
- background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
9
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
10
10
  min-height: 100vh;
11
- padding: 40px 20px;
11
+ padding: 60px 20px;
12
12
  }
13
13
 
14
14
  .container {
@@ -18,58 +18,198 @@ body {
18
18
 
19
19
  h1 {
20
20
  text-align: center;
21
- color: #333;
22
- margin-bottom: 40px;
21
+ color: white;
22
+ margin-bottom: 10px;
23
23
  font-size: 2.5rem;
24
+ font-weight: 700;
25
+ }
26
+
27
+ .subtitle {
28
+ text-align: center;
29
+ color: rgba(255, 255, 255, 0.8);
30
+ margin-bottom: 50px;
31
+ font-size: 1.1rem;
24
32
  }
25
33
 
26
34
  .cards-grid {
27
35
  display: grid;
28
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
36
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
29
37
  gap: 30px;
30
38
  }
31
39
 
40
+ /* Card Base */
32
41
  .card {
33
42
  background: white;
34
- border-radius: 15px;
43
+ border-radius: 16px;
35
44
  overflow: hidden;
36
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
37
- transition: all 0.3s ease;
45
+ box-shadow: 0 10px 35px rgba(0, 0, 0, 0.15);
46
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
47
+ position: relative;
38
48
  }
39
49
 
40
50
  .card:hover {
41
- transform: translateY(-10px);
42
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
51
+ transform: translateY(-8px);
52
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
53
+ }
54
+
55
+ .card-header {
56
+ position: relative;
57
+ overflow: hidden;
58
+ height: 200px;
43
59
  }
44
60
 
45
61
  .card-img {
46
62
  width: 100%;
47
- height: 200px;
63
+ height: 100%;
48
64
  object-fit: cover;
49
- transition: transform 0.3s ease;
65
+ transition: transform 0.4s ease;
50
66
  }
51
67
 
52
68
  .card:hover .card-img {
53
- transform: scale(1.1);
69
+ transform: scale(1.08);
70
+ }
71
+
72
+ /* Badges */
73
+ .card-badge {
74
+ position: absolute;
75
+ top: 12px;
76
+ right: 12px;
77
+ background: #667eea;
78
+ color: white;
79
+ padding: 6px 14px;
80
+ border-radius: 20px;
81
+ font-size: 12px;
82
+ font-weight: 600;
83
+ text-transform: uppercase;
84
+ letter-spacing: 0.5px;
85
+ z-index: 10;
86
+ animation: slideIn 0.4s ease;
87
+ }
88
+
89
+ .card-badge.premium {
90
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
91
+ }
92
+
93
+ .card-badge.secondary {
94
+ background: #43e97b;
95
+ }
96
+
97
+ .card-badge.interactive {
98
+ background: #fa709a;
99
+ animation: pulse 2s infinite;
100
+ }
101
+
102
+ @keyframes slideIn {
103
+ from {
104
+ opacity: 0;
105
+ transform: translateX(20px);
106
+ }
107
+ to {
108
+ opacity: 1;
109
+ transform: translateX(0);
110
+ }
111
+ }
112
+
113
+ @keyframes pulse {
114
+ 0%, 100% { opacity: 1; }
115
+ 50% { opacity: 0.7; }
54
116
  }
55
117
 
56
118
  .card-body {
57
119
  padding: 25px;
58
120
  }
59
121
 
122
+ .card-category {
123
+ display: inline-block;
124
+ color: #667eea;
125
+ font-size: 12px;
126
+ font-weight: 600;
127
+ text-transform: uppercase;
128
+ letter-spacing: 0.5px;
129
+ margin-bottom: 10px;
130
+ }
131
+
60
132
  .card-title {
61
133
  color: #333;
62
- font-size: 1.5rem;
134
+ font-size: 1.6rem;
135
+ margin-bottom: 12px;
136
+ font-weight: 600;
137
+ line-height: 1.3;
138
+ }
139
+
140
+ .card-subtitle {
141
+ color: #666;
142
+ font-size: 0.95rem;
63
143
  margin-bottom: 15px;
144
+ font-weight: 500;
64
145
  }
65
146
 
66
147
  .card-text {
67
148
  color: #666;
68
- line-height: 1.6;
149
+ line-height: 1.7;
69
150
  margin-bottom: 20px;
151
+ font-size: 0.95rem;
152
+ }
153
+
154
+ .card-price {
155
+ font-size: 2rem;
156
+ font-weight: 700;
157
+ color: #667eea;
158
+ margin-bottom: 15px;
159
+ }
160
+
161
+ /* Features List */
162
+ .card-features {
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: 8px;
166
+ margin-bottom: 20px;
167
+ }
168
+
169
+ .feature {
170
+ color: #555;
171
+ font-size: 0.9rem;
172
+ display: flex;
173
+ align-items: center;
174
+ gap: 8px;
70
175
  }
71
176
 
177
+ /* Tags */
178
+ .card-tags {
179
+ display: flex;
180
+ flex-wrap: wrap;
181
+ gap: 8px;
182
+ margin-bottom: 15px;
183
+ }
184
+
185
+ .tag {
186
+ display: inline-block;
187
+ background: #f0f0f0;
188
+ color: #667eea;
189
+ padding: 5px 12px;
190
+ border-radius: 16px;
191
+ font-size: 0.85rem;
192
+ font-weight: 500;
193
+ }
194
+
195
+ /* Meta Information */
196
+ .card-meta {
197
+ display: flex;
198
+ gap: 15px;
199
+ margin-bottom: 20px;
200
+ font-size: 0.9rem;
201
+ color: #999;
202
+ }
203
+
204
+ .meta-item {
205
+ display: flex;
206
+ align-items: center;
207
+ gap: 5px;
208
+ }
209
+
210
+ /* Button */
72
211
  .card-btn {
212
+ width: 100%;
73
213
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
74
214
  color: white;
75
215
  border: none;
@@ -80,18 +220,143 @@ h1 {
80
220
  cursor: pointer;
81
221
  transition: all 0.3s ease;
82
222
  text-transform: uppercase;
83
- letter-spacing: 1px;
223
+ letter-spacing: 0.5px;
224
+ position: relative;
225
+ overflow: hidden;
226
+ }
227
+
228
+ .card-btn::before {
229
+ content: '';
230
+ position: absolute;
231
+ top: 50%;
232
+ left: 50%;
233
+ width: 0;
234
+ height: 0;
235
+ border-radius: 50%;
236
+ background: rgba(255, 255, 255, 0.3);
237
+ transform: translate(-50%, -50%);
238
+ transition: width 0.6s, height 0.6s;
239
+ }
240
+
241
+ .card-btn:hover::before {
242
+ width: 300px;
243
+ height: 300px;
84
244
  }
85
245
 
86
246
  .card-btn:hover {
87
247
  transform: translateY(-2px);
88
- box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
248
+ box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
89
249
  }
90
250
 
91
251
  .card-btn:active {
92
252
  transform: translateY(0);
93
253
  }
94
254
 
255
+ .btn-premium {
256
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
257
+ }
258
+
259
+ .btn-premium:hover {
260
+ box-shadow: 0 8px 20px rgba(245, 87, 108, 0.4);
261
+ }
262
+
263
+ /* Card Variations */
264
+ .card-premium {
265
+ border-top: 3px solid #f5576c;
266
+ }
267
+
268
+ .card-minimal .card-body {
269
+ padding: 20px;
270
+ }
271
+
272
+ .card-minimal .card-title {
273
+ font-size: 1.4rem;
274
+ }
275
+
276
+ .card-minimal .card-text {
277
+ margin-bottom: 15px;
278
+ }
279
+
280
+ /* User Card */
281
+ .card-user .card-body {
282
+ text-align: center;
283
+ padding: 30px 25px;
284
+ }
285
+
286
+ .card-avatar {
287
+ font-size: 3rem;
288
+ margin-bottom: 15px;
289
+ }
290
+
291
+ .card-user .card-title {
292
+ margin-bottom: 5px;
293
+ }
294
+
295
+ .card-user .card-subtitle {
296
+ margin-bottom: 15px;
297
+ }
298
+
299
+ .card-social {
300
+ display: flex;
301
+ gap: 10px;
302
+ margin-top: 20px;
303
+ justify-content: center;
304
+ }
305
+
306
+ .social-btn {
307
+ display: inline-block;
308
+ padding: 8px 16px;
309
+ background: #f0f0f0;
310
+ color: #667eea;
311
+ text-decoration: none;
312
+ border-radius: 6px;
313
+ font-size: 0.85rem;
314
+ font-weight: 600;
315
+ transition: all 0.3s ease;
316
+ cursor: pointer;
317
+ }
318
+
319
+ .social-btn:hover {
320
+ background: #667eea;
321
+ color: white;
322
+ transform: translateY(-2px);
323
+ }
324
+
325
+ /* Interactive Card */
326
+ .card-actions {
327
+ display: flex;
328
+ gap: 10px;
329
+ margin-top: 20px;
330
+ }
331
+
332
+ .action-btn {
333
+ flex: 1;
334
+ padding: 10px 12px;
335
+ background: #f5f5f5;
336
+ border: 2px solid #e0e0e0;
337
+ border-radius: 8px;
338
+ cursor: pointer;
339
+ font-size: 0.9rem;
340
+ font-weight: 600;
341
+ transition: all 0.3s ease;
342
+ display: flex;
343
+ align-items: center;
344
+ justify-content: center;
345
+ gap: 6px;
346
+ }
347
+
348
+ .action-btn:hover {
349
+ border-color: #667eea;
350
+ background: #f9f9f9;
351
+ color: #667eea;
352
+ transform: translateY(-2px);
353
+ }
354
+
355
+ .action-btn:active {
356
+ transform: translateY(0);
357
+ }
358
+
359
+ /* Responsive */
95
360
  @media (max-width: 768px) {
96
361
  .cards-grid {
97
362
  grid-template-columns: 1fr;
@@ -100,4 +365,15 @@ h1 {
100
365
  h1 {
101
366
  font-size: 2rem;
102
367
  }
368
+
369
+ .card-title {
370
+ font-size: 1.4rem;
371
+ }
372
+
373
+ .subtitle {
374
+ font-size: 1rem;
375
+ }
376
+ }
377
+ font-size: 2rem;
378
+ }
103
379
  }
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Test Page</title>
6
+ <link rel="stylesheet" href="button-component.css">
7
+ </head>
8
+ <body>
9
+ <h1>Welcome</h1>
10
+ <p>Test content</p>
11
+ <!-- BUTTON Component -->
12
+ <div class="container">
13
+ <h1>Button Component</h1>
14
+
15
+ <!-- Primary Button -->
16
+ <button class="btn btn-primary">Click Here</button>
17
+
18
+ <!-- Secondary Button -->
19
+ <button class="btn btn-secondary">Secondary</button>
20
+
21
+ <!-- Success Button -->
22
+ <button class="btn btn-success">Success</button>
23
+
24
+ <!-- Danger Button -->
25
+ <button class="btn btn-danger">Delete</button>
26
+
27
+ <!-- Outlined Button -->
28
+ <button class="btn btn-outlined">Outlined</button>
29
+
30
+ <!-- Disabled Button -->
31
+ <button class="btn btn-primary" disabled>Disabled</button>
32
+ </div>
33
+
34
+ <script id="button-script">
35
+ // BUTTON Component Script
36
+ // Button click animations
37
+ document.querySelectorAll('.btn').forEach(button => {
38
+ button.addEventListener('click', function(e) {
39
+ if (!this.disabled) {
40
+ console.log('Button clicked:', this.textContent);
41
+
42
+ // Ripple effect
43
+ const ripple = document.createElement('span');
44
+ ripple.classList.add('ripple');
45
+ this.appendChild(ripple);
46
+
47
+ setTimeout(() => ripple.remove(), 600);
48
+ }
49
+ });
50
+ });
51
+
52
+ </script>
53
+ </body>
54
+ </html>