Skip to content
jswnthui

Section

Philosophy Section

The anti-jank promise: a counter that runs to 60fps and a smooth curve that draws itself beside a crossed-out jagged one.

The Anti-Jank Promise
0fps

Not a tagline — a promise, held on every scroll trigger and every hover state. If an animation drops a frame on a mid-range phone, it doesn't ship.

Dropped Frames
Kinematic Studio
Zero Dropped FramesSub-Second LoadsEvery Interaction Profiled

The counter runs to 60 and the smooth line draws in the first time it comes into view

Installation

  1. 1. Install the dependencies.

    terminal
    npm install gsap
  2. 2. Add the cn helper, if you don't already have it.

    lib/utils.ts
    import { clsx, type ClassValue } from "clsx";
    import { twMerge } from "tailwind-merge";
    
    export function cn(...inputs: ClassValue[]) {
      return twMerge(clsx(inputs));
    }
  3. 3. Copy the component into your project.

    components/ui/philosophy-section.tsx
    'use client'
    
    import { useEffect, useRef } from 'react'
    import gsap from 'gsap'
    
    const PHILOSOPHY_BADGES = ['Zero Dropped Frames', 'Sub-Second Loads', 'Every Interaction Profiled']
    
    // Philosophy callout — anti-jank / 60fps as a designed statement.
    export default function PhilosophySection() {
      const sectionRef = useRef<HTMLDivElement>(null)
      const counterRef = useRef<HTMLSpanElement>(null)
      const smoothPathRef = useRef<SVGPathElement>(null)
      const hasPlayedRef = useRef(false)
    
      useEffect(() => {
        const section = sectionRef.current
        const counter = counterRef.current
        const smoothPath = smoothPathRef.current
        if (!section || !counter || !smoothPath) return
    
        const total = smoothPath.getTotalLength()
        smoothPath.style.strokeDasharray = `${total}`
        smoothPath.style.strokeDashoffset = `${total}`
    
        const observer = new IntersectionObserver(
          ([entry]) => {
            if (entry.isIntersecting && !hasPlayedRef.current) {
              hasPlayedRef.current = true
              const proxy = { value: 0 }
              gsap.to(proxy, {
                value: 60,
                duration: 1.4,
                ease: 'power2.out',
                onUpdate: () => {
                  counter.textContent = String(Math.round(proxy.value))
                },
              })
              gsap.to(smoothPath, { strokeDashoffset: 0, duration: 1.4, ease: 'power2.out' })
            }
          },
          { threshold: 0.4 }
        )
    
        observer.observe(section)
        return () => observer.disconnect()
      }, [])
    
      return (
        <section ref={sectionRef} className="border-t border-white/10 bg-kine-bg px-6 py-24 md:px-12 md:py-40">
          <div className="mx-auto flex max-w-4xl flex-col items-center gap-10 text-center">
            <span className="font-mono text-xs font-bold uppercase tracking-widest text-kine-primary">
              The Anti-Jank Promise
            </span>
    
            <div className="flex items-baseline gap-2">
              <span
                ref={counterRef}
                className="font-kine-display text-8xl leading-none text-kine-primary md:text-[11rem]"
              >
                0
              </span>
              <span className="font-kine-display text-4xl uppercase leading-none text-kine-accent md:text-6xl">fps</span>
            </div>
    
            <p className="max-w-2xl font-kine-body text-lg leading-relaxed text-kine-accent/70 md:text-xl">
              Not a tagline — a promise, held on every scroll trigger and every hover state. If an animation drops a
              frame on a mid-range phone, it doesn't ship.
            </p>
    
            {/* Smooth curve vs. a crossed-out jagged line — the promise, drawn. */}
            <div className="flex w-full max-w-md flex-col gap-6">
              <div className="flex items-center gap-4 opacity-40">
                <svg viewBox="0 0 200 40" className="h-8 flex-1" fill="none">
                  <path
                    d="M4 20 L30 6 L50 34 L70 6 L90 34 L110 6 L130 34 L150 6 L170 34 L196 20"
                    stroke="#e2e8f0"
                    strokeWidth={2}
                    strokeLinecap="round"
                    strokeLinejoin="round"
                  />
                  <line x1="0" y1="0" x2="200" y2="40" stroke="#e2e8f0" strokeWidth={1.5} />
                </svg>
                <span className="font-mono text-xs uppercase tracking-widest text-kine-accent/50">Dropped Frames</span>
              </div>
    
              <div className="flex items-center gap-4">
                <svg viewBox="0 0 200 40" className="h-8 flex-1" fill="none">
                  <path
                    ref={smoothPathRef}
                    d="M4 30 C 40 30, 40 10, 76 10 S 112 30, 148 30 S 176 14, 196 14"
                    stroke="#ceff00"
                    strokeWidth={3}
                    strokeLinecap="round"
                  />
                </svg>
                <span className="font-mono text-xs uppercase tracking-widest text-kine-primary">Kinematic Studio</span>
              </div>
            </div>
    
            <div className="mt-4 flex flex-wrap items-center justify-center gap-3">
              {PHILOSOPHY_BADGES.map((badge) => (
                <span
                  key={badge}
                  className="rounded-full border border-kine-primary/30 bg-kine-primary/5 px-5 py-2 font-mono text-xs font-bold uppercase tracking-widest text-kine-primary"
                >
                  {badge}
                </span>
              ))}
            </div>
          </div>
        </section>
      )
    }
    styles/kinematics.css
    /* Kinematic Studio's palette and type, for the components ported from its
       site. Namespaced as kine-* so they sit beside this library's own tokens —
       Kinematics calls its off-white "accent", which here is already the blue.
       Import this from the stylesheet that imports Tailwind, so the @theme
       block turns into utilities: bg-kine-bg, text-kine-primary/70,
       font-kine-display and so on. */
    @theme {
      --color-kine-bg: #0f1113;
      --color-kine-primary: #ceff00;
      --color-kine-surface: #2b2e34;
      --color-kine-accent: #e2e8f0;
    
      /* Audiowide for headlines, Geo for body copy. Geo breaks up at small
         sizes, so tiny labels use the plain sans instead. */
      --font-kine-display: "Audiowide", ui-sans-serif, system-ui, sans-serif;
      --font-kine-body: "Geo", ui-sans-serif, system-ui, sans-serif;
    
      /* Morni, the restaurant client in the Emoji Cursor demo. */
      --font-morni-brand: "Modak", ui-sans-serif, system-ui, sans-serif;
      --font-morni-body: "Poppins", ui-sans-serif, system-ui, sans-serif;
    }
    
    /* Manifesto emoji markers — the box grows from square to rectangle as scroll
       reaches it; once mostly grown, the emoji starts rolling, paused until then
       via animation-play-state set inline. */
    @keyframes manifesto-emoji-roll {
      to {
        transform: rotate(360deg);
      }
    }
    
    /* Expertise cards — small, always-looping visuals tying each card's
       animation to its own subject (motion, layout, load speed, ranking,
       language). CSS keyframes rather than GSAP, since they run for as long as
       the card is mounted. */
    @keyframes expertise-dash {
      to {
        stroke-dashoffset: -24;
      }
    }
    .expertise-flow-line {
      stroke-dasharray: 6 6;
      animation: expertise-dash 1s linear infinite;
    }
    
    @keyframes expertise-block-a {
      0%, 100% { width: 85%; }
      50% { width: 45%; }
    }
    @keyframes expertise-block-b {
      0%, 100% { width: 55%; }
      50% { width: 90%; }
    }
    @keyframes expertise-block-c {
      0%, 100% { width: 70%; }
      50% { width: 30%; }
    }
    .expertise-block-a { animation: expertise-block-a 3.2s ease-in-out infinite; }
    .expertise-block-b { animation: expertise-block-b 3.2s ease-in-out infinite 0.3s; }
    .expertise-block-c { animation: expertise-block-c 3.2s ease-in-out infinite 0.6s; }
    
    @keyframes expertise-fill {
      0% { width: 6%; opacity: 0.5; }
      70% { width: 100%; opacity: 1; }
      85% { width: 100%; opacity: 1; }
      100% { width: 6%; opacity: 0.5; }
    }
    .expertise-fill-bar {
      animation: expertise-fill 2.2s cubic-bezier(0.65, 0, 0.35, 1) infinite;
    }
    
    @keyframes expertise-rank-bar-1 { 0%, 100% { height: 30%; } 50% { height: 80%; } }
    @keyframes expertise-rank-bar-2 { 0%, 100% { height: 55%; } 50% { height: 25%; } }
    @keyframes expertise-rank-bar-3 { 0%, 100% { height: 40%; } 50% { height: 95%; } }
    @keyframes expertise-rank-bar-4 { 0%, 100% { height: 70%; } 50% { height: 50%; } }
    .expertise-rank-1 { animation: expertise-rank-bar-1 2.4s ease-in-out infinite; }
    .expertise-rank-2 { animation: expertise-rank-bar-2 2.4s ease-in-out infinite 0.15s; }
    .expertise-rank-3 { animation: expertise-rank-bar-3 2.4s ease-in-out infinite 0.3s; }
    .expertise-rank-4 { animation: expertise-rank-bar-4 2.4s ease-in-out infinite 0.45s; }
    
    @keyframes expertise-lang-cross {
      0%, 45% { opacity: 1; transform: translateY(0); }
      50%, 95% { opacity: 0; transform: translateY(-6px); }
      100% { opacity: 1; transform: translateY(0); }
    }
    @keyframes expertise-lang-cross-alt {
      0%, 45% { opacity: 0; transform: translateY(6px); }
      50%, 95% { opacity: 1; transform: translateY(0); }
      100% { opacity: 0; transform: translateY(6px); }
    }
    .expertise-lang-a { animation: expertise-lang-cross 3.6s ease-in-out infinite; }
    .expertise-lang-b { animation: expertise-lang-cross-alt 3.6s ease-in-out infinite; }

Props

PropTypeDefaultDescription