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 +35 -0
- package/INSERT-DEMO.md +31 -2
- package/bin/cli.js +116 -57
- package/package.json +1 -1
- package/src/inserter.js +69 -22
- package/templates/card/index.html +106 -16
- package/templates/card/script.js +120 -7
- package/templates/card/style.css +293 -17
- package/test-insert.html +54 -0
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
|
|
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('
|
|
12
|
-
.version('1.
|
|
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
|
-
|
|
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 (
|
|
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:
|
|
85
|
+
default: true
|
|
69
86
|
}
|
|
70
87
|
]);
|
|
71
88
|
|
|
72
89
|
await generateTemplate(answers);
|
|
73
|
-
|
|
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
|
|
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
|
-
|
|
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: '
|
|
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
|
-
|
|
143
|
-
console.log(chalk.
|
|
144
|
-
console.log(chalk.cyan(
|
|
145
|
-
console.log(chalk.
|
|
146
|
-
console.log(chalk.
|
|
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
|
|
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('
|
|
158
|
-
|
|
159
|
-
console.log('
|
|
160
|
-
console.log('
|
|
161
|
-
console.log('
|
|
162
|
-
console.log('
|
|
163
|
-
console.log('
|
|
164
|
-
console.log('
|
|
165
|
-
console.log('
|
|
166
|
-
console.log('
|
|
167
|
-
console.log('
|
|
168
|
-
console.log(
|
|
169
|
-
|
|
170
|
-
console.log('
|
|
171
|
-
console.log('
|
|
172
|
-
console.log('
|
|
173
|
-
console.log(
|
|
174
|
-
console.log('
|
|
175
|
-
|
|
176
|
-
console.log('
|
|
177
|
-
console.log(
|
|
178
|
-
console.log('
|
|
179
|
-
console.log('
|
|
180
|
-
|
|
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
|
-
|
|
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
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
|
|
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
|
-
|
|
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
|
-
//
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
72
|
-
const
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
|
25
|
+
This is a beautifully designed card with rich information and interactive elements.
|
|
21
26
|
</p>
|
|
22
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
|
|
46
|
+
Get exclusive access to premium features and priority support.
|
|
33
47
|
</p>
|
|
34
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
|
|
106
|
+
Passionate about creating beautiful and functional web experiences.
|
|
45
107
|
</p>
|
|
46
|
-
<
|
|
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>
|
package/templates/card/script.js
CHANGED
|
@@ -1,9 +1,122 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
});
|
package/templates/card/style.css
CHANGED
|
@@ -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, #
|
|
9
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
10
10
|
min-height: 100vh;
|
|
11
|
-
padding:
|
|
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:
|
|
22
|
-
margin-bottom:
|
|
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(
|
|
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:
|
|
43
|
+
border-radius: 16px;
|
|
35
44
|
overflow: hidden;
|
|
36
|
-
box-shadow: 0 10px
|
|
37
|
-
transition: all 0.3s
|
|
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(-
|
|
42
|
-
box-shadow: 0 20px
|
|
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:
|
|
63
|
+
height: 100%;
|
|
48
64
|
object-fit: cover;
|
|
49
|
-
transition: transform 0.
|
|
65
|
+
transition: transform 0.4s ease;
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
.card:hover .card-img {
|
|
53
|
-
transform: scale(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.
|
|
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.
|
|
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:
|
|
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
|
|
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
|
}
|
package/test-insert.html
ADDED
|
@@ -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>
|