EbonUI Accordion Documentation

website·Indexed 31 days ago

https://ebonui.com/

Technical specifications and implementation details for the accordion component, focusing on the accordion primitive, display names, and React-based implementation patterns.

Tokens
32.3K
Snippets
144
Records
166
Agent score
0%

What's inside EbonUI

  1. Animate text using the TextAnimate component

    The TextAnimate component allows you to animate text by splitting it into characters, words, or lines and applying predefined animation presets. It uses motion/react (Framer Motion) for transitions and supports accessibility by rendering a screen-reader-only version of the full text.
    import { TextAnimate } from '@/components/ebonui/text-animate';
    
    export default function Page() {
      return (
        <TextAnimate 
          text="Hello World" 
          preset="blurInUp" 
          split="word" 
        />
      );
    }
  2. Implement a sequenced terminal interface with Terminal, TypingAnimation, and AnimatedSpan

    The Terminal component system allows you to create a mock terminal window where content appears in a controlled sequence. The Terminal component acts as a provider that manages the active index of children, triggering animations one by one as they complete.
    import { Terminal, TypingAnimation, AnimatedSpan } from "@/registry/ebonui/terminal"
    
    export default function TerminalDemo() {
      return (
        <Terminal>
          <TypingAnimation>
            &gt; npx create-vite@latest my-app --template react-ts
          </TypingAnimation>
          <AnimatedSpan className="text-green-500">
            ✔ Scaffolding project in /path/to/my-app...
          </AnimatedSpan>
          <AnimatedSpan className="text-green-500">
            ✔ Done. Now run:
          </AnimatedSpan>
          <AnimatedSpan className="ml-4 text-green-500">npm install</AnimatedSpan>
        </Terminal>
      )
    }
  3. Implement a hierarchical File Tree using Tree, Folder, and File components

    The Tree component system allows for the creation of a nested file-system-like interface. It manages the selection state of files and the expansion state of folders using a context provider. The structure typically consists of a Tree root, Folder components for collapsible directories, and File components for selectable items.
    import { Tree, Folder, File } from "@/registry/ebonui/file-tree"
    
    function MyFileTree() {
      return (
        <Tree initialSelectedId="file-1">
          <Folder element="src" value="src">
            <File value="file-1">index.ts</File>
            <Folder element="components" value="components">
              <File value="file-2">Button.tsx</File>
            </Folder>
          </Folder>
          <File value="package.json">package.json</File>
        </Tree>
      );
    }
  4. Implement an interactive 3D Icon Cloud

    The Icon Cloud is a canvas-based component that renders a set of icons or images in a 3D spherical arrangement. It supports mouse-driven rotation, dragging, and a 'target rotation' animation when a specific icon is clicked. The implementation uses a 2D canvas context to simulate 3D projections by calculating rotated X, Y, and Z coordinates and adjusting scale and opacity based on the Z-depth.
    // Core logic for 3D projection and rendering
    const rotatedX = icon.x * cosY - icon.z * sinY;
    const rotatedZ = icon.x * sinY + icon.z * cosY;
    const rotatedY = icon.y * cosX + rotatedZ * sinX;
    
    const scale = (rotatedZ + 200) / 300;
    const opacity = Math.max(0.2, Math.min(1, (rotatedZ + 150) / 200));
    
    ctx.save();
    ctx.translate(canvas.width / 2 + rotatedX, canvas.height / 2 + rotatedY);
    ctx.scale(scale, scale);
    ctx.globalAlpha = opacity;
    // Render image or fallback circle
    ctx.drawImage(iconCanvasesRef.current[index], -20, -20, 40, 40);
    ctx.restore();
  5. Configure RainbowButton variants and sizes

    The RainbowButton uses cva for styling variants.

    Variants:

    • default: Solid rainbow-animated background and border.
    • outline: Bordered style with rainbow animation.

    Sizes:

    • default: h-9 px-4 py-2
    • sm: h-8 rounded-xl px-3 text-xs
    • lg: h-11 rounded-xl px-8
    • icon: size-9
  6. Implement a 3D perspective marquee grid

    To create a 3D effect, wrap multiple Marquee components in a container with a CSS perspective value. Apply transform properties (rotateX, translateZ, etc.) to the inner wrapper to tilt the grid. Combine this with vertical marquees moving in opposite directions for a dynamic team or review wall.
    export default function Marquee3D() {
      return (
        <div className="relative flex h-96 w-full flex-row items-center justify-center gap-4 overflow-hidden [perspective:300px]">
          <div
            className="flex flex-row items-center gap-4"
            style={{
              transform: "translateX(0px) translateY(-50px) translateZ(-150px) rotateX(15deg) rotateY(0deg) rotateZ(0deg)",
            }}
          >
            <Marquee pauseOnHover vertical className="[--duration:20s]">
              {firstRow.map((review) => <TeamCard key={review.role} {...review} />)}
            </Marquee>
            <Marquee reverse pauseOnHover className="[--duration:20s]" vertical>
              {secondRow.map((review) => <TeamCard key={review.role} {...review} />)}
            </Marquee>
            {/* Additional Marquee columns... */}
          </div>
          {/* Edge gradients for depth effect */}
          <div className="from-background pointer-events-none absolute inset-x-0 top-0 h-1/4 bg-gradient-to-b"></div>
          <div className="from-background pointer-events-none absolute inset-x-0 bottom-0 h-1/4 bg-gradient-to-t"></div>
        </div>
      )
    }
  7. Implement a Bento Grid layout

    Use the BentoGrid and BentoCard components to create a responsive grid of feature cards. Each card can be configured with a name, description, CTA, and a custom background element (such as an image or a marquee) to create a visually rich layout.
    const features = [
      {
        name: "Book Summaries",
        description: "The art of condensing knowledge into bite-sized pieces.",
        href: "#",
        cta: "Learn more",
        className: "col-span-3 lg:col-span-1",
        background: (
          <Marquee pauseOnHover className="absolute top-10 [mask-image:linear-gradient(to_top,transparent_40%,#000_100%)] [--duration:20s]">
            {books.map((f, idx) => (
              <figure key={idx} className="relative w-32 cursor-pointer overflow-hidden rounded-xl border p-4 border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15] transform-gpu transition-all duration-300 ease-out hover:blur-none">
                <div className="flex flex-row items-center gap-2">
                  <div className="flex flex-col">
                    <figcaption className="text-sm font-medium dark:text-white">{f.title}</figcaption>
                  </div>
                </div>
                <blockquote className="mt-2 text-xs">{f.description}</blockquote>
              </figure>
            ))}
          </Marquee>
        ),
      },
      // ... other features
    ];
    
    export default function BentoDemo() {
      return (
        <BentoGrid>
          {features.map((feature, idx) => (
            <BentoCard key={idx} {...feature} />
          ))}
        </BentoGrid>
      )
    }
  8. Implement scroll-based velocity animations

    The ScrollVelocityContainer and ScrollVelocityRow components create a continuous horizontal scrolling effect that responds to the user's vertical scroll speed. The ScrollVelocityRow accepts a baseVelocity to set the constant speed and a direction (1 or -1) to control the movement direction.
    import { ScrollVelocityContainer, ScrollVelocityRow } from "@/registry/ebonui/scroll-based-velocity"
    
    export default function ScrollBasedVelocityDemo() {
      return (
        <div className="relative flex w-full flex-col items-center justify-center overflow-hidden">
          <ScrollVelocityContainer className="text-4xl font-bold tracking-[-0.02em] md:text-7xl md:leading-[5rem]">
            <ScrollVelocityRow baseVelocity={20} direction={1}>
              Velocity Scroll
            </ScrollVelocityRow>
            <ScrollVelocityRow baseVelocity={20} direction={-1}>
              Velocity Scroll
            </ScrollVelocityRow>
          </ScrollVelocityContainer>
          {/* Gradient overlays for fade effect */}
          <div className="from-background pointer-events-none absolute inset-y-0 left-0 w-1/4 bg-gradient-to-r"></div>
          <div className="from-background pointer-events-none absolute inset-y-0 right-0 w-1/4 bg-gradient-to-l"></div>
        </div>
      )
    }
  9. Implement a shimmering icon effect

    Create a visual 'shimmer' or 'sweep' effect over an icon by overlaying a gradient motion.div that animates its horizontal position from -100% to 100% in a continuous loop.
    <div className="bg-background border-border/70 relative flex h-16 w-16 items-center justify-center overflow-hidden rounded-2xl border-2 p-4 shadow-[0_0_15px_5px_var(--muted)]">
      <Image src={SocialInstagram} alt="Logo" className="brightness-0 dark:invert" />
      <motion.div
        className="via-foreground absolute top-0 left-0 h-full w-full bg-gradient-to-r from-transparent to-transparent opacity-30"
        initial={{ x: "-100%" }}
        animate={{ x: "100%" }}
        transition={{
          duration: 2,
          ease: "linear",
          repeat: Infinity,
          repeatType: "loop",
        }}
        style={{ willChange: "transform" }}
      />
    </div>
  10. Create an animated SVG line chart with Intersection Observer

    Implement a data-driven SVG line chart that animates its path length from 0 to 1 when it enters the viewport. This is achieved using a ref and IntersectionObserver to trigger the isChartVisible state, which then drives a Framer Motion animate prop on the polyline element.
    useEffect(() => {
        const observer = new IntersectionObserver(
          (entries) => {
            entries.forEach((entry) => {
              if (entry.isIntersecting) {
                setIsChartVisible(true)
                observer.unobserve(entry.target)
              }
            })
          },
          { threshold: 0.5 }
        )
    
        if (chartRef.current) observer.observe(chartRef.current)
        return () => { if (chartRef.current) observer.unobserve(chartRef.current) }
    }, [chartRef])
    
    // In JSX:
    <motion.polyline
        fill="none"
        stroke="var(--primary)"
        strokeWidth="3"
        points={data.map((value, index) => `${(index / (data.length - 1)) * chartWidth},${chartHeight - (value / maxData) * chartHeight}`).join(" ")}
        initial={{ pathLength: 0 }}
        animate={{ pathLength: isChartVisible ? 1 : 0 }}
        transition={{ duration: 1.5, ease: "easeInOut" }}
    />
  11. Implement a Custom Cursor with Framer Motion

    Create a high-performance custom cursor that follows the mouse with spring physics, rotates based on movement velocity, and scales down when moving. The implementation uses requestAnimationFrame to throttle mouse move events and useSpring for smooth animations.
    const cursorX = useSpring(0, springConfig)
    const cursorY = useSpring(0, springConfig)
    const rotation = useSpring(0, { ...springConfig, damping: 60, stiffness: 300 })
    const scale = useSpring(1, { ...springConfig, stiffness: 500, damping: 35 })
    
    useEffect(() => {
      const CustomMouseMove = (e: MouseEvent) => {
        const currentPos = { x: e.clientX, y: e.clientY }
        // ... velocity calculation logic ...
        cursorX.set(currentPos.x)
        cursorY.set(currentPos.y)
        
        if (speed > 0.1) {
          // Calculate rotation based on velocity
          rotation.set(accumulatedRotation.current)
          scale.set(0.95)
          setIsMoving(true)
          
          setTimeout(() => {
            scale.set(1)
            setIsMoving(false)
          }, 150)
        }
      }
    
      const throttledMouseMove = (e: MouseEvent) => {
        if (rafId) return
        rafId = requestAnimationFrame(() => {
          CustomMouseMove(e)
          rafId = 0
        })
      }
    
      document.body.style.cursor = "none"
      window.addEventListener("mousemove", throttledMouseMove)
      return () => {
        window.removeEventListener("mousemove", throttledMouseMove)
        document.body.style.cursor = "auto"
      }
    }, [cursorX, cursorY, rotation, scale])
    
    return (
      <motion.div
        style={{
          position: "fixed",
          left: cursorX,
          top: cursorY,
          translateX: "-50%",
          translateY: "-50%",
          rotate: rotation,
          scale: scale,
          zIndex: 100,
          pointerEvents: "none",
          willChange: "transform",
        }}
      >
        {cursor}
      </motion.div>
    )
  12. Use the Icons library for social and platform logos

    The Icons object provides a set of React components that render SVG logos for common platforms including GitHub, Notion, OpenAI, Google Drive, and WhatsApp. These components are designed to be used within the UI to provide visual identification for different integrations or platforms.
    const Icons = {
      gitHub: () => (
        <svg width="100" height="100" viewBox="0 0 438.549 438.549">
          <path fill="currentColor" d="M409.132 114.573..." />
        </svg>
      ),
      notion: () => (
        <svg width="100" height="100" viewBox="0 0 100 100" fill="none">
          <path d="M6.017 4.313..." fill="#ffffff" />
          <path d="M61.35 0.227..." fill="#000000" />
        </svg>
      ),
      openai: () => (
        <svg width="100" height="100" viewBox="0 0 24 24" className="fill-black dark:fill-white">
          <path d="M22.2819 9.8211..." />
        </svg>
      ),
      googleDrive: () => (
        <svg width="100" height="100" viewBox="0 0 87.3 78">
          <path d="m6.6 66.85..." fill="#0066da" />
          {/* Additional paths for Google Drive logo */}
        </svg>
      ),
      whatsapp: () => (
        <svg width="100" height="100" viewBox="0 0 175.216 175.552">
          <defs>
            <linearGradient id="b" x1="85.915" x2="86.535" y1="32.567" y2="137.092" gradientUnits="userSpaceOnUse">
              <stop offset="0" stopColor="#57d163" />
              <stop offset="1" stopColor="#23b33a" />
            </linearGradient>
          </defs>
          <path d="M87.184 25.227..." fill="url(#b)" />
        </svg>
      )
    };