VibeCoderzVibeCoderz
Telegram
All Prompts
Interactive Quote Reveal Testimonial Section preview
testimonialquotescroll-animationtailwindinteractivemarketingsectionintersectionobserver

Interactive Quote Reveal Testimonial Section

Интерактивный блок отзывов с анимацией по словам при скролле. Аватар, бейдж доверия, темная тема. UI компонент для лендингов.

Prompt

<section class="relative z-10 max-w-7xl sm:px-6 lg:px-8 quoteRevealSection mr-auto ml-auto pt-8 pr-4 pb-20 pl-4" style="--reveal: 100%;">
  <div class="relative overflow-hidden sm:p-10 ring-white/10 ring-1 bg-neutral-900 rounded-3xl pt-6 pr-6 pb-6 pl-6">
    <!-- badge -->
    <div class="flex justify-center">
      <span class="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1.5 text-[11px] text-neutral-200">
        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" data-lucide="star" class="lucide lucide-star w-3.5 h-3.5" style="stroke-width:1.5"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" class=""></path></svg>
        TRUSTED BY CREATORS
      </span>
    </div>

    <!-- quote with word-by-word reveal -->
    <div class="relative mt-8 sm:mt-10 quoteReveal">
      <p class="mx-auto max-w-5xl text-center sm:text-4xl md:text-5xl lg:text-6xl leading-[1.1] tracking-tight">
        <span class="quote-word transition-colors duration-300" data-word-index="0" style="color: rgb(255, 255, 255);">"Vellum</span>
        <span class="quote-word transition-colors duration-300" data-word-index="1" style="color: rgb(255, 255, 255);">Edit</span>
        <span class="quote-word transition-colors duration-300" data-word-index="2" style="color: rgb(255, 255, 255);">has</span>
        <span class="quote-word transition-colors duration-300" data-word-index="3" style="color: rgb(255, 255, 255);">completely</span>
        <span class="quote-word transition-colors duration-300 font-instrument-serif italic" data-word-index="4" style="color: rgb(255, 255, 255);">transformed</span>
        <span class="quote-word transition-colors duration-300" data-word-index="5" style="color: rgb(255, 255, 255);">our</span>
        <span class="quote-word transition-colors duration-300" data-word-index="6" style="color: rgb(255, 255, 255);">workflow.</span>
        <span class="quote-word transition-colors duration-300" data-word-index="7" style="color: rgb(255, 255, 255);">The</span>
        <span class="quote-word transition-colors duration-300" data-word-index="8" style="color: rgb(255, 255, 255);">AI-powered</span>
        <span class="quote-word transition-colors duration-300" data-word-index="9" style="color: rgb(255, 255, 255);">tools</span>
        <span class="quote-word transition-colors duration-300" data-word-index="10" style="color: rgb(255, 255, 255);">and</span>
        <span class="quote-word transition-colors duration-300" data-word-index="11" style="color: rgb(255, 255, 255);">real-time</span>
        <span class="quote-word transition-colors duration-300" data-word-index="12" style="color: rgb(255, 255, 255);">performance</span>
        <span class="quote-word transition-colors duration-300" data-word-index="13" style="color: rgb(255, 255, 255);">make</span>
        <span class="quote-word transition-colors duration-300" data-word-index="14" style="color: rgb(255, 255, 255);">professional</span>
        <span class="quote-word transition-colors duration-300" data-word-index="15" style="color: rgb(255, 255, 255);">editing</span>
        <span class="quote-word transition-colors duration-300 font-instrument-serif italic" data-word-index="16" style="color: rgb(255, 255, 255);">accessible</span>
        <span class="quote-word transition-colors duration-300" data-word-index="17" style="color: rgb(115, 115, 115);">to</span>
        <span class="quote-word transition-colors duration-300" data-word-index="18" style="color: rgb(115, 115, 115);">everyone."</span>
      </p>
    </div>

    <!-- author -->
    <div class="sm:mt-10 flex gap-3 mt-8 items-center justify-center">
      <img alt="Author avatar" class="h-10 w-10 ring-1 ring-white/10 object-cover rounded-full" src="https://hoirqrkdgbmvpwutwuwj-all.supabase.co/storage/v1/object/public/assets/assets/05d02a28-c159-471f-8c4d-f4df12e74bdf_320w.jpg">
      <span class="text-sm sm:text-base text-neutral-300 font-medium">Creative Director &amp; Editor</span>
    </div>

    <!-- subtle glow -->
    
  </div>

  <script>
    (function () {
      const section = document.querySelector('.quoteRevealSection');
      const target = section ? section.querySelector('.quoteReveal') : null;
      const words = section ? section.querySelectorAll('.quote-word') : [];
      
      if (!section || !target || words.length === 0) return;

      // Initialize all words as gray
      words.forEach(word => {
        word.style.color = 'rgb(115 115 115)'; // text-neutral-500
      });

      function updateWordReveal() {
        const rect = target.getBoundingClientRect();
        const vh = window.innerHeight || document.documentElement.clientHeight;
        const viewportCenter = vh / 2;
        
        // Calculate distance from element center to viewport center
        const elementCenter = rect.top + rect.height / 2;
        const distanceFromCenter = Math.abs(elementCenter - viewportCenter);
        const maxDistance = vh / 2;
        
        // Create a ratio where 0 = element is at viewport center, 1 = element is at edge
        const centerRatio = Math.max(0, Math.min(1, 1 - (distanceFromCenter / maxDistance)));
        
        // Calculate how many words to reveal based on center proximity
        const totalWords = words.length;
        const wordsToReveal = Math.floor(centerRatio * totalWords);
        
        // Update word colors
        words.forEach((word, index) => {
          if (index < wordsToReveal) {
            word.style.color = 'rgb(255 255 255)'; // text-white
          } else {
            word.style.color = 'rgb(115 115 115)'; // text-neutral-500
          }
        });
      }

      if (typeof IntersectionObserver !== 'undefined') {
        const io = new IntersectionObserver(updateWordReveal, { 
          threshold: Array.from({ length: 101 }, (_, i) => i / 100) 
        });
        io.observe(target);
      }

      window.addEventListener('scroll', updateWordReveal, { passive: true });
      window.addEventListener('resize', updateWordReveal);
      updateWordReveal();
    })();
  </script>
</section>
All Prompts