Skip to content
back to lab

Neobrutalist button

The site's press-down button pattern, rebuilt from scratch: shadow sinks, border stays, gsap wiggles the icon.

preview
sketch.tsx· read-onlyedit
function Sketch() {
  const ref = useRef(null)
  const [count, setCount] = useState(0)

  useEffect(() => {
    if (count === 0) return
    const ctx = gsap.context(() => {
      gsap.fromTo(
        ".star",
        { rotation: -20, scale: 0.6 },
        { rotation: 0, scale: 1, ease: EASE.jelly, duration: 0.8 }
      )
    }, ref)
    return () => ctx.revert()
  }, [count])

  return (
    <div ref={ref} className="flex h-full min-h-56 flex-col items-center justify-center gap-5">
      <button
        type="button"
        onClick={() => setCount((c) => c + 1)}
        className="flex items-center gap-2 rounded-sm border-2 border-border bg-accent-cyan px-5 py-2.5 font-nav text-sm font-bold text-fixed-dark shadow-hard transition-all hover:shadow-hard-sm hover:translate-x-[1px] hover:translate-y-[1px] active:translate-x-[2px] active:translate-y-[2px] active:shadow-none"
      >
        <span className="star inline-flex">
          <Star size={16} strokeWidth={2.5} />
        </span>
        press me
      </button>
      <p className="font-mono text-xs uppercase tracking-wider text-ink-muted">
        pressed {count}x
      </p>
    </div>
  )
}

render(<Sketch />)
end of editor
what's in scope

Sketches run with these values already available — no imports needed. Anything Tailwind can style, gsap can move.

react
React
useState
useEffect
useRef
useMemo
useCallback
useLayoutEffect
motion
gsap
EASE
DURATION
STAGGER
utils
cn
icons
ArrowRight
Check
Heart
Moon
Sparkles
Star
Sun
Zap