create-template-html-css 1.0.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/CONTRIBUTING.md +62 -0
- package/INSERT-DEMO.md +142 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +195 -0
- package/README.md +0 -0
- package/SECURITY.md +95 -0
- package/SHOWCASE.html +342 -0
- package/bin/cli.js +158 -0
- package/package.json +50 -0
- package/src/generator.js +64 -0
- package/src/index.js +1 -0
- package/src/inserter.js +99 -0
- package/templates/button/index.html +32 -0
- package/templates/button/script.js +15 -0
- package/templates/button/style.css +108 -0
- package/templates/card/index.html +52 -0
- package/templates/card/script.js +9 -0
- package/templates/card/style.css +103 -0
- package/templates/footer/index.html +67 -0
- package/templates/footer/script.js +43 -0
- package/templates/footer/style.css +165 -0
- package/templates/form/index.html +54 -0
- package/templates/form/script.js +34 -0
- package/templates/form/style.css +137 -0
- package/templates/hero/index.html +73 -0
- package/templates/hero/script.js +80 -0
- package/templates/hero/style.css +272 -0
- package/templates/modal/index.html +63 -0
- package/templates/modal/script.js +69 -0
- package/templates/modal/style.css +223 -0
- package/templates/navigation/index.html +69 -0
- package/templates/navigation/script.js +61 -0
- package/templates/navigation/style.css +161 -0
package/src/inserter.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
// Security: Validate component name against whitelist
|
|
5
|
+
const VALID_COMPONENTS = ['button', 'card', 'form', 'navigation', 'modal', 'footer', 'hero'];
|
|
6
|
+
|
|
7
|
+
async function insertComponent(options) {
|
|
8
|
+
const { component, targetFile, styleMode, scriptMode } = options;
|
|
9
|
+
|
|
10
|
+
// Security: Validate component name
|
|
11
|
+
if (!VALID_COMPONENTS.includes(component)) {
|
|
12
|
+
throw new Error(`Invalid component: ${component}. Must be one of: ${VALID_COMPONENTS.join(', ')}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Check if target file exists
|
|
16
|
+
const targetPath = path.resolve(process.cwd(), targetFile);
|
|
17
|
+
try {
|
|
18
|
+
await fs.access(targetPath);
|
|
19
|
+
} catch {
|
|
20
|
+
throw new Error(`Target file not found: ${targetFile}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Read target HTML file
|
|
24
|
+
let htmlContent = await fs.readFile(targetPath, 'utf-8');
|
|
25
|
+
|
|
26
|
+
// Get component templates
|
|
27
|
+
const templateDir = path.join(__dirname, '..', 'templates', component);
|
|
28
|
+
const componentHtml = await fs.readFile(path.join(templateDir, 'index.html'), 'utf-8');
|
|
29
|
+
const componentCss = await fs.readFile(path.join(templateDir, 'style.css'), 'utf-8');
|
|
30
|
+
|
|
31
|
+
// Extract component body content (between <body> tags, excluding container if needed)
|
|
32
|
+
const bodyMatch = componentHtml.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
|
33
|
+
if (!bodyMatch) {
|
|
34
|
+
throw new Error('Invalid component template structure');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let componentBody = bodyMatch[1].trim();
|
|
38
|
+
|
|
39
|
+
// 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
|
+
}
|
|
45
|
+
|
|
46
|
+
// Handle CSS
|
|
47
|
+
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
|
+
}
|
|
53
|
+
} else if (styleMode === 'separate') {
|
|
54
|
+
// Create separate CSS file
|
|
55
|
+
const cssFileName = `${component}-component.css`;
|
|
56
|
+
const cssPath = path.join(path.dirname(targetPath), cssFileName);
|
|
57
|
+
await fs.writeFile(cssPath, `/* ${component.toUpperCase()} Component Styles */\n${componentCss}`);
|
|
58
|
+
|
|
59
|
+
// 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
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle JavaScript
|
|
67
|
+
try {
|
|
68
|
+
const componentJs = await fs.readFile(path.join(templateDir, 'script.js'), 'utf-8');
|
|
69
|
+
|
|
70
|
+
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>`);
|
|
74
|
+
} else if (scriptMode === 'separate') {
|
|
75
|
+
// Create separate JS file
|
|
76
|
+
const jsFileName = `${component}-component.js`;
|
|
77
|
+
const jsPath = path.join(path.dirname(targetPath), jsFileName);
|
|
78
|
+
await fs.writeFile(jsPath, `// ${component.toUpperCase()} Component Script\n${componentJs}`);
|
|
79
|
+
|
|
80
|
+
// Add script tag
|
|
81
|
+
const scriptTag = `\n <script src="${jsFileName}"></script>\n`;
|
|
82
|
+
htmlContent = htmlContent.replace('</body>', `${scriptTag}</body>`);
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// No JavaScript file for this component, skip
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Write updated HTML
|
|
89
|
+
await fs.writeFile(targetPath, htmlContent);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
targetFile: targetPath,
|
|
93
|
+
component,
|
|
94
|
+
styleMode,
|
|
95
|
+
scriptMode
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = { insertComponent };
|
|
@@ -0,0 +1,32 @@
|
|
|
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}} - Button Component</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<h1>Button Component</h1>
|
|
12
|
+
|
|
13
|
+
<!-- Primary Button -->
|
|
14
|
+
<button class="btn btn-primary">Click Here</button>
|
|
15
|
+
|
|
16
|
+
<!-- Secondary Button -->
|
|
17
|
+
<button class="btn btn-secondary">Secondary</button>
|
|
18
|
+
|
|
19
|
+
<!-- Success Button -->
|
|
20
|
+
<button class="btn btn-success">Success</button>
|
|
21
|
+
|
|
22
|
+
<!-- Danger Button -->
|
|
23
|
+
<button class="btn btn-danger">Delete</button>
|
|
24
|
+
|
|
25
|
+
<!-- Outlined Button -->
|
|
26
|
+
<button class="btn btn-outlined">Outlined</button>
|
|
27
|
+
|
|
28
|
+
<!-- Disabled Button -->
|
|
29
|
+
<button class="btn btn-primary" disabled>Disabled</button>
|
|
30
|
+
</div>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Button click animations
|
|
2
|
+
document.querySelectorAll('.btn').forEach(button => {
|
|
3
|
+
button.addEventListener('click', function(e) {
|
|
4
|
+
if (!this.disabled) {
|
|
5
|
+
console.log('Button clicked:', this.textContent);
|
|
6
|
+
|
|
7
|
+
// Ripple effect
|
|
8
|
+
const ripple = document.createElement('span');
|
|
9
|
+
ripple.classList.add('ripple');
|
|
10
|
+
this.appendChild(ripple);
|
|
11
|
+
|
|
12
|
+
setTimeout(() => ripple.remove(), 600);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
background: white;
|
|
19
|
+
padding: 40px;
|
|
20
|
+
border-radius: 15px;
|
|
21
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
22
|
+
text-align: center;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
h1 {
|
|
26
|
+
margin-bottom: 30px;
|
|
27
|
+
color: #333;
|
|
28
|
+
font-size: 2rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.btn {
|
|
32
|
+
padding: 12px 30px;
|
|
33
|
+
margin: 10px;
|
|
34
|
+
border: none;
|
|
35
|
+
border-radius: 8px;
|
|
36
|
+
font-size: 16px;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
transition: all 0.3s ease;
|
|
40
|
+
text-transform: uppercase;
|
|
41
|
+
letter-spacing: 1px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.btn:hover {
|
|
45
|
+
transform: translateY(-2px);
|
|
46
|
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.btn:active {
|
|
50
|
+
transform: translateY(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.btn-primary {
|
|
54
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
55
|
+
color: white;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.btn-primary:hover {
|
|
59
|
+
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.btn-secondary {
|
|
63
|
+
background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
|
|
64
|
+
color: white;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.btn-secondary:hover {
|
|
68
|
+
background: linear-gradient(135deg, #495057 0%, #6c757d 100%);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.btn-success {
|
|
72
|
+
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
|
|
73
|
+
color: white;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.btn-success:hover {
|
|
77
|
+
background: linear-gradient(135deg, #20c997 0%, #28a745 100%);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.btn-danger {
|
|
81
|
+
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
|
|
82
|
+
color: white;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.btn-danger:hover {
|
|
86
|
+
background: linear-gradient(135deg, #c82333 0%, #dc3545 100%);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.btn-outlined {
|
|
90
|
+
background: transparent;
|
|
91
|
+
color: #667eea;
|
|
92
|
+
border: 2px solid #667eea;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.btn-outlined:hover {
|
|
96
|
+
background: #667eea;
|
|
97
|
+
color: white;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.btn:disabled {
|
|
101
|
+
opacity: 0.5;
|
|
102
|
+
cursor: not-allowed;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.btn:disabled:hover {
|
|
106
|
+
transform: none;
|
|
107
|
+
box-shadow: none;
|
|
108
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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}} - Card Component</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<h1>Card Components</h1>
|
|
12
|
+
|
|
13
|
+
<div class="cards-grid">
|
|
14
|
+
<!-- Card 1 -->
|
|
15
|
+
<div class="card">
|
|
16
|
+
<img src="https://via.placeholder.com/300x200" alt="Card Image" class="card-img">
|
|
17
|
+
<div class="card-body">
|
|
18
|
+
<h2 class="card-title">Card Title</h2>
|
|
19
|
+
<p class="card-text">
|
|
20
|
+
This is a card description. Here you can add more information about the content.
|
|
21
|
+
</p>
|
|
22
|
+
<button class="card-btn">Read More</button>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<!-- Card 2 -->
|
|
27
|
+
<div class="card">
|
|
28
|
+
<img src="https://via.placeholder.com/300x200/667eea" alt="Card Image" class="card-img">
|
|
29
|
+
<div class="card-body">
|
|
30
|
+
<h2 class="card-title">Styled Card</h2>
|
|
31
|
+
<p class="card-text">
|
|
32
|
+
Clean and modern design with shadows and animations.
|
|
33
|
+
</p>
|
|
34
|
+
<button class="card-btn">Learn More</button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- Card 3 -->
|
|
39
|
+
<div class="card">
|
|
40
|
+
<img src="https://via.placeholder.com/300x200/764ba2" alt="Card Image" class="card-img">
|
|
41
|
+
<div class="card-body">
|
|
42
|
+
<h2 class="card-title">Third Card</h2>
|
|
43
|
+
<p class="card-text">
|
|
44
|
+
More interesting content displayed in an attractive and pleasant way.
|
|
45
|
+
</p>
|
|
46
|
+
<button class="card-btn">Get Started</button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</body>
|
|
52
|
+
</html>
|
|
@@ -0,0 +1,9 @@
|
|
|
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}`);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
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, #f5f7fa 0%, #c3cfe2 100%);
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
padding: 40px 20px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.container {
|
|
15
|
+
max-width: 1200px;
|
|
16
|
+
margin: 0 auto;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
h1 {
|
|
20
|
+
text-align: center;
|
|
21
|
+
color: #333;
|
|
22
|
+
margin-bottom: 40px;
|
|
23
|
+
font-size: 2.5rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.cards-grid {
|
|
27
|
+
display: grid;
|
|
28
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
29
|
+
gap: 30px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.card {
|
|
33
|
+
background: white;
|
|
34
|
+
border-radius: 15px;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
|
37
|
+
transition: all 0.3s ease;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.card:hover {
|
|
41
|
+
transform: translateY(-10px);
|
|
42
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.card-img {
|
|
46
|
+
width: 100%;
|
|
47
|
+
height: 200px;
|
|
48
|
+
object-fit: cover;
|
|
49
|
+
transition: transform 0.3s ease;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.card:hover .card-img {
|
|
53
|
+
transform: scale(1.1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.card-body {
|
|
57
|
+
padding: 25px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.card-title {
|
|
61
|
+
color: #333;
|
|
62
|
+
font-size: 1.5rem;
|
|
63
|
+
margin-bottom: 15px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.card-text {
|
|
67
|
+
color: #666;
|
|
68
|
+
line-height: 1.6;
|
|
69
|
+
margin-bottom: 20px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.card-btn {
|
|
73
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
74
|
+
color: white;
|
|
75
|
+
border: none;
|
|
76
|
+
padding: 12px 25px;
|
|
77
|
+
border-radius: 8px;
|
|
78
|
+
font-size: 14px;
|
|
79
|
+
font-weight: 600;
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
transition: all 0.3s ease;
|
|
82
|
+
text-transform: uppercase;
|
|
83
|
+
letter-spacing: 1px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.card-btn:hover {
|
|
87
|
+
transform: translateY(-2px);
|
|
88
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.card-btn:active {
|
|
92
|
+
transform: translateY(0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@media (max-width: 768px) {
|
|
96
|
+
.cards-grid {
|
|
97
|
+
grid-template-columns: 1fr;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
h1 {
|
|
101
|
+
font-size: 2rem;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -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}} - Footer Component</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="page-content">
|
|
11
|
+
<h1>Page Content</h1>
|
|
12
|
+
<p>Scroll down to see the Footer</p>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<footer class="footer">
|
|
16
|
+
<div class="footer-container">
|
|
17
|
+
<div class="footer-section">
|
|
18
|
+
<h3 class="footer-title">About Us</h3>
|
|
19
|
+
<p class="footer-text">
|
|
20
|
+
We are a company specializing in developing advanced websites and applications.
|
|
21
|
+
Our goal is to provide quality solutions to our clients.
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="footer-section">
|
|
26
|
+
<h3 class="footer-title">Quick Links</h3>
|
|
27
|
+
<ul class="footer-links">
|
|
28
|
+
<li><a href="#home">Home</a></li>
|
|
29
|
+
<li><a href="#about">About</a></li>
|
|
30
|
+
<li><a href="#services">Services</a></li>
|
|
31
|
+
<li><a href="#contact">Contact</a></li>
|
|
32
|
+
<li><a href="#blog">Blog</a></li>
|
|
33
|
+
</ul>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="footer-section">
|
|
37
|
+
<h3 class="footer-title">Services</h3>
|
|
38
|
+
<ul class="footer-links">
|
|
39
|
+
<li><a href="#web">Web Development</a></li>
|
|
40
|
+
<li><a href="#mobile">Mobile Apps</a></li>
|
|
41
|
+
<li><a href="#design">UI/UX Design</a></li>
|
|
42
|
+
<li><a href="#seo">SEO</a></li>
|
|
43
|
+
</ul>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="footer-section">
|
|
47
|
+
<h3 class="footer-title">Contact Us</h3>
|
|
48
|
+
<div class="footer-contact">
|
|
49
|
+
<p>📧 info@example.com</p>
|
|
50
|
+
<p>📱 +1 234-567-8900</p>
|
|
51
|
+
<p>📍 New York, USA</p>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="footer-social">
|
|
54
|
+
<a href="#" class="social-link">FB</a>
|
|
55
|
+
<a href="#" class="social-link">TW</a>
|
|
56
|
+
<a href="#" class="social-link">IN</a>
|
|
57
|
+
<a href="#" class="social-link">YT</a>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div class="footer-bottom">
|
|
63
|
+
<p>© 2026 All rights reserved | Designed with ❤️</p>
|
|
64
|
+
</div>
|
|
65
|
+
</footer>
|
|
66
|
+
</body>
|
|
67
|
+
</html>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Animate footer sections on scroll
|
|
2
|
+
const observerOptions = {
|
|
3
|
+
threshold: 0.1,
|
|
4
|
+
rootMargin: '0px 0px -50px 0px'
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const observer = new IntersectionObserver((entries) => {
|
|
8
|
+
entries.forEach((entry, index) => {
|
|
9
|
+
if (entry.isIntersecting) {
|
|
10
|
+
setTimeout(() => {
|
|
11
|
+
entry.target.style.opacity = '1';
|
|
12
|
+
entry.target.style.transform = 'translateY(0)';
|
|
13
|
+
}, index * 100);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}, observerOptions);
|
|
17
|
+
|
|
18
|
+
document.querySelectorAll('.footer-section').forEach(section => {
|
|
19
|
+
section.style.opacity = '0';
|
|
20
|
+
section.style.transform = 'translateY(20px)';
|
|
21
|
+
section.style.transition = 'all 0.6s ease';
|
|
22
|
+
observer.observe(section);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Smooth scroll for footer links
|
|
26
|
+
document.querySelectorAll('.footer-links a').forEach(link => {
|
|
27
|
+
link.addEventListener('click', function(e) {
|
|
28
|
+
const href = this.getAttribute('href');
|
|
29
|
+
if (href.startsWith('#')) {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
console.log('Navigating to:', href);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Social links
|
|
37
|
+
document.querySelectorAll('.social-link').forEach(link => {
|
|
38
|
+
link.addEventListener('click', function(e) {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
console.log('Social link clicked');
|
|
41
|
+
alert('Opening social network...');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
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: #f5f5f5;
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
min-height: 100vh;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.page-content {
|
|
16
|
+
flex: 1;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
align-items: center;
|
|
21
|
+
padding: 40px 20px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.page-content h1 {
|
|
25
|
+
font-size: 3rem;
|
|
26
|
+
color: #333;
|
|
27
|
+
margin-bottom: 20px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.page-content p {
|
|
31
|
+
font-size: 1.2rem;
|
|
32
|
+
color: #666;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Footer Styles */
|
|
36
|
+
.footer {
|
|
37
|
+
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
|
38
|
+
color: white;
|
|
39
|
+
padding: 60px 20px 20px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.footer-container {
|
|
43
|
+
max-width: 1200px;
|
|
44
|
+
margin: 0 auto;
|
|
45
|
+
display: grid;
|
|
46
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
47
|
+
gap: 40px;
|
|
48
|
+
margin-bottom: 40px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.footer-section {
|
|
52
|
+
animation: fadeInUp 0.6s ease;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@keyframes fadeInUp {
|
|
56
|
+
from {
|
|
57
|
+
opacity: 0;
|
|
58
|
+
transform: translateY(20px);
|
|
59
|
+
}
|
|
60
|
+
to {
|
|
61
|
+
opacity: 1;
|
|
62
|
+
transform: translateY(0);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.footer-title {
|
|
67
|
+
font-size: 1.5rem;
|
|
68
|
+
margin-bottom: 20px;
|
|
69
|
+
position: relative;
|
|
70
|
+
padding-bottom: 10px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.footer-title::after {
|
|
74
|
+
content: '';
|
|
75
|
+
position: absolute;
|
|
76
|
+
right: 0;
|
|
77
|
+
bottom: 0;
|
|
78
|
+
width: 50px;
|
|
79
|
+
height: 3px;
|
|
80
|
+
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
81
|
+
border-radius: 3px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.footer-text {
|
|
85
|
+
line-height: 1.6;
|
|
86
|
+
color: rgba(255, 255, 255, 0.8);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.footer-links {
|
|
90
|
+
list-style: none;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.footer-links li {
|
|
94
|
+
margin-bottom: 12px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.footer-links a {
|
|
98
|
+
color: rgba(255, 255, 255, 0.8);
|
|
99
|
+
text-decoration: none;
|
|
100
|
+
transition: all 0.3s ease;
|
|
101
|
+
display: inline-block;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.footer-links a:hover {
|
|
105
|
+
color: white;
|
|
106
|
+
transform: translateX(-5px);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.footer-contact p {
|
|
110
|
+
margin-bottom: 15px;
|
|
111
|
+
color: rgba(255, 255, 255, 0.8);
|
|
112
|
+
font-size: 1rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.footer-social {
|
|
116
|
+
display: flex;
|
|
117
|
+
gap: 15px;
|
|
118
|
+
margin-top: 20px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.social-link {
|
|
122
|
+
width: 40px;
|
|
123
|
+
height: 40px;
|
|
124
|
+
background: rgba(255, 255, 255, 0.1);
|
|
125
|
+
border-radius: 50%;
|
|
126
|
+
display: flex;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
align-items: center;
|
|
129
|
+
color: white;
|
|
130
|
+
text-decoration: none;
|
|
131
|
+
font-weight: bold;
|
|
132
|
+
transition: all 0.3s ease;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.social-link:hover {
|
|
136
|
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
137
|
+
transform: translateY(-5px);
|
|
138
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.footer-bottom {
|
|
142
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
143
|
+
padding-top: 30px;
|
|
144
|
+
text-align: center;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.footer-bottom p {
|
|
148
|
+
color: rgba(255, 255, 255, 0.6);
|
|
149
|
+
font-size: 0.9rem;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@media (max-width: 768px) {
|
|
153
|
+
.footer {
|
|
154
|
+
padding: 40px 20px 20px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.footer-container {
|
|
158
|
+
grid-template-columns: 1fr;
|
|
159
|
+
gap: 30px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.page-content h1 {
|
|
163
|
+
font-size: 2rem;
|
|
164
|
+
}
|
|
165
|
+
}
|