bottega

primitives

Layered Shadow Tooltip

A CSS-art tooltip popup with a signature stacked box-shadow border technique — eight to ten concentric shadow layers build up a retro/pixel-art multi-ring outline effect on both the pill trigger and the floating popup. The popup slides up and fades in above the trigger on hover and keyboard focus (focus-visible ring, aria-describedby wiring, role='tooltip'). The star icon inside renders via a primary-token linear-gradient fill, and the decorative content-placeholder lines use token-derived colors throughout. No JS is required for the hover path; keyboard focus is tracked via a React state flag to show the tooltip for non-pointer users. The mount-gate pattern prevents hydration mismatch between SSR and the first client render.

Ported from SelfMadeSystem (MIT)

Layered shadow tooltip

Install

1. Register the namespace (once per project):

json
// components.json — register the @bottega namespace once
{
  "registries": {
    "@bottega": { "url": "https://bottega.ariacode.ca/r/{name}.json" }
  }
}

2. Add the component:

bash
npx shadcn add @bottega/layered-shadow-tooltip

Usage

Usagetsx
<LayeredShadowTooltip>
  {/* children */}
</LayeredShadowTooltip>

Props

PropTypeDefaultDescription
contentReact.ReactNodeText or node shown inside the tooltip popup. The default preview renders a star icon + two placeholder lines when this prop is omitted — supply a string for a real label.
children*React.ReactNodeThe trigger element that the tooltip is anchored to.
classNamestringAdditional class names merged onto the wrapper element.

Source

layered-shadow-tooltip.tsxtsx
"use client";

import { useEffect, useId, useRef, useState } from "react";
import type React from "react";
import { useReducedMotion } from "motion/react";
import styles from "./layered-shadow-tooltip.module.css";

export type LayeredShadowTooltipProps = {
  /**
   * Text or node shown inside the tooltip popup.
   * The default preview renders a star icon + two placeholder lines
   * when this prop is omitted — supply a string for a real label.
   */
  content?: React.ReactNode;
  /** The trigger element that the tooltip is anchored to. */
  children: React.ReactNode;
  /** Additional class names merged onto the wrapper element. */
  className?: string;
};

/**
 * Layered-shadow tooltip trigger element.
 * The tooltip popup is shown above the trigger on hover AND keyboard focus.
 * Accessibility: trigger is focusable, tooltip has role="tooltip",
 * and the trigger is wired via aria-describedby.
 */
export function LayeredShadowTooltip({
  content,
  children,
  className = "",
}: LayeredShadowTooltipProps) {
  const tooltipId = useId();
  const triggerRef = useRef<HTMLSpanElement>(null);

  // Mount-gate + null-safe reduced-motion (avoids hydration mismatch).
  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);
  const prefersReducedMotion = useReducedMotion();
  const reduce = !mounted || prefersReducedMotion === null || prefersReducedMotion;

  // Keyboard focus state drives the open class when mouse is not in play.
  const [focused, setFocused] = useState(false);

  const rootClassName = [
    styles.container,
    focused && styles.focused,
    reduce && styles.reducedMotion,
    className,
  ]
    .filter(Boolean)
    .join(" ");

  const defaultContent = (
    <>
      {/* Decorative star icon — aria-hidden because it's purely visual. */}
      <span aria-hidden="true" className={styles.iconWrap}>
        <svg
          className={styles.icon}
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
          aria-hidden="true"
          focusable="false"
        >
          <defs>
            <linearGradient y2="1" x2="0" id={`${tooltipId}-gradient`}>
              <stop
                style={{ stopColor: "color-mix(in oklch, var(--primary) 70%, var(--background))" }}
                offset="0%"
              />
              <stop
                style={{ stopColor: "var(--primary)" }}
                offset="100%"
              />
            </linearGradient>
          </defs>
          <path
            d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"
            fill={`url(#${tooltipId}-gradient)`}
            className={styles.starPath}
          />
        </svg>
      </span>
      {/* Decorative content lines — represent text rows inside the tooltip. */}
      <span aria-hidden="true" className={styles.lines}>
        <span className={styles.line1} />
        <span className={styles.line2} />
      </span>
    </>
  );

  return (
    <span
      ref={triggerRef}
      className={rootClassName}
      aria-describedby={tooltipId}
      tabIndex={0}
      onFocus={() => setFocused(true)}
      onBlur={() => setFocused(false)}
    >
      {/* Tooltip popup — absolutely positioned above the trigger. */}
      <span
        id={tooltipId}
        role="tooltip"
        className={styles.tooltip}
      >
        {content != null ? (
          content
        ) : (
          <>
            {/* Decorative star in tooltip */}
            <span aria-hidden="true" className={styles.iconWrap}>
              <svg
                className={styles.icon}
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 24 24"
                aria-hidden="true"
                focusable="false"
              >
                <path
                  d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"
                  fill={`url(#${tooltipId}-gradient)`}
                  className={styles.starPath}
                />
              </svg>
            </span>
            <span aria-hidden="true" className={styles.lines}>
              <span className={styles.line1} />
              <span className={styles.line2} />
            </span>
          </>
        )}
      </span>

      {/* Trigger body: children + decorative placeholder lines. */}
      <span className={styles.body}>
        {children}
        <span aria-hidden="true" className={styles.lines}>
          <span className={styles.line1} />
          <span className={styles.line2} />
        </span>
      </span>
    </span>
  );
}

Dependencies

  • motion
  • @bottega/tokens