VibeCoderzVibeCoderz
Telegram
All Prompts
Science of Posture Product Feature Section preview
featuresectioninteractiveanimatedwebglgallerytailwind

Science of Posture Product Feature Section

Секция с интерактивной галереей WebGL для демонстрации мебели, улучшающей осанку. Адаптивный дизайн с Tailwind CSS.

Prompt

<section id="science-of-posture" class="py-32 relative overflow-hidden"
  style="background-color: #F2EFEA; color: #2C2824;">
  <script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>

  <style>
    @keyframes sonar {
      0% {
        transform: scale(1);
        opacity: 0.8;
      }

      100% {
        transform: scale(2.5);
        opacity: 0;
      }
    }

    .sonar-ring {
      position: absolute;
      inset: 0;
      border-radius: 50%;
      border: 1px solid currentColor;
      animation: sonar 2s cubic-bezier(0, 0, 0.2, 1) infinite;
      pointer-events: none;
    }

    .card-flashlight {
      position: relative;
      border-radius: 1rem;
      background: rgba(255, 255, 255, 0.4);
      backdrop-filter: blur(12px);
      border: 1px solid rgba(0, 0, 0, 0.05);
      overflow: hidden;
      transition: transform 0.3s ease;
    }

    .card-flashlight::before {
      content: "";
      position: absolute;
      inset: 0;
      background: radial-gradient(600px circle at var(--mouse-x, 0) var(--mouse-y, 0), rgba(255, 255, 255, 0.8), transparent 40%);
      z-index: 0;
      opacity: 0;
      transition: opacity 0.4s ease;
      pointer-events: none;
    }

    .card-flashlight:hover::before {
      opacity: 1;
    }

    .card-content {
      position: relative;
      z-index: 2;
    }
  </style>

  <div class="max-w-[88rem] mx-auto px-6 lg:px-12">
    <!-- Header -->
    <div class="flex flex-col md:flex-row justify-between items-start md:items-end mb-24 gap-8">
      <h2 class="text-5xl md:text-6xl tracking-tighter font-light"
        style="font-family: 'Plus Jakarta Sans', sans-serif;">
        The Science of<br>Posture
      </h2>
      <a href="#"
        class="inline-flex items-center gap-2 bg-[#2C2824] text-[#F2EFEA] px-6 py-3 rounded-full text-sm font-medium transition-transform hover:scale-105"
        style="font-family: 'Geist', sans-serif;">
        <span>Explore Specifications</span>
        <iconify-icon icon="solar:arrow-right-up-linear"></iconify-icon>
      </a>
    </div>

    <div class="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-8 items-start">
      <!-- Description -->
      <div class="lg:col-span-4 max-w-sm">
        <p class="text-sm font-medium tracking-tight opacity-50 mb-4 uppercase"
          style="font-family: 'Geist', sans-serif;">( Method )</p>
        <p class="text-xl leading-relaxed opacity-90 mb-8 font-light"
          style="font-family: 'Plus Jakarta Sans', sans-serif;">
          Every structural decision serves a biomechanical purpose. We strip away the superfluous, leaving only what
          actively contributes to your physical wellbeing. Materials are tested for dynamic tension, ensuring longevity
          without aesthetic compromise.
        </p>
        <div class="relative inline-flex text-[#C48C56] mt-4">
          <iconify-icon icon="solar:asterisk-bold-duotone" class="text-3xl"></iconify-icon>
          <div class="sonar-ring" style="animation-delay: 1s"></div>
        </div>
      </div>

      <!-- Interactive Gallery -->
      <div class="lg:col-span-8 grid grid-cols-1 md:grid-cols-2 gap-8 lg:gap-12 items-end">
        <!-- Card 1 -->
        <div class="card-flashlight p-6 pb-8 flex flex-col group cursor-pointer">
          <div class="card-content">
            <div class="aspect-square rounded-lg overflow-hidden bg-black/5 mb-6 relative">
              <img src="https://images.unsplash.com/photo-1592078615290-033ee584e267?q=80&w=1000&auto=format&fit=crop"
                   alt="Lounge Chair"
                   class="webgl-reveal w-full h-full object-cover mix-blend-multiply transition-transform duration-700 group-hover:scale-110">
            </div>
            <div class="flex justify-between items-end text-sm" style="font-family: 'Geist', sans-serif;">
              <div>
                <span class="opacity-50 text-xs block mb-1">01</span>
                <p class="font-medium tracking-tight">Kinetic Lounge V1</p>
              </div>
              <span class="opacity-60">240 Units</span>
            </div>
          </div>
        </div>

        <!-- Card 2 -->
        <div class="card-flashlight p-6 pb-8 flex flex-col group cursor-pointer lg:-mt-24">
          <div class="card-content">
            <div class="aspect-[4/5] rounded-lg overflow-hidden bg-black/5 mb-6 relative">
              <img src="https://images.unsplash.com/photo-1567538096630-e0c55bd6374c?q=80&w=1000&auto=format&fit=crop"
                   alt="Accent Chair"
                   class="webgl-reveal w-full h-full object-cover mix-blend-multiply transition-transform duration-700 group-hover:scale-110">
            </div>
            <div class="flex justify-between items-end text-sm" style="font-family: 'Geist', sans-serif;">
              <div>
                <span class="opacity-50 text-xs block mb-1">02</span>
                <p class="font-medium tracking-tight">Architectural Accent</p>
              </div>
              <span class="opacity-60">850+ Units</span>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    (function() {
      // Flashlight Logic
      const section = document.getElementById('science-of-posture');
      const cards = section.querySelectorAll('.card-flashlight');
      cards.forEach(card => {
        card.addEventListener('mousemove', e => {
          const rect = card.getBoundingClientRect();
          card.style.setProperty('--mouse-x', `${e.clientX - rect.left}px`);
          card.style.setProperty('--mouse-y', `${e.clientY - rect.top}px`);
        });
      });

      // WebGL Reveal Logic
      const vs = `attribute vec2 p;varying vec2 u;void main(){u=p*0.5+0.5;gl_Position=vec4(p,0,1);}`;
      const fs = `precision highp float;varying vec2 u;uniform sampler2D t;uniform float g;void main(){float c=4.0;float i=floor(u.x*c);float d=i*0.15;float p=clamp((g*(1.0+3.0*0.15)-d),0.0,1.0);float e=pow(p-1.0,3.0)+1.0;if(u.y<1.0-e)discard;gl_FragColor=texture2D(t,u);}`;

      function initWebGL(el) {
        const img = new Image();
        img.crossOrigin = "anonymous";
        img.onload = () => {
          const canv = document.createElement('canvas');
          canv.width = img.naturalWidth; canv.height = img.naturalHeight;
          canv.className = el.className; canv.style.cssText = el.style.cssText;
          el.parentNode.insertBefore(canv, el); el.style.display = 'none';
          const gl = canv.getContext('webgl', { alpha: true });
          const s = (t, src) => { const sh = gl.createShader(t); gl.shaderSource(sh, src); gl.compileShader(sh); return sh; };
          const prog = gl.createProgram();
          gl.attachShader(prog, s(gl.VERTEX_SHADER, vs)); gl.attachShader(prog, s(gl.FRAGMENT_SHADER, fs));
          gl.linkProgram(prog); gl.useProgram(prog);
          const buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf);
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]), gl.STATIC_DRAW);
          const loc = gl.getAttribLocation(prog, "p"); gl.enableVertexAttribArray(loc);
          gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
          const tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex);
          gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
          const uG = gl.getUniformLocation(prog, "g");
          let start = null;
          const draw = (now) => {
            if (!start) start = now;
            let progVal = (now - start) / 1200;
            if (progVal > 1) progVal = 1;
            gl.clear(gl.COLOR_BUFFER_BIT); gl.uniform1f(uG, progVal);
            gl.drawArrays(gl.TRIANGLES, 0, 6);
            if (progVal < 1) requestAnimationFrame(draw);
          };
          new IntersectionObserver(ents => {
            if (ents[0].isIntersecting) requestAnimationFrame(draw);
          }, { threshold: 0.1 }).observe(canv);
        };
        img.src = el.src;
      }
      section.querySelectorAll('.webgl-reveal').forEach(initWebGL);
    })();
  </script>
</section>
All Prompts