Skip to content
Frontend
Hook

Hooks

What 8bitcn-ui runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.

From plugin
8bitcn-ui
2.1k23 skills4 hooks

Claude Code

$ npx -y skills add theorcdev/8bitcn-ui --agent claude-code

Where it lives

  • hooks/use-center-active-sidebar-item.tsGitHub
    Read the script
    import { useLayoutEffect, useRef } from "react";
    
    function centerActiveSidebarItem(
      viewport: HTMLDivElement,
      activeItem: HTMLAnchorElement
    ): boolean {
      if (viewport.clientHeight === 0) {
        return false;
      }
    
      const viewportRect = viewport.getBoundingClientRect();
      const activeItemRect = activeItem.getBoundingClientRect();
      const centeredScrollTop =
        viewport.scrollTop +
        activeItemRect.top -
        viewportRect.top -
        (viewport.clientHeight - activeItemRect.height) / 2;
      const maximumScrollTop = Math.max(
        viewport.scrollHeight - viewport.clientHeight,
        0
      );
    
      viewport.scrollTop = Math.min(
        Math.max(centeredScrollTop, 0),
        maximumScrollTop
      );
    
      return true;
    }
    
    function useCenterActiveSidebarItem(pathname: string) {
      const activeItemRef = useRef<HTMLAnchorElement>(null);
      const viewportRef = useRef<HTMLDivElement>(null);
    
      // biome-ignore lint/correctness/useExhaustiveDependencies: Re-center whenever the active route changes.
      useLayoutEffect(() => {
        const activeItem = activeItemRef.current;
        const viewport = viewportRef.current;
    
        if (!(activeItem && viewport)) {
          return;
        }
    
        if (centerActiveSidebarItem(viewport, activeItem)) {
          return;
        }
    
        const resizeObserver = new ResizeObserver(() => {
          if (centerActiveSidebarItem(viewport, activeItem)) {
            resizeObserver.disconnect();
          }
        });
        resizeObserver.observe(viewport);
    
        return () => resizeObserver.disconnect();
      }, [pathname]);
    
      return { activeItemRef, viewportRef };
    }
    
    export { useCenterActiveSidebarItem };
    
  • hooks/use-mobile.tsGitHub
    Read the script
    import * as React from "react";
    
    const MOBILE_BREAKPOINT = 768;
    
    export function useIsMobile() {
      const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
        undefined
      );
    
      React.useEffect(() => {
        const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
        const onChange = () => {
          setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
        };
        mql.addEventListener("change", onChange);
        setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
        return () => mql.removeEventListener("change", onChange);
      }, []);
    
      return !!isMobile;
    }
    
  • hooks/use-near-viewport.tsGitHub
    Read the script
    "use client";
    
    import { useEffect, useRef, useState } from "react";
    
    export function useNearViewport<T extends Element = HTMLDivElement>(
      rootMargin: string
    ) {
      const ref = useRef<T>(null);
      const [isNearViewport, setIsNearViewport] = useState(false);
    
      useEffect(() => {
        const element = ref.current;
        if (!element || isNearViewport) {
          return;
        }
        if (typeof window.IntersectionObserver === "undefined") {
          setIsNearViewport(true);
          return;
        }
        let isDisconnected = false;
        const disconnect = () => {
          if (!isDisconnected) {
            observer.disconnect();
            isDisconnected = true;
          }
        };
        const observer = new IntersectionObserver(
          ([entry]) => {
            if (entry?.isIntersecting) {
              setIsNearViewport(true);
              disconnect();
            }
          },
          { rootMargin }
        );
        observer.observe(element);
        return disconnect;
      }, [isNearViewport, rootMargin]);
    
      return { isNearViewport, ref };
    }
    
  • hooks/use-showcase-scroll-guard.tsGitHub
    Read the script
    "use client";
    
    import { useEffect } from "react";
    
    export function useShowcaseScrollGuard(enabled = true) {
      useEffect(() => {
        if (!enabled) {
          return;
        }
        const original = Element.prototype.scrollIntoView;
        Element.prototype.scrollIntoView = () => {
          // Intentionally suppress mount-time focus scrolling from showcase widgets.
        };
        const animationFrame = requestAnimationFrame(() => {
          if (window.scrollY > 0 && !window.location.hash) {
            window.scrollTo(0, 0);
          }
        });
        const timer = setTimeout(() => {
          Element.prototype.scrollIntoView = original;
        }, 600);
        return () => {
          cancelAnimationFrame(animationFrame);
          clearTimeout(timer);
          Element.prototype.scrollIntoView = original;
        };
      }, [enabled]);
    }
    

Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.

@theorcdev
Ships with8bitcn-ui

Accessible retro components that you can copy and paste into your apps.Free. Open Source. Built on shadcn/ui. Documentation · Components · Blocks · Themes · Discord

Stars on GitHub

Momentum

stars in total
2.1k
Star history of theorcdev/8bitcn-ui on GitHub, a chart by star-history.com

More plugins that ship hooks.

fluid-functionalism

fluid-functionalism

A shadcn/ui registry of animated React components with a shared motion system, including a hover highlight that glides between items. Useful for building interactive frontend interfaces.

1 skill4 hooks1.1k
@mickadesign@mickadesign
Install Plugin

Questions about the hooks in 8bitcn-ui.

Which coding agents can use the hooks in 8bitcn-ui?
The hooks in 8bitcn-ui install in Claude Code and Cursor: each runs Claude Code hooks from .claude/settings.json, so copy the hooks there.