feat(services-cards): add accordion feature to section
This commit is contained in:
parent
d742364f46
commit
b109387471
|
@ -4,6 +4,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="{{ metaDesc }}">
|
||||
|
||||
<title>{{title}}</title>
|
||||
|
||||
|
@ -11,6 +12,11 @@
|
|||
<link rel="stylesheet" href="/styles/base.css">
|
||||
<link rel="stylesheet" href="/styles/header-footer.css">
|
||||
<link rel="stylesheet" href="{{ stylesheet }}">
|
||||
|
||||
{% if hCAPTCHA %}
|
||||
<script src="{{ hCAPTCHA }}" async defer></script>
|
||||
{% endif %}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
---
|
||||
layout: base.njk
|
||||
metaDesc: "Learn about Derek L. Seitz's values as a freelance developer, his commitment to accessible design, and his collaborative approach to web development."
|
||||
title: "About Derek"
|
||||
stylesheet: /styles/about.css
|
||||
isLandingPage: false
|
||||
hCAPTCHA: "https://hcaptcha.com/1/api.js"
|
||||
pageScripts:
|
||||
- "/scripts/contact-form.js"
|
||||
- "/scripts/accordion.js"
|
||||
---
|
||||
|
||||
<section class="about-section module" id="bio" role="region" aria-labelledby="about-heading">
|
||||
<h1 id="about-heading">Who <span class="i-am">I</span> Am</h1>
|
||||
<h2 id="about-heading">Who <span class="i-am">I</span> Am</h2>
|
||||
<p>I’m a freelance developer dedicated to building clean, functional, and accessible web experiences. But beyond the code, I’m someone driven by a deep sense of purpose and a commitment to creating digital solutions that <strong>genuinely</strong> serve people.</p>
|
||||
<p>My approach is simple: solve real problems and build with integrity. I believe great websites don’t just look good—they respect users’ time, adapt to diverse needs, and are built on a foundation of honesty and transparency. That’s why I communicate openly with clients, build trust through consistency, and deliver work that is both technically sound and thoughtfully designed.</p>
|
||||
<p class="click">Click on the cards below to learn more about what makes me who I am.</p>
|
||||
|
@ -106,10 +108,15 @@ pageScripts:
|
|||
<textarea id="message" name="message" rows="15" placeholder="What questions or feedback do you have for me?" required aria-describedby="message-error"></textarea>
|
||||
<span class="error-message" id="message-error" aria-live="polite"></span>
|
||||
</fieldset>
|
||||
<div class="h-captcha" data-sitekey="b63e5b64-c6f2-4154-b5a6-77169a924022"></div>
|
||||
<div class="submit-reset">
|
||||
<button type="submit">Send Message</button>
|
||||
<button type="reset">Reset Form</button>
|
||||
</div>
|
||||
<div class="honeypot-field" aria-hidden="true">
|
||||
<label for="url">Website URL</label>
|
||||
<input type="text" name="url" id="url" autocomplete="off" tabindex="-1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="form-note">* Required fields</p>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
layout: base.njk
|
||||
metaDesc: "Elevate your business with a custom, high-quality website. Derek L. Seitz provides professional web design and development to help you grow your online presence."
|
||||
title: "Derek L. Seitz - Portfolio"
|
||||
stylesheet: styles/index.css
|
||||
isLandingPage: true
|
||||
pageScripts:
|
||||
- "/scripts/benefits.js"
|
||||
- "/scripts/carousel.js"
|
||||
- "/scripts/services.js"
|
||||
---
|
||||
|
||||
|
||||
|
@ -76,35 +77,36 @@ pageScripts:
|
|||
|
||||
<!--? This section will feature a carousel of services on cards. The carousel will be scrollable, and when the card is clicked or touched, it will expand to provide a clearer description of the service. This will be accomplished with JavaScript-->
|
||||
<div class="services-section module">
|
||||
<div class="carousel-container">
|
||||
<h2>The Ways I Can Help</h2>
|
||||
<div class="accordion-container">
|
||||
<h2>How I Can Help</h2>
|
||||
<p>Click each button below to see ways I can help!</p>
|
||||
|
||||
<button class="carousel-button prev-button">❮</button>
|
||||
|
||||
<div class="service-cards">
|
||||
<!-- Website Design -->
|
||||
<div class="service-card">
|
||||
<h3>Website Design</h3>
|
||||
<div class="accordion">
|
||||
<div class="accordion-item">
|
||||
<button class="accordion-header">Website Design</button>
|
||||
<div class="accordion-content">
|
||||
<ul class="design-service">
|
||||
<li>Custom web design & user experience (UX) services</li>
|
||||
<li>Discovery, planning, & wireframing</li>
|
||||
<li>Website redesign & optimization</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Website Development -->
|
||||
<div class="service-card">
|
||||
<h3>Website Development</h3>
|
||||
<div class="accordion-item">
|
||||
<button class="accordion-header">Website Development</button>
|
||||
<div class="accordion-content">
|
||||
<ul class="development-service">
|
||||
<li>Full-stack web development (Front-End & Back-End)</li>
|
||||
<li>Custom solutions (e.g., CMS, eCommerce, booking systems)</li>
|
||||
<li>Ongoing maintenance & support</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Other Services -->
|
||||
<div class="service-card">
|
||||
<h3>Other Related Services</h3>
|
||||
<div class="accordion-item">
|
||||
<button class="accordion-header">Other Related Services</button>
|
||||
<div class="accordion-content">
|
||||
<ul class="other-service">
|
||||
<li>Basic SEO setup & Google Analytics integration</li>
|
||||
<li>Hosting services</li>
|
||||
|
@ -112,13 +114,14 @@ pageScripts:
|
|||
<li>E-commerce</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<button class="carousel-button next-button">❯</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ↓ Start Services Module ↓ -->
|
||||
|
||||
<!--? This section will feature a carousel of services on cards. The carousel will be scrollable, and when the card is clicked or touched, it will expand to provide a clearer description of the service. This will be accomplished with JavaScript -->
|
||||
|
||||
<!-- ↓ Start CTA Module ↓ -->
|
||||
|
||||
|
|
|
@ -23,6 +23,28 @@ form.addEventListener("submit", function(event) {
|
|||
|
||||
clearErrors(); // Clear previous errors
|
||||
|
||||
const honeypotField = document.getElementById("url").value.trim();
|
||||
// Add the honeypot check at the top
|
||||
if (honeypotField.length > 0) {
|
||||
console.warn("Honeypot field was filled. Blocking submission.");
|
||||
// You might want to display a message to the user,
|
||||
// but it's often better to fail silently to not alert the bot.
|
||||
// For debugging, you can add an alert:
|
||||
// alert("Submission failed due to security check.");
|
||||
return; // This is the most important part: stop the function here.
|
||||
}
|
||||
|
||||
// Get the hCaptcha response token
|
||||
const hCaptchaResponse = document.querySelector('[name="h-captcha-response"]').value;
|
||||
|
||||
// Check if the hCaptcha token is missing
|
||||
if (!hCaptchaResponse) {
|
||||
// You might want to display a user-facing error here
|
||||
console.warn("hCaptcha token is missing. Submission blocked.");
|
||||
alert("Please complete the hCaptcha verification.");
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
// Get values
|
||||
const firstName = document.getElementById("first-name").value.trim();
|
||||
const lastName = document.getElementById("last-name").value.trim();
|
||||
|
@ -79,8 +101,44 @@ form.addEventListener("submit", function(event) {
|
|||
}
|
||||
|
||||
if (!hasErrors) {
|
||||
alert("Form submitted successfully!");
|
||||
// form.submit(); // Uncomment when ready to send to server
|
||||
// Package the form data into an object
|
||||
const formData = {
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
organization: organization,
|
||||
email: email,
|
||||
phone: phone,
|
||||
contactMethod: contactMethod,
|
||||
message: message,
|
||||
url: honeypotField,
|
||||
hCaptchaResponse: hCaptchaResponse
|
||||
};
|
||||
|
||||
// Send the data to the backend using fetch()
|
||||
fetch('/api/submit-form', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(formData), // Convert the data object to a JSON string
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json(); // Parse the JSON response
|
||||
}
|
||||
throw new Error('Network response was not ok.');
|
||||
})
|
||||
.then(data => {
|
||||
// Success: handle the server's response
|
||||
console.log('Success:', data);
|
||||
alert('Form submitted successfully!');
|
||||
form.reset(); // Optionally, reset the form after successful submission
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error: handle any network or server errors
|
||||
console.error('Error:', error);
|
||||
alert('An error occurred during submission. Please try again.');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const accordionHeaders = document.querySelectorAll('.accordion-header');
|
||||
|
||||
accordionHeaders.forEach(header => {
|
||||
header.addEventListener('click', function() {
|
||||
const content = this.nextElementSibling;
|
||||
|
||||
// Check if maxHeight has a value. If it does, the accordion is open.
|
||||
if (content.style.maxHeight) {
|
||||
content.style.maxHeight = null; // Close the accordion
|
||||
} else {
|
||||
// The accordion is closed, open it by setting max-height to its scroll height
|
||||
content.style.maxHeight = content.scrollHeight + 'px';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -20,7 +20,7 @@ body {
|
|||
}
|
||||
|
||||
.i-am {
|
||||
color: #ea7e0b;
|
||||
color: #ca6e0b;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ body {
|
|||
|
||||
.click {
|
||||
font-weight: bold;
|
||||
color:#ea7e0b;
|
||||
color:#ca6e0b;
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ body {
|
|||
}
|
||||
|
||||
.w3-acag21 {
|
||||
color: #ea7e0b;
|
||||
color: #ca6e0b;
|
||||
}
|
||||
|
||||
/*? ↓ Contact Section Styles ↓ */
|
||||
|
@ -208,7 +208,7 @@ textarea:focus {
|
|||
button[type="submit"], button[type="reset"] {
|
||||
margin:20px 0px 20px 0px;
|
||||
padding: 8px 25px;
|
||||
background-color: #ea7e0b;
|
||||
background-color: #ca6e0b;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
|
@ -221,6 +221,10 @@ button[type="submit"]:hover, button[type="reset"]:hover {
|
|||
background-color: #2e97be;
|
||||
}
|
||||
|
||||
.honeypot-field {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Responsive tweaks */
|
||||
@media (max-width: 768px) {
|
||||
.form-sections {
|
||||
|
|
|
@ -25,10 +25,11 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|||
.intro h1 {
|
||||
color: #495057;
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.intro h1 .accent-name {
|
||||
color: #ea7e0b;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.intro p {
|
||||
|
@ -54,8 +55,12 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.benefits-container h2, .accordion-container h2, .project-steps h2 {
|
||||
font-color: #2e97be;
|
||||
}
|
||||
|
||||
.benefits-container p, .transition p {
|
||||
font-size: 1.2em;
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
@ -68,6 +73,13 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|||
min-height: 150px;
|
||||
}
|
||||
|
||||
/* Correct media query to force a single column layout below 950px */
|
||||
@media (max-width: 950px) {
|
||||
.benefits-visual {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.benefits-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -78,7 +90,7 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|||
border-radius: 10px;
|
||||
padding: 15px 10px 15px 10px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
|
||||
transition: all 0.3s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -86,7 +98,7 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|||
.benefits-card:hover {
|
||||
transform: translateY(-5px);
|
||||
background: linear-gradient(to right, #DDF0FF 0%, #fff 40%, #fff 60%, #DDF0FF 100%);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.benefits-card h3 {
|
||||
|
@ -130,92 +142,114 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*? ↓ Start Services Container ↓ */
|
||||
|
||||
.carousel-container {
|
||||
.services-section.module {
|
||||
max-width: 850px;
|
||||
}
|
||||
|
||||
.accordion-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
margin: 10px auto;
|
||||
overflow: hidden;
|
||||
min-height: 400px;
|
||||
min-height: 150px;
|
||||
background-color: transparent; /* Ensure no background color is affecting the text */
|
||||
}
|
||||
|
||||
.carousel {
|
||||
.accordion-container h2, .accordion-container p {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.accordion-container p {
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.accordion {
|
||||
display: flex;
|
||||
transition: transform 0.5s ease-in-out;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.service-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
.accordion-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 50px auto;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
width: 300px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #dee2e6;
|
||||
background-color: #ffffff; /* Ensure a solid white background */
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.service-card h3 {
|
||||
font-size: 1.3em;
|
||||
color: #495057;
|
||||
background-color: #f8f9fa;
|
||||
.accordion-item.active {
|
||||
opacity: 1; /* Fully opaque for the focused card */
|
||||
transform: scale(1.1);
|
||||
z-index: 1;
|
||||
min-height: 318px; /* Ensure the focused card is above others */
|
||||
}
|
||||
|
||||
/* Styles for an active (open) accordion item */
|
||||
.accordion-item.active .accordion-content {
|
||||
max-height: 500px; /* This value should be large enough to contain all content */
|
||||
padding: 15px; /* Adds padding back when open */
|
||||
border-top: 1px solid #dee2e6; /* Adds a border to separate header and content */
|
||||
}
|
||||
|
||||
.accordion-header {
|
||||
width: 100%;
|
||||
background-color: #ddf0ff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
box-shadow: none;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.service-card li {
|
||||
padding: 10px;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.service-card ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.carousel-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background-color: rgba(46, 151, 190, 0.8);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
font-size: 1.3em;
|
||||
color: #333333;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
z-index: 1;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.carousel-button:hover {
|
||||
background-color: #ea7e0b;
|
||||
.accordion-header:hover {
|
||||
background-color: #ca6e0b;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.prev-button {
|
||||
left: 10px;
|
||||
.accordion-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease;
|
||||
background-color: #ffffff; /* Ensure a solid white background */
|
||||
padding: 0 15px;
|
||||
color: #333333; /* Explicitly set text color to dark gray */
|
||||
}
|
||||
|
||||
.next-button {
|
||||
right: 10px;
|
||||
.accordion-content ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
min-height: 192px;
|
||||
}
|
||||
|
||||
.accordion-content li {
|
||||
padding: 10px;
|
||||
color: #333333; /* Explicitly set text color to dark gray */
|
||||
}
|
||||
|
||||
.hover-list {
|
||||
transition: max-height 0.4s ease-in-out, opacity 0.4s ease-in-out;
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hover-list.active {
|
||||
max-height: 300px; /* Adjust this value if your lists get longer */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/*? ↓ Start CTA Section ↓ */
|
||||
|
@ -241,7 +275,7 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|||
display: inline-block;
|
||||
padding: 8px 25px;
|
||||
margin: 20px 0px 20px 0px;
|
||||
background-color: #ea7e0b;
|
||||
background-color: #ca6e0b;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
|
@ -256,7 +290,7 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|||
}
|
||||
|
||||
.demos-link:active, .cta-button:active {
|
||||
background-color: #ea7e0b;
|
||||
background-color: #ca6e0b;
|
||||
box-shadow: 0 4px 8px rgba(46, 151, 190, 0.2);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
:root {
|
||||
/* Color Palette */
|
||||
--primary-color: #2e97be;
|
||||
--secondary-color: #ea7e0b;
|
||||
--secondary-color: #ca6e0b;
|
||||
--text-color: #495057;
|
||||
--light-bg-color: #ffffff;
|
||||
--light-gray-color: #f8f9fa;
|
||||
|
|
Loading…
Reference in New Issue