import { useState } from 'react'

export default function App() {
    const [value, setValue] = useState<string | null>('banana')
    return (
        <div className="p-4 space-y-4">
            <button>interactive element</button>
            <fieldset className="border-2 p-2  border-black">
                <legend className="bg-black py-1 px-3 text-white">
                    Select your favorite fruit:
                </legend>

                <div className="space-x-2">
                    <input
                        type="radio"
                        id="strawberry"
                        name="drone"
                        value="strawberry"
                        onChange={() => setValue('strawberry')}
                        checked={value === 'strawberry'}
                    />
                    <label htmlFor="strawberry">Strawberry</label>
                </div>

                <div className="space-x-2">
                    <input
                        type="radio"
                        id="banana"
                        name="drone"
                        value="banana"
                        onChange={() => setValue('banana')}
                        checked={value === 'banana'}
                    />
                    <label htmlFor="banana">Banana</label>
                </div>

                <div className="space-x-2">
                    <input
                        type="radio"
                        id="huey"
                        name="drone"
                        value="apple"
                        onChange={() => setValue('apple')}
                        checked={value === 'apple'}
                    />
                    <label htmlFor="apple">Apple</label>
                </div>
            </fieldset>
            <button>interactive element</button>
        </div>
    )
}

import classNames from "classnames";
import {
  PointerEvent,
  FocusEvent,
  useEffect,
  useRef,
  useState,
  CSSProperties,
} from "react";

type Tab = { label: string; id: string };

type Props = {
  selectedTabIndex: number;
  tabs: Tab[];
  setSelectedTab: (input: number) => void;
};

export const CSSTabs = ({
  tabs,
  selectedTabIndex,
  setSelectedTab,
}: Props): JSX.Element => {
  const [buttonRefs, setButtonRefs] = useState<Array<HTMLButtonElement | null>>(
    []
  );

  useEffect(() => {
    setButtonRefs((prev) => prev.slice(0, tabs.length));
  }, [tabs.length]);

  const [hoveredTabIndex, setHoveredTabIndex] = useState<number | null>(null);
  const [hoveredRect, setHoveredRect] = useState<DOMRect | null>(null);

  const navRef = useRef<HTMLDivElement>(null);
  const navRect = navRef.current?.getBoundingClientRect();

  const selectedRect = buttonRefs[selectedTabIndex]?.getBoundingClientRect();

  const [isInitialHoveredElement, setIsInitialHoveredElement] = useState(true);
  const isInitialRender = useRef(true);

  const onLeaveTabs = () => {
    setIsInitialHoveredElement(true);
    setHoveredTabIndex(null);
  };

  const onEnterTab = (
    e: PointerEvent<HTMLButtonElement> | FocusEvent<HTMLButtonElement>,
    i: number
  ) => {
    if (!e.target || !(e.target instanceof HTMLButtonElement)) return;

    setHoveredTabIndex((prev) => {
      if (prev != null && prev !== i) {
        setIsInitialHoveredElement(false);
      }

      return i;
    });
    setHoveredRect(e.target.getBoundingClientRect());
  };

  const onSelectTab = (i: number) => {
    setSelectedTab(i);
  };

  let hoverStyles: CSSProperties = { opacity: 0 };
  if (navRect && hoveredRect) {
    hoverStyles.transform = `translate3d(${hoveredRect.left - navRect.left}px,${
      hoveredRect.top - navRect.top
    }px,0px)`;
    hoverStyles.width = hoveredRect.width;
    hoverStyles.height = hoveredRect.height;
    hoverStyles.opacity = hoveredTabIndex != null ? 1 : 0;
    hoverStyles.transition = isInitialHoveredElement
      ? `opacity 150ms`
      : `transform 150ms 0ms, opacity 150ms 0ms, width 150ms`;
  }

  let selectStyles: CSSProperties = { opacity: 0 };
  if (navRect && selectedRect) {
    selectStyles.width = selectedRect.width * 0.8;
    selectStyles.transform = `translateX(calc(${
      selectedRect.left - navRect.left
    }px + 10%))`;
    selectStyles.opacity = 1;
    selectStyles.transition = isInitialRender.current
      ? `opacity 150ms 150ms`
      : `transform 150ms 0ms, opacity 150ms 150ms, width 150ms`;

    isInitialRender.current = false;
  }

  return (
    <nav
      ref={navRef}
      className="flex flex-shrink-0 justify-center items-center relative z-0 py-2"
      onPointerLeave={onLeaveTabs}
    >
      {tabs.map((item, i) => {
        return (
          <button
            key={i}
            className={classNames(
              "text-md relative rounded-md flex items-center h-8 px-4 z-20 bg-transparent text-sm text-slate-500 cursor-pointer select-none transition-colors",
              {
                "text-slate-700":
                  hoveredTabIndex === i || selectedTabIndex === i,
              }
            )}
            ref={(el) => (buttonRefs[i] = el)}
            onPointerEnter={(e) => onEnterTab(e, i)}
            onFocus={(e) => onEnterTab(e, i)}
            onClick={() => onSelectTab(i)}
          >
            {item.label}
          </button>
        );
      })}
      <div
        className="absolute z-10 top-0 left-0 rounded-md bg-gray-200 transition-[width]"
        style={hoverStyles}
      />
      <div
        className={"absolute z-10 bottom-0 left-0 h-0.5 bg-slate-500"}
        style={selectStyles}
      />
    </nav>
  );
};

import { useState } from 'react'

export default function App() {
    const [value, setValue] = useState<string | null>('banana')
    return (
        <div className="p-4 space-y-4">
            <button>interactive element</button>
            <fieldset className="border-2 p-2  border-black">
                <legend className="bg-black py-1 px-3 text-white">
                    Select your favorite fruit:
                </legend>

                <div className="space-x-2">
                    <input
                        type="radio"
                        id="strawberry"
                        name="drone"
                        value="strawberry"
                        onChange={() => setValue('strawberry')}
                        checked={value === 'strawberry'}
                    />
                    <label htmlFor="strawberry">Strawberry</label>
                </div>

                <div className="space-x-2">
                    <input
                        type="radio"
                        id="banana"
                        name="drone"
                        value="banana"
                        onChange={() => setValue('banana')}
                        checked={value === 'banana'}
                    />
                    <label htmlFor="banana">Banana</label>
                </div>

                <div className="space-x-2">
                    <input
                        type="radio"
                        id="huey"
                        name="drone"
                        value="apple"
                        onChange={() => setValue('apple')}
                        checked={value === 'apple'}
                    />
                    <label htmlFor="apple">Apple</label>
                </div>
            </fieldset>
            <button>interactive element</button>
        </div>
    )
}

Contact me at