From 31eb93e01b7187e334acc95b39ab16d5290d0d10 Mon Sep 17 00:00:00 2001 From: hobokenchicken Date: Wed, 11 Feb 2026 20:34:03 -0500 Subject: [PATCH] feat(site): add engaging interactions (typewriter, progress, skills, particles, testimonials) Ultraworked with Sisyphus --- index.html | 108 +++++++++++++++++++---------- main.js | 164 ++++++++++++++++++++++++++++++++++++++++++++ styles.css | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 431 insertions(+), 38 deletions(-) create mode 100644 main.js diff --git a/index.html b/index.html index 8257bb4..283aed8 100644 --- a/index.html +++ b/index.html @@ -42,26 +42,58 @@ scroll-behavior: smooth; } - - + + + + +
+ + + +

d@n tech

-

Your partner in web development

-

Visit my main site

-
+

+

Visit my main site

+

About Us

Welcome to d@n tech! We specialize in creating stunning and efficient websites for businesses of all sizes. Our team of experienced developers is dedicated to delivering high-quality web solutions that meet your unique needs.

-

Services

-
    -
  • Custom Website Development
  • -
  • Responsive Design
  • -
  • E-commerce Solutions
  • -
  • SEO Optimization
  • -
  • Website Maintenance
  • -
+

Services

+
    +
  • + Custom Website Development +
    +
    +
    +
  • +
  • + Responsive Design +
    +
    +
    +
  • +
  • + E-commerce Solutions +
    +
    +
    +
  • +
  • + SEO Optimization +
    +
    +
    +
  • +
  • + Website Maintenance +
    +
    +
    +
  • +
@@ -121,29 +153,33 @@
-
-

Testimonials

-
-
-
-
-

"It was a pleasure working with Dustin on my counseling practice website. Throughout the process, he was thoughtful, responsive, and truly understood my vision. In addition to bringing my ideas to life with a clean, professional design, he made the entire process enjoyable and easy. His attention to detail, creativity, and technical skills are excellent. As a result of Dustin's work, I now have a website that truly represents my practice. There is no better person to recommend than him!"

-

- Margaret Pemu, Pemu Counseling and Wellness

-
-
-
-
-
-
-

"Working with Dustin at d@n tech was an absolute pleasure from start to finish. He is funny, engaging, and listens well. He offered grace for my lack of technical skill and worked from simple examples provided, which translated into a functional and funky-professional website that represents my brand. - - His patience and technical skills are truly impressive. Furthermore, he was responsive throughout the entire process, always open to feedback, and quick to implement changes. What I appreciated most was how he spent time working toward what I wanted and was supportive in my goals."

-

-Laura, Woman-Owned Small Business

-
-
-
-
-
+
+

Testimonials

+
+
+
+
+

"It was a pleasure working with Dustin on my counseling practice website. Throughout the process, he was thoughtful, responsive, and truly understood my vision..."

+

"It was a pleasure working with Dustin on my counseling practice website. Throughout the process, he was thoughtful, responsive, and truly understood my vision. In addition to bringing my ideas to life with a clean, professional design, he made the entire process enjoyable and easy. His attention to detail, creativity, and technical skills are excellent. As a result of Dustin's work, I now have a website that truly represents my practice. There is no better person to recommend than him!"

+

- Margaret Pemu, Pemu Counseling and Wellness

+ +
+
+
+
+
+
+

"Working with Dustin at d@n tech was an absolute pleasure from start to finish. He is funny, engaging, and listens well. He offered grace for my lack of technical skill..."

+

"Working with Dustin at d@n tech was an absolute pleasure from start to finish. He is funny, engaging, and listens well. He offered grace for my lack of technical skill and worked from simple examples provided, which translated into a functional and funky-professional website that represents my brand. + + His patience and technical skills are truly impressive. Furthermore, he was responsive throughout the entire process, always open to feedback, and quick to implement changes. What I appreciated most was how he spent time working toward what I wanted and was supportive in my goals."

+

-Laura, Woman-Owned Small Business

+ +
+
+
+
+
diff --git a/main.js b/main.js new file mode 100644 index 0000000..1917ba1 --- /dev/null +++ b/main.js @@ -0,0 +1,164 @@ +// ===== TYPEWRITER EFFECT ===== +function initTypewriter() { + const el = document.querySelector('.typewriter'); + if (!el) return; + + const text = el.getAttribute('data-text'); + let i = 0; + + el.textContent = ''; + el.style.opacity = '1'; + + function type() { + if (i < text.length) { + el.textContent += text.charAt(i); + i++; + setTimeout(type, 80); + } + } + + setTimeout(type, 500); +} + +// ===== SCROLL PROGRESS BAR ===== +function initScrollProgress() { + const progressBar = document.querySelector('.scroll-progress'); + if (!progressBar) return; + + function updateProgress() { + const docHeight = document.documentElement.scrollHeight - window.innerHeight; + const scrolled = window.scrollY; + const progress = (scrolled / docHeight) * 100; + + progressBar.style.setProperty('--scroll-progress', `${progress}%`); + progressBar.setAttribute('aria-valuenow', Math.round(progress)); + } + + window.addEventListener('scroll', updateProgress, { passive: true }); + updateProgress(); +} + +// ===== ANIMATED SKILLS BARS ===== +function initSkillBars() { + const skillItems = document.querySelectorAll('.skill-item'); + if (!skillItems.length) return; + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('animate'); + observer.unobserve(entry.target); + } + }); + }, { threshold: 0.3 }); + + skillItems.forEach(item => observer.observe(item)); +} + +// ===== PARTICLE BACKGROUND ===== +function initParticles() { + const canvas = document.getElementById('particles'); + if (!canvas) return; + + const ctx = canvas.getContext('2d'); + let particles = []; + let animationId; + + function resize() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + } + + class Particle { + constructor() { + this.x = Math.random() * canvas.width; + this.y = Math.random() * canvas.height; + this.vx = (Math.random() - 0.5) * 0.3; + this.vy = (Math.random() - 0.5) * 0.3; + this.radius = Math.random() * 1.5 + 0.5; + this.opacity = Math.random() * 0.3 + 0.1; + } + + update() { + this.x += this.vx; + this.y += this.vy; + + if (this.x < 0 || this.x > canvas.width) this.vx *= -1; + if (this.y < 0 || this.y > canvas.height) this.vy *= -1; + } + + draw() { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); + ctx.fillStyle = `rgba(111, 78, 55, ${this.opacity})`; + ctx.fill(); + } + } + + function init() { + resize(); + particles = []; + const particleCount = Math.floor((canvas.width * canvas.height) / 15000); + for (let i = 0; i < particleCount; i++) { + particles.push(new Particle()); + } + } + + function animate() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + particles.forEach(particle => { + particle.update(); + particle.draw(); + }); + + animationId = requestAnimationFrame(animate); + } + + window.addEventListener('resize', () => { + cancelAnimationFrame(animationId); + init(); + animate(); + }); + + init(); + animate(); +} + +// ===== INTERACTIVE TESTIMONIALS ===== +function initTestimonials() { + const testimonialCards = document.querySelectorAll('.testimonial-card'); + + testimonialCards.forEach(card => { + const toggle = card.querySelector('.testimonial-toggle'); + const excerpt = card.querySelector('.testimonial-excerpt'); + const full = card.querySelector('.testimonial-full'); + + if (!toggle || !excerpt || !full) return; + + toggle.addEventListener('click', () => { + const isExpanded = card.classList.contains('expanded'); + + card.classList.toggle('expanded'); + toggle.textContent = isExpanded ? 'Read More' : 'Read Less'; + toggle.setAttribute('aria-expanded', !isExpanded); + + if (!isExpanded) { + excerpt.style.display = 'none'; + full.style.display = 'block'; + } else { + excerpt.style.display = 'block'; + full.style.display = 'none'; + } + }); + }); +} + +// ===== INITIALIZE ALL ===== +document.addEventListener('DOMContentLoaded', () => { + initTypewriter(); + initScrollProgress(); + initSkillBars(); + initParticles(); + initTestimonials(); +}); diff --git a/styles.css b/styles.css index f24ad0e..1c3e4bc 100644 --- a/styles.css +++ b/styles.css @@ -219,16 +219,209 @@ footer { .btn-primary { background-color: var(--color-primary); border-color: var(--color-primary); - color: white; /* Electric blue needs white text for contrast */ + color: white; font-weight: 700; text-transform: uppercase; font-size: 0.85rem; letter-spacing: 0.05em; padding: 0.6em 1.2em; - border-radius: 50px; /* Pill shape */ + border-radius: 50px; &:hover, &:focus, &:active { background-color: color-mix(in srgb, var(--color-primary), black 10%) !important; border-color: color-mix(in srgb, var(--color-primary), black 10%) !important; } } + +/* ===== SCROLL PROGRESS BAR ===== */ +.scroll-progress { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 4px; + background: linear-gradient( + to right, + var(--color-primary) 0%, + var(--color-primary) var(--scroll-progress, 0%), + transparent var(--scroll-progress, 0%) + ); + z-index: 9999; + transition: none; +} + +/* ===== PARTICLE CANVAS ===== */ +#particles { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: -1; + opacity: 0.6; +} + +/* ===== TYPEWRITER EFFECT ===== */ +.typewriter { + opacity: 0; + transition: opacity 0.3s; + position: relative; + display: inline-block; +} + +.typewriter::after { + content: '|'; + position: absolute; + right: -8px; + animation: blink 0.7s infinite; + color: var(--color-secondary); +} + +@keyframes blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } +} + +/* ===== ANIMATED SKILL BARS ===== */ +.skill-bars { + --bs-list-group-bg: transparent; +} + +.skill-item { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 1.25rem; + border-left: 4px solid transparent; + transition: border-color 0.3s, background-color 0.3s; +} + +.skill-item:hover { + border-left-color: var(--color-secondary); +} + +.skill-name { + font-weight: 600; + font-size: 1rem; + color: var(--text-body); +} + +.skill-bar-track { + width: 100%; + height: 8px; + background: color-mix(in srgb, var(--color-coffee-medium), transparent 85%); + border-radius: 50px; + overflow: hidden; + position: relative; +} + +.skill-bar-fill { + height: 100%; + width: 0; + background: linear-gradient(90deg, var(--color-primary), var(--color-secondary)); + border-radius: 50px; + transition: width 1.5s cubic-bezier(0.65, 0, 0.35, 1); + position: relative; + overflow: hidden; +} + +.skill-bar-fill::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.3), + transparent + ); + animation: shimmer 2s infinite; +} + +@keyframes shimmer { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } +} + +.skill-item.animate .skill-bar-fill { + width: var(--skill-level); +} + +/* ===== INTERACTIVE TESTIMONIALS ===== */ +.testimonial-card { + position: relative; + overflow: visible; +} + +.testimonial-full { + display: none; +} + +.testimonial-excerpt { + display: block; +} + +.testimonial-card.expanded .testimonial-excerpt { + display: none; +} + +.testimonial-card.expanded .testimonial-full { + display: block; +} + +.testimonial-author { + font-weight: 600; + font-style: italic; + color: var(--color-coffee-medium); + margin-top: 1rem; + margin-bottom: 0; +} + +.testimonial-toggle { + margin-top: 1rem; + background: var(--color-secondary); + color: var(--color-coffee-dark); + border: none; + padding: 0.5rem 1.5rem; + border-radius: 50px; + font-weight: 700; + font-size: 0.85rem; + cursor: pointer; + text-transform: uppercase; + letter-spacing: 0.05em; + transition: transform 0.2s, background-color 0.2s; +} + +.testimonial-toggle:hover { + background: var(--color-primary); + color: white; + transform: translateY(-2px); +} + +.testimonial-toggle:active { + transform: translateY(0); +} + +/* ===== REDUCED MOTION SUPPORT ===== */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + + .typewriter::after { + animation: none; + } + + .skill-bar-fill::after { + animation: none; + } +}