Works natively with Cursor. Add npx -y bare-ui-mcp to your MCP settings.

Marquee

A zero-dependency, ultra-lightweight marquee. Uses pure CSS for lightning-fast infinite scrolling.

Horizontal Scroll (Pause on Hover)

Item 1

Bare UI is native.

Item 2

Bare UI is native.

Item 3

Bare UI is native.

Item 4

Bare UI is native.

Item 5

Bare UI is native.

Item 6

Bare UI is native.

Reverse Direction

React
Next.js
Pure CSS
Zero Deps
Performance
Accessible

Installation

1. Add the CSS to your global stylesheet:

css
/* Bare Marquee Animation */
.bare-marquee-wrapper {
  overflow: hidden;
  display: flex;
  position: relative;
  width: 100%;
  mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent);
}
.bare-marquee-wrapper[style*="--marquee-direction"] .bare-marquee.vertical {
  mask-image: linear-gradient(to bottom, transparent, black 10%, black 90%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, transparent, black 10%, black 90%, transparent);
  height: 100%;
}
.bare-marquee {
  display: flex;
  animation: bare-marquee-scroll var(--marquee-speed, 20s) linear infinite var(--marquee-direction, normal);
}
.bare-marquee.horizontal {
  width: max-content;
  flex-direction: row;
}
.bare-marquee.vertical {
  height: max-content;
  flex-direction: column;
  animation-name: bare-marquee-scroll-vertical;
}
.bare-marquee.pause-on-hover:hover {
  animation-play-state: paused;
}
.bare-marquee-content {
  display: flex;
  flex-shrink: 0;
  gap: var(--marquee-gap, 1rem);
  padding-right: var(--marquee-gap, 1rem);
}
.bare-marquee.vertical .bare-marquee-content {
  flex-direction: column;
  padding-right: 0;
  padding-bottom: var(--marquee-gap, 1rem);
}
@keyframes bare-marquee-scroll {
  0% { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}
@keyframes bare-marquee-scroll-vertical {
  0% { transform: translateY(0); }
  100% { transform: translateY(-50%); }
}

2. Copy the React component into your project:

tsx
import * as React from "react";
import { HTMLAttributes } from "react";

export interface MarqueeProps extends HTMLAttributes<HTMLDivElement> {
  /**
   * The speed of the marquee animation. Accepts standard CSS time values (e.g., '10s', '500ms').
   * @default '20s'
   */
  speed?: string;
  /**
   * Pause the animation when hovering over the marquee.
   * @default false
   */
  pauseOnHover?: boolean;
  /**
   * Direction of the marquee.
   * @default 'normal'
   */
  direction?: "normal" | "reverse";
  /**
   * Orientation of the marquee.
   * @default 'horizontal'
   */
  orientation?: "horizontal" | "vertical";
  /**
   * Gap between items. Accepts standard CSS length values.
   * @default '1rem'
   */
  gap?: string;
  /**
   * Content to scroll within the marquee.
   */
  children: React.ReactNode;
}

/**
 * A highly performant, zero-dependency Marquee component.
 * Uses pure CSS animations to provide infinite scrolling with minimal bundle footprint.
 */
export const Marquee = React.forwardRef<HTMLDivElement, MarqueeProps>(
  (
    {
      speed = "20s",
      pauseOnHover = false,
      direction = "normal",
      orientation = "horizontal",
      gap = "1rem",
      children,
      className = "",
      style,
      ...props
    },
    ref
  ) => {
    // Determine dynamic classes based on props
    const orientationClass = orientation === "vertical" ? "vertical" : "horizontal";
    const hoverClass = pauseOnHover ? "pause-on-hover" : "";
    const classes = `bare-marquee ${orientationClass} ${hoverClass}`.trim();

    // Inline CSS Variables to pass configuration cleanly to pure CSS
    const customStyle = {
      "--marquee-speed": speed,
      "--marquee-direction": direction,
      "--marquee-gap": gap,
      ...style,
    } as React.CSSProperties;

    return (
      <div
        ref={ref}
        className={`bare-marquee-wrapper ${className}`}
        style={customStyle}
        {...props}
      >
        <div className={classes}>
          <div className="bare-marquee-content">{children}</div>
          {/* Duplicate content to create the infinite scroll effect seamlessly */}
          <div className="bare-marquee-content" aria-hidden="true">
            {children}
          </div>
        </div>
      </div>
    );
  }
);

Marquee.displayName = "Marquee";

Usage

tsx
import { Marquee } from '@/components/ui/marquee';
import { GlassCard } from '@/components/ui/glass-card';

export default function MyComponent() {
  return (
    <Marquee speed="25s" pauseOnHover>
      {[1, 2, 3, 4].map((i) => (
        <GlassCard key={i} style={{ width: '200px', padding: '1rem' }}>
          Item {i}
        </GlassCard>
      ))}
    </Marquee>
  );
}