underpost 2.90.1 → 2.90.4
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/README.md +3 -3
- package/bin/deploy.js +16 -0
- package/cli.md +31 -5
- package/examples/QUICK-REFERENCE.md +499 -0
- package/examples/README.md +447 -0
- package/examples/STATIC-GENERATOR-GUIDE.md +807 -0
- package/examples/ssr-components/CustomPage.js +579 -0
- package/examples/static-config-example.json +183 -0
- package/examples/static-config-simple.json +57 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/src/api/document/document.model.js +7 -0
- package/src/api/document/document.service.js +4 -1
- package/src/cli/index.js +91 -4
- package/src/cli/run.js +1 -1
- package/src/cli/static.js +785 -49
- package/src/client/components/core/Input.js +6 -4
- package/src/client/components/core/Modal.js +13 -18
- package/src/client/components/core/Panel.js +26 -6
- package/src/client/components/core/PanelForm.js +67 -52
- package/src/index.js +1 -1
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Page SSR Component Example
|
|
3
|
+
* @module examples/ssr-components/CustomPage
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* This is an example SSR component demonstrating best practices for creating
|
|
7
|
+
* custom static pages with the Underpost Static Site Generator.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Usage with static generator
|
|
11
|
+
* underpost static \
|
|
12
|
+
* --page ./examples/ssr-components/CustomPage.js \
|
|
13
|
+
* --output-path ./dist/custom.html
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Usage in configuration file
|
|
17
|
+
* {
|
|
18
|
+
* "page": "./examples/ssr-components/CustomPage.js",
|
|
19
|
+
* "outputPath": "./dist/custom.html"
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Main SSR Component for rendering a custom landing page
|
|
25
|
+
*
|
|
26
|
+
* This component demonstrates:
|
|
27
|
+
* - Semantic HTML structure
|
|
28
|
+
* - Accessibility best practices
|
|
29
|
+
* - Responsive design patterns
|
|
30
|
+
* - Progressive enhancement
|
|
31
|
+
* - Clean, maintainable code
|
|
32
|
+
*
|
|
33
|
+
* @function SrrComponent
|
|
34
|
+
* @returns {string} HTML string for the page body
|
|
35
|
+
*/
|
|
36
|
+
SrrComponent = () => html`
|
|
37
|
+
<!-- Main Container -->
|
|
38
|
+
<div class="custom-page">
|
|
39
|
+
<!-- Hero Section -->
|
|
40
|
+
<section class="hero" role="banner">
|
|
41
|
+
<div class="hero-content">
|
|
42
|
+
<h1 class="hero-title">Welcome to Our Application</h1>
|
|
43
|
+
<p class="hero-subtitle">Building amazing experiences with Underpost</p>
|
|
44
|
+
<div class="hero-actions">
|
|
45
|
+
<button class="btn btn-primary" id="get-started">Get Started</button>
|
|
46
|
+
<button class="btn btn-secondary" id="learn-more">Learn More</button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="hero-image" role="img" aria-label="Application preview">
|
|
50
|
+
<!-- Hero image background via CSS -->
|
|
51
|
+
</div>
|
|
52
|
+
</section>
|
|
53
|
+
|
|
54
|
+
<!-- Features Section -->
|
|
55
|
+
<section class="features" role="region" aria-labelledby="features-heading">
|
|
56
|
+
<h2 id="features-heading" class="section-title">Features</h2>
|
|
57
|
+
<div class="features-grid">
|
|
58
|
+
<article class="feature-card">
|
|
59
|
+
<div class="feature-icon" aria-hidden="true">🚀</div>
|
|
60
|
+
<h3 class="feature-title">Fast Performance</h3>
|
|
61
|
+
<p class="feature-description">Lightning-fast static pages with optimized delivery</p>
|
|
62
|
+
</article>
|
|
63
|
+
|
|
64
|
+
<article class="feature-card">
|
|
65
|
+
<div class="feature-icon" aria-hidden="true">🎨</div>
|
|
66
|
+
<h3 class="feature-title">Customizable</h3>
|
|
67
|
+
<p class="feature-description">Fully customizable with metadata, scripts, and styles</p>
|
|
68
|
+
</article>
|
|
69
|
+
|
|
70
|
+
<article class="feature-card">
|
|
71
|
+
<div class="feature-icon" aria-hidden="true">📱</div>
|
|
72
|
+
<h3 class="feature-title">Responsive</h3>
|
|
73
|
+
<p class="feature-description">Works seamlessly across all devices and screen sizes</p>
|
|
74
|
+
</article>
|
|
75
|
+
|
|
76
|
+
<article class="feature-card">
|
|
77
|
+
<div class="feature-icon" aria-hidden="true">♿</div>
|
|
78
|
+
<h3 class="feature-title">Accessible</h3>
|
|
79
|
+
<p class="feature-description">Built with accessibility in mind for all users</p>
|
|
80
|
+
</article>
|
|
81
|
+
</div>
|
|
82
|
+
</section>
|
|
83
|
+
|
|
84
|
+
<!-- Content Section -->
|
|
85
|
+
<section class="content" role="region" aria-labelledby="content-heading">
|
|
86
|
+
<div class="content-container">
|
|
87
|
+
<h2 id="content-heading" class="section-title">Why Choose Us?</h2>
|
|
88
|
+
<div class="content-grid">
|
|
89
|
+
<div class="content-text">
|
|
90
|
+
<p>
|
|
91
|
+
Our static site generator provides everything you need to create modern, performant web pages with
|
|
92
|
+
comprehensive SEO support, PWA capabilities, and full customization options.
|
|
93
|
+
</p>
|
|
94
|
+
<ul class="content-list">
|
|
95
|
+
<li>✓ Complete metadata control</li>
|
|
96
|
+
<li>✓ Script and stylesheet injection</li>
|
|
97
|
+
<li>✓ SSR component system</li>
|
|
98
|
+
<li>✓ JSON-LD structured data</li>
|
|
99
|
+
<li>✓ Production-ready builds</li>
|
|
100
|
+
</ul>
|
|
101
|
+
</div>
|
|
102
|
+
<div class="content-media">
|
|
103
|
+
<div class="placeholder-image" role="img" aria-label="Feature showcase">
|
|
104
|
+
<!-- Image placeholder -->
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</section>
|
|
110
|
+
|
|
111
|
+
<!-- Call to Action -->
|
|
112
|
+
<section class="cta" role="region" aria-labelledby="cta-heading">
|
|
113
|
+
<div class="cta-container">
|
|
114
|
+
<h2 id="cta-heading" class="cta-title">Ready to Get Started?</h2>
|
|
115
|
+
<p class="cta-text">Join thousands of developers building with Underpost</p>
|
|
116
|
+
<form class="cta-form" id="signup-form" aria-label="Sign up form">
|
|
117
|
+
<input type="email" class="cta-input" placeholder="Enter your email" aria-label="Email address" required />
|
|
118
|
+
<button type="submit" class="btn btn-primary">Sign Up</button>
|
|
119
|
+
</form>
|
|
120
|
+
</div>
|
|
121
|
+
</section>
|
|
122
|
+
|
|
123
|
+
<!-- Footer -->
|
|
124
|
+
<footer class="footer" role="contentinfo">
|
|
125
|
+
<div class="footer-content">
|
|
126
|
+
<div class="footer-section">
|
|
127
|
+
<h3 class="footer-heading">About</h3>
|
|
128
|
+
<ul class="footer-links">
|
|
129
|
+
<li><a href="/about">About Us</a></li>
|
|
130
|
+
<li><a href="/team">Team</a></li>
|
|
131
|
+
<li><a href="/careers">Careers</a></li>
|
|
132
|
+
</ul>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="footer-section">
|
|
135
|
+
<h3 class="footer-heading">Resources</h3>
|
|
136
|
+
<ul class="footer-links">
|
|
137
|
+
<li><a href="/docs">Documentation</a></li>
|
|
138
|
+
<li><a href="/guides">Guides</a></li>
|
|
139
|
+
<li><a href="/api">API Reference</a></li>
|
|
140
|
+
</ul>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="footer-section">
|
|
143
|
+
<h3 class="footer-heading">Community</h3>
|
|
144
|
+
<ul class="footer-links">
|
|
145
|
+
<li><a href="/github">GitHub</a></li>
|
|
146
|
+
<li><a href="/discord">Discord</a></li>
|
|
147
|
+
<li><a href="/twitter">Twitter</a></li>
|
|
148
|
+
</ul>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="footer-section">
|
|
151
|
+
<h3 class="footer-heading">Legal</h3>
|
|
152
|
+
<ul class="footer-links">
|
|
153
|
+
<li><a href="/privacy">Privacy Policy</a></li>
|
|
154
|
+
<li><a href="/terms">Terms of Service</a></li>
|
|
155
|
+
<li><a href="/cookies">Cookie Policy</a></li>
|
|
156
|
+
</ul>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="footer-bottom">
|
|
160
|
+
<p class="footer-copyright">© ${new Date().getFullYear()} Underpost. All rights reserved.</p>
|
|
161
|
+
</div>
|
|
162
|
+
</footer>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<!-- Inline Styles (Critical CSS) -->
|
|
166
|
+
<style>
|
|
167
|
+
/* CSS Reset and Base Styles */
|
|
168
|
+
* {
|
|
169
|
+
margin: 0;
|
|
170
|
+
padding: 0;
|
|
171
|
+
box-sizing: border-box;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
body {
|
|
175
|
+
font-family:
|
|
176
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
|
|
177
|
+
'Droid Sans', 'Helvetica Neue', sans-serif;
|
|
178
|
+
line-height: 1.6;
|
|
179
|
+
color: #333;
|
|
180
|
+
background: #f8f9fa;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.custom-page {
|
|
184
|
+
min-height: 100vh;
|
|
185
|
+
display: flex;
|
|
186
|
+
flex-direction: column;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Hero Section */
|
|
190
|
+
.hero {
|
|
191
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
192
|
+
color: white;
|
|
193
|
+
padding: 80px 20px;
|
|
194
|
+
text-align: center;
|
|
195
|
+
min-height: 500px;
|
|
196
|
+
display: flex;
|
|
197
|
+
align-items: center;
|
|
198
|
+
justify-content: center;
|
|
199
|
+
position: relative;
|
|
200
|
+
overflow: hidden;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.hero-content {
|
|
204
|
+
max-width: 800px;
|
|
205
|
+
z-index: 1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.hero-title {
|
|
209
|
+
font-size: 3rem;
|
|
210
|
+
font-weight: 700;
|
|
211
|
+
margin-bottom: 1rem;
|
|
212
|
+
animation: fadeInUp 0.8s ease-out;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.hero-subtitle {
|
|
216
|
+
font-size: 1.5rem;
|
|
217
|
+
margin-bottom: 2rem;
|
|
218
|
+
opacity: 0.9;
|
|
219
|
+
animation: fadeInUp 0.8s ease-out 0.2s backwards;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.hero-actions {
|
|
223
|
+
display: flex;
|
|
224
|
+
gap: 1rem;
|
|
225
|
+
justify-content: center;
|
|
226
|
+
animation: fadeInUp 0.8s ease-out 0.4s backwards;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* Buttons */
|
|
230
|
+
.btn {
|
|
231
|
+
padding: 12px 32px;
|
|
232
|
+
font-size: 1rem;
|
|
233
|
+
font-weight: 600;
|
|
234
|
+
border: none;
|
|
235
|
+
border-radius: 8px;
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
transition: all 0.3s ease;
|
|
238
|
+
text-decoration: none;
|
|
239
|
+
display: inline-block;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.btn-primary {
|
|
243
|
+
background: white;
|
|
244
|
+
color: #667eea;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.btn-primary:hover {
|
|
248
|
+
transform: translateY(-2px);
|
|
249
|
+
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.btn-secondary {
|
|
253
|
+
background: transparent;
|
|
254
|
+
color: white;
|
|
255
|
+
border: 2px solid white;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.btn-secondary:hover {
|
|
259
|
+
background: white;
|
|
260
|
+
color: #667eea;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* Section Styles */
|
|
264
|
+
section {
|
|
265
|
+
padding: 60px 20px;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.section-title {
|
|
269
|
+
font-size: 2.5rem;
|
|
270
|
+
text-align: center;
|
|
271
|
+
margin-bottom: 3rem;
|
|
272
|
+
color: #2d3748;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Features Section */
|
|
276
|
+
.features {
|
|
277
|
+
background: white;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.features-grid {
|
|
281
|
+
display: grid;
|
|
282
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
283
|
+
gap: 2rem;
|
|
284
|
+
max-width: 1200px;
|
|
285
|
+
margin: 0 auto;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.feature-card {
|
|
289
|
+
background: #f8f9fa;
|
|
290
|
+
padding: 2rem;
|
|
291
|
+
border-radius: 12px;
|
|
292
|
+
text-align: center;
|
|
293
|
+
transition:
|
|
294
|
+
transform 0.3s ease,
|
|
295
|
+
box-shadow 0.3s ease;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.feature-card:hover {
|
|
299
|
+
transform: translateY(-5px);
|
|
300
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.feature-icon {
|
|
304
|
+
font-size: 3rem;
|
|
305
|
+
margin-bottom: 1rem;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.feature-title {
|
|
309
|
+
font-size: 1.5rem;
|
|
310
|
+
margin-bottom: 1rem;
|
|
311
|
+
color: #2d3748;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.feature-description {
|
|
315
|
+
color: #4a5568;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* Content Section */
|
|
319
|
+
.content {
|
|
320
|
+
background: #f8f9fa;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.content-container {
|
|
324
|
+
max-width: 1200px;
|
|
325
|
+
margin: 0 auto;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.content-grid {
|
|
329
|
+
display: grid;
|
|
330
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
331
|
+
gap: 3rem;
|
|
332
|
+
align-items: center;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.content-list {
|
|
336
|
+
list-style: none;
|
|
337
|
+
margin-top: 1.5rem;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.content-list li {
|
|
341
|
+
padding: 0.5rem 0;
|
|
342
|
+
font-size: 1.1rem;
|
|
343
|
+
color: #4a5568;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.placeholder-image {
|
|
347
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
348
|
+
height: 300px;
|
|
349
|
+
border-radius: 12px;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* CTA Section */
|
|
353
|
+
.cta {
|
|
354
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
355
|
+
color: white;
|
|
356
|
+
text-align: center;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.cta-container {
|
|
360
|
+
max-width: 600px;
|
|
361
|
+
margin: 0 auto;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.cta-title {
|
|
365
|
+
font-size: 2.5rem;
|
|
366
|
+
margin-bottom: 1rem;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.cta-text {
|
|
370
|
+
font-size: 1.2rem;
|
|
371
|
+
margin-bottom: 2rem;
|
|
372
|
+
opacity: 0.9;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.cta-form {
|
|
376
|
+
display: flex;
|
|
377
|
+
gap: 1rem;
|
|
378
|
+
max-width: 500px;
|
|
379
|
+
margin: 0 auto;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.cta-input {
|
|
383
|
+
flex: 1;
|
|
384
|
+
padding: 12px 20px;
|
|
385
|
+
border: none;
|
|
386
|
+
border-radius: 8px;
|
|
387
|
+
font-size: 1rem;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* Footer */
|
|
391
|
+
.footer {
|
|
392
|
+
background: #2d3748;
|
|
393
|
+
color: white;
|
|
394
|
+
padding: 60px 20px 20px;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.footer-content {
|
|
398
|
+
max-width: 1200px;
|
|
399
|
+
margin: 0 auto;
|
|
400
|
+
display: grid;
|
|
401
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
402
|
+
gap: 2rem;
|
|
403
|
+
margin-bottom: 2rem;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.footer-heading {
|
|
407
|
+
margin-bottom: 1rem;
|
|
408
|
+
font-size: 1.2rem;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.footer-links {
|
|
412
|
+
list-style: none;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.footer-links a {
|
|
416
|
+
color: #a0aec0;
|
|
417
|
+
text-decoration: none;
|
|
418
|
+
display: block;
|
|
419
|
+
padding: 0.3rem 0;
|
|
420
|
+
transition: color 0.3s ease;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.footer-links a:hover {
|
|
424
|
+
color: white;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.footer-bottom {
|
|
428
|
+
text-align: center;
|
|
429
|
+
padding-top: 2rem;
|
|
430
|
+
border-top: 1px solid #4a5568;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.footer-copyright {
|
|
434
|
+
color: #a0aec0;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* Animations */
|
|
438
|
+
@keyframes fadeInUp {
|
|
439
|
+
from {
|
|
440
|
+
opacity: 0;
|
|
441
|
+
transform: translateY(30px);
|
|
442
|
+
}
|
|
443
|
+
to {
|
|
444
|
+
opacity: 1;
|
|
445
|
+
transform: translateY(0);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/* Responsive Design */
|
|
450
|
+
@media (max-width: 768px) {
|
|
451
|
+
.hero-title {
|
|
452
|
+
font-size: 2rem;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.hero-subtitle {
|
|
456
|
+
font-size: 1.2rem;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.hero-actions {
|
|
460
|
+
flex-direction: column;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.section-title {
|
|
464
|
+
font-size: 2rem;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.cta-form {
|
|
468
|
+
flex-direction: column;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.cta-title {
|
|
472
|
+
font-size: 2rem;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/* Accessibility - Focus Styles */
|
|
477
|
+
*:focus {
|
|
478
|
+
outline: 3px solid #667eea;
|
|
479
|
+
outline-offset: 2px;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/* Print Styles */
|
|
483
|
+
@media print {
|
|
484
|
+
.hero,
|
|
485
|
+
.cta {
|
|
486
|
+
background: white !important;
|
|
487
|
+
color: black !important;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.btn {
|
|
491
|
+
display: none;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
</style>
|
|
495
|
+
|
|
496
|
+
<!-- Inline JavaScript (Progressive Enhancement) -->
|
|
497
|
+
<script>
|
|
498
|
+
// Wait for DOM to be ready
|
|
499
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
500
|
+
console.log('Custom page loaded successfully');
|
|
501
|
+
|
|
502
|
+
// Get Started button handler
|
|
503
|
+
const getStartedBtn = document.getElementById('get-started');
|
|
504
|
+
if (getStartedBtn) {
|
|
505
|
+
getStartedBtn.addEventListener('click', function () {
|
|
506
|
+
window.location.href = '/signup';
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Learn More button handler
|
|
511
|
+
const learnMoreBtn = document.getElementById('learn-more');
|
|
512
|
+
if (learnMoreBtn) {
|
|
513
|
+
learnMoreBtn.addEventListener('click', function () {
|
|
514
|
+
document.querySelector('.features').scrollIntoView({
|
|
515
|
+
behavior: 'smooth',
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Signup form handler
|
|
521
|
+
const signupForm = document.getElementById('signup-form');
|
|
522
|
+
if (signupForm) {
|
|
523
|
+
signupForm.addEventListener('submit', function (e) {
|
|
524
|
+
e.preventDefault();
|
|
525
|
+
const email = this.querySelector('input[type="email"]').value;
|
|
526
|
+
|
|
527
|
+
// Basic email validation
|
|
528
|
+
if (email && /^[^s@]+@[^s@]+.[^s@]+$/.test(email)) {
|
|
529
|
+
alert('Thank you for signing up! We will contact you soon.');
|
|
530
|
+
this.reset();
|
|
531
|
+
} else {
|
|
532
|
+
alert('Please enter a valid email address.');
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Intersection Observer for animations
|
|
538
|
+
if ('IntersectionObserver' in window) {
|
|
539
|
+
const observerOptions = {
|
|
540
|
+
threshold: 0.1,
|
|
541
|
+
rootMargin: '0px 0px -50px 0px',
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
const observer = new IntersectionObserver(function (entries) {
|
|
545
|
+
entries.forEach(function (entry) {
|
|
546
|
+
if (entry.isIntersecting) {
|
|
547
|
+
entry.target.style.opacity = '1';
|
|
548
|
+
entry.target.style.transform = 'translateY(0)';
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
}, observerOptions);
|
|
552
|
+
|
|
553
|
+
// Observe feature cards
|
|
554
|
+
document.querySelectorAll('.feature-card').forEach(function (card, index) {
|
|
555
|
+
card.style.opacity = '0';
|
|
556
|
+
card.style.transform = 'translateY(30px)';
|
|
557
|
+
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
|
558
|
+
card.style.transitionDelay = index * 0.1 + 's';
|
|
559
|
+
observer.observe(card);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Accessibility: Skip to content link
|
|
564
|
+
const skipLink = document.createElement('a');
|
|
565
|
+
skipLink.href = '#main-content';
|
|
566
|
+
skipLink.textContent = 'Skip to main content';
|
|
567
|
+
skipLink.className = 'skip-link';
|
|
568
|
+
skipLink.style.cssText =
|
|
569
|
+
'position: absolute; top: -40px; left: 0; background: #667eea; color: white; padding: 8px 16px; text-decoration: none; z-index: 100;';
|
|
570
|
+
skipLink.addEventListener('focus', function () {
|
|
571
|
+
this.style.top = '0';
|
|
572
|
+
});
|
|
573
|
+
skipLink.addEventListener('blur', function () {
|
|
574
|
+
this.style.top = '-40px';
|
|
575
|
+
});
|
|
576
|
+
document.body.insertBefore(skipLink, document.body.firstChild);
|
|
577
|
+
});
|
|
578
|
+
</script>
|
|
579
|
+
`;
|