VibeCoderzVibeCoderz
Telegram
All Prompts
Animated Hair Serum Stats Comparison Section preview
featuresectionanimatedresponsivetailwindstatscomparison

Animated Hair Serum Stats Comparison Section

Секция сравнения характеристик сывороток для волос с анимированной статистикой. Использует Tailwind CSS и IntersectionObserver для отображения данных при прокрутке.

Prompt

<section class="md:px-12 bg-white border-neutral-100 border-b pt-32 pr-6 pb-32 pl-6">
  <div class="max-w-7xl mx-auto grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-24">
    <!-- Left Title -->
    <div class="lg:col-span-4">
      <h2 class="text-4xl md:text-5xl font-medium tracking-tight text-neutral-900 leading-[1.1]">
        What makesit special
      </h2>
      <!-- Orange Tag (Mobile Only) -->
      <div class="mt-8 lg:hidden text-xs font-semibold text-orange-600 uppercase tracking-widest">
        [ IN-VITRO STUDIES ]
      </div>
    </div>

    <!-- Right Content -->
    <div class="lg:col-span-8 flex flex-col justify-between h-full">
      <div class="">
        <!-- Big Number -->
        <div class="text-[12vw] lg:text-[10rem] font-medium leading-none text-neutral-100 select-none -ml-2 mb-6 flex">
          <span id="animated-stat-number" class="">0.0</span>%
        </div>

        <h3 class="text-xl font-medium tracking-tight text-neutral-900 mb-4">
          Revolutionary Patent Pending Blend
        </h3>
        <p class="text-neutral-500 max-w-lg leading-relaxed text-sm mb-16">
          Our serum delivers a remarkable 22.9% increase in the tensile strength of hair fibers after just one wash,
          surpassing competitors' results.
        </p>
      </div>

      <!-- Chart Area -->
      <div class="w-full relative">
        <!-- Label -->
        <div
          class="absolute bottom-4 left-0 text-xs font-semibold text-orange-600 uppercase tracking-widest hidden lg:block">
          [ IN-VITRO STUDIES ]
        </div>

        <!-- Bars -->
        <div class="w-full max-w-xl ml-auto">
          <!-- Bar 1 -->
          <div class="mb-8">
            <div class="text-xs text-neutral-500 mb-2 font-medium">Usual Serum</div>
            <div class="w-full bg-neutral-100 h-8 relative rounded-sm">
              <!-- Dashed line marker effect -->
              <div class="absolute right-0 top-[-4px] bottom-[-4px] border-r border-dashed border-neutral-300"></div>
            </div>
          </div>

          <!-- Bar 2 -->
          <div class="">
            <div class="flex justify-between items-end mb-2">
              <span class="text-xs text-neutral-900 font-medium">Minimalist Serum</span>
              <span class="text-sm text-orange-600 font-semibold">+22.9%</span>
            </div>
            <!-- Animated Bar -->
            <div id="animated-stat-bar" class="bg-orange-600 h-8 rounded-sm relative shadow-lg shadow-orange-600/20"
              style="width: 100%; transition: width 1.5s cubic-bezier(0.25, 1, 0.5, 1);">
              <!-- Dashed line marker effect matching the one above -->
              <div class="absolute right-0 top-[-4px] bottom-[-4px] border-r border-dashed border-neutral-900/20"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Animation Script -->
  <script>
    (function () {
      // Grab the section that contains THIS script
      const script = document.currentScript;
      const section = script.closest('section');
      if (!section) return;

      const numberEl = section.querySelector('#animated-stat-number');
      const barEl = section.querySelector('#animated-stat-bar');
      if (!numberEl || !barEl) return;

      const observer = new IntersectionObserver(
        (entries) => {
          entries.forEach((entry) => {
            if (!entry.isIntersecting) return;

            // Animate bar width
            setTimeout(() => {
              barEl.style.width = '100%';
            }, 100);

            // Animate number from 0.0 to 22.9
            const targetValue = 22.9;
            const duration = 1500;
            const startTime = performance.now();

            function updateNumber(currentTime) {
              const elapsed = currentTime - startTime;
              const progress = Math.min(elapsed / duration, 1);
              const ease = 1 - Math.pow(1 - progress, 4);
              const current = progress === 1 ? targetValue : targetValue * ease;

              numberEl.textContent = current.toFixed(1);

              if (progress < 1) {
                requestAnimationFrame(updateNumber);
              }
            }

            requestAnimationFrame(updateNumber);

            // Only run once
            observer.unobserve(entry.target);
          });
        },
        { threshold: 0.3 }
      );

      observer.observe(section);
    })();
  </script>
</section>
All Prompts