Skip to content

Marquee

<quiet-marquee> stable since 6.1

Scrolls its content in a smooth, continuous loop like a news ticker. Use a marquee for logo walls, testimonials, headlines, and other streams of content that benefit from ambient motion.

Accessibility is built in. The marquee shows a pause button, pauses while the user hovers over it or focuses anything inside it, honors reduced motion preferences, and announces its content to screen readers exactly once. Slotted content is cloned automatically to fill the loop, so you only need to provide one copy, and the edges fade so items glide in and out of view.

MEOW PURR NAPS HISS TUNA
<quiet-marquee id="marquee__overview" speed="40" label="Cat market update">
  <span class="stock up">
    <span class="symbol">MEOW</span>
    <quiet-number number="42.87" type="currency" currency="USD"></quiet-number>
    <span class="change">
      <quiet-icon name="trending-up"></quiet-icon>
      <quiet-number number="0.024" type="percent" maximum-fraction-digits="1"></quiet-number>
    </span>
  </span>

  <span class="stock down">
    <span class="symbol">PURR</span>
    <quiet-number number="18.32" type="currency" currency="USD"></quiet-number>
    <span class="change">
      <quiet-icon name="trending-down"></quiet-icon>
      <quiet-number number="0.008" type="percent" maximum-fraction-digits="1"></quiet-number>
    </span>
  </span>

  <span class="stock up">
    <span class="symbol">NAPS</span>
    <quiet-number number="106.5" type="currency" currency="USD"></quiet-number>
    <span class="change">
      <quiet-icon name="trending-up"></quiet-icon>
      <quiet-number number="0.051" type="percent" maximum-fraction-digits="1"></quiet-number>
    </span>
  </span>

  <span class="stock down">
    <span class="symbol">HISS</span>
    <quiet-number number="3.09" type="currency" currency="USD"></quiet-number>
    <span class="change">
      <quiet-icon name="trending-down"></quiet-icon>
      <quiet-number number="0.012" type="percent" maximum-fraction-digits="1"></quiet-number>
    </span>
  </span>

  <span class="stock up">
    <span class="symbol">TUNA</span>
    <quiet-number number="67.4" type="currency" currency="USD"></quiet-number>
    <span class="change">
      <quiet-icon name="trending-up"></quiet-icon>
      <quiet-number number="0.033" type="percent" maximum-fraction-digits="1"></quiet-number>
    </span>
  </span>
</quiet-marquee>

<style>
  #marquee__overview {
    font-variant-numeric: tabular-nums;

    .stock {
      display: flex;
      align-items: center;
      gap: 0.5rem;

      .symbol {
        font-weight: var(--quiet-font-weight-semibold);
      }

      .change {
        display: flex;
        align-items: center;
        gap: 0.25rem;
      }

      &.up .change {
        color: var(--quiet-constructive-text-colorful);
      }

      &.down .change {
        color: var(--quiet-destructive-text-colorful);
      }
    }
  }
</style>

Marquee honors the user's prefers-reduced-motion setting. If you're not seeing animations, this might be why. The content remains visible, and users can start the marquee themselves with its play button. To override this behavior, which is generally not recommended, use the ignore-reduced-motion attribute.

Examples Jump to heading

Providing content Jump to heading

Slot in the items you want to scroll as direct children of the marquee. Any content works, and you can mix different types of elements freely. The marquee measures them and clones them as many times as needed to keep the loop seamless, even as the container resizes. Clones are hidden from assistive devices and can't receive focus, so screen readers hear each item only once.

You can style items with regular CSS since they live in your document, not the shadow root. Use descendant selectors such as #my-marquee img, because the clones are nested one level deeper than the originals and direct child selectors won't match them.

A black and white cat resting on a ledge A blue-eyed cat looking up A wide-eyed kitten lying down A gray kitten peeking from behind a tree A fluffy kitten sprawled on a red pillow
<quiet-marquee id="marquee__content" label="Cats on parade">
  <img src="https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?q=80&w=400&auto=format&fit=crop" alt="A black and white cat resting on a ledge">
  <img src="https://images.unsplash.com/photo-1472491235688-bdc81a63246e?q=80&w=400&auto=format&fit=crop" alt="A blue-eyed cat looking up">
  <img src="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?q=80&w=400&auto=format&fit=crop" alt="A wide-eyed kitten lying down">
  <img src="https://images.unsplash.com/photo-1503844281047-cf42eade5ca5?q=80&w=400&auto=format&fit=crop" alt="A gray kitten peeking from behind a tree">
  <img src="https://images.unsplash.com/photo-1498336179775-9836baef8fdf?q=80&w=400&auto=format&fit=crop" alt="A fluffy kitten sprawled on a red pillow">
</quiet-marquee>

<style>
  #marquee__content img {
    width: 10rem;
    height: 7rem;
    object-fit: cover;
    border-radius: var(--quiet-border-radius-md);
  }
</style>

Prefer non-interactive content. Links and buttons work, and focusing them pauses the marquee, but their clones are inert stand-ins that can't be clicked. Avoid form controls entirely, since cloned controls still submit with a containing form.

Setting the speed Jump to heading

Use the speed attribute to set the scrolling speed in pixels per second. The default is 50. Because speed is a velocity, not a duration, marquees with different amounts of content move at the same pace.

🐈 🐈‍⬛ 🐈 🐈‍⬛ 💨
<div id="marquee__speed">
  <quiet-marquee speed="150" label="Zoomies in progress">
    <span>🐈</span>
    <span>🐈‍⬛</span>
    <span>🐈</span>
    <span>🐈‍⬛</span>
    <span>💨</span>
  </quiet-marquee>

  <quiet-slider label="Speed" min="10" max="400" value="150" step="10" with-tooltip></quiet-slider>
</div>

<script>
  const container = document.getElementById('marquee__speed');
  const marquee = container.querySelector('quiet-marquee');
  const slider = container.querySelector('quiet-slider');

  slider.addEventListener('quiet-input', () => {
    marquee.speed = slider.value;
  });
</script>

<style>
  #marquee__speed {
    quiet-marquee {
      font-size: 2rem;
      margin-block-end: 1.5rem;
    }

    quiet-slider {
      max-width: 260px;
    }
  }
</style>

Reversing the direction Jump to heading

Add the reverse attribute to scroll the other way. Horizontal marquees are RTL-aware, so content always enters from the natural reading end unless you reverse it.

Backwards day at the cat café All zoomies are now reverse zoomies Tail-first entrances only
<div id="marquee__reverse">
  <quiet-marquee reverse label="Cats walking backwards">
    <span>Backwards day at the cat café</span>
    <quiet-icon name="coffee"></quiet-icon>
    <span>All zoomies are now reverse zoomies</span>
    <quiet-icon name="run"></quiet-icon>
    <span>Tail-first entrances only</span>
    <quiet-icon name="arrow-big-right"></quiet-icon>
  </quiet-marquee>

  <quiet-switch label="Reverse" checked></quiet-switch>
</div>

<script>
  const container = document.getElementById('marquee__reverse');
  const marquee = container.querySelector('quiet-marquee');
  const reverseSwitch = container.querySelector('quiet-switch');

  reverseSwitch.addEventListener('quiet-change', () => {
    marquee.reverse = reverseSwitch.checked;
  });
</script>

<style>
  #marquee__reverse {
    quiet-marquee {
      margin-block-end: 1.5rem;
    }

    quiet-icon {
      color: var(--quiet-primary-text-colorful);
    }
  }
</style>

Vertical marquees Jump to heading

Set orientation to vertical to scroll up instead of across. Give the marquee a height, otherwise there's nothing to clip the content to. Combine with reverse to scroll down.

🎉 Mochi found a forever home
🏠 Biscuit is settling in nicely
😻 Clementine adopted by a lovely family
🧶 Ziggy now rules a house with three yarn baskets
🛋️ Pretzel claimed the good couch on day one
<div id="marquee__vertical">
  <quiet-marquee orientation="vertical" reverse speed="30" label="Adoption updates">
    <div>🎉 Mochi found a forever home</div>
    <div>🏠 Biscuit is settling in nicely</div>
    <div>😻 Clementine adopted by a lovely family</div>
    <div>🧶 Ziggy now rules a house with three yarn baskets</div>
    <div>🛋️ Pretzel claimed the good couch on day one</div>
  </quiet-marquee>

  <quiet-switch label="Reverse" checked></quiet-switch>
</div>

<script>
  const container = document.getElementById('marquee__vertical');
  const marquee = container.querySelector('quiet-marquee');
  const reverseSwitch = container.querySelector('quiet-switch');

  reverseSwitch.addEventListener('quiet-change', () => {
    marquee.reverse = reverseSwitch.checked;
  });
</script>

<style>
  #marquee__vertical {
    quiet-marquee {
      height: 8rem;
      max-width: 400px;
      margin-inline: auto;
      margin-block-end: 1.5rem;

      &::part(content) {
        justify-content: center;
      }
    }
  }
</style>

Adjusting the gap Jump to heading

Use the --gap custom property to control the space between items, including the space between the last item and the start of the next loop.

😸 😹 😻 😼 😽
<quiet-marquee id="marquee__gap" style="--gap: 4rem;" label="Well-spaced cats">
  <span>😸</span>
  <span>😹</span>
  <span>😻</span>
  <span>😼</span>
  <span>😽</span>
</quiet-marquee>

<style>
  #marquee__gap {
    font-size: 2rem;
  }
</style>

Changing the fade Jump to heading

Content fades in and out at the marquee's edges by default. The fade is a mask, so it works over any background, including images and gradients. Use the --fade-size custom property to adjust its size.

Cats sleep for about 70% of their lives A group of kittens is called a kindle Cats can rotate their ears 180 degrees
<quiet-marquee style="--fade-size: 6rem;" label="Slow fading cat facts">
  <span>Cats sleep for about 70% of their lives</span>
  <quiet-icon name="moon"></quiet-icon>
  <span>A group of kittens is called a kindle</span>
  <quiet-icon name="zzz"></quiet-icon>
  <span>Cats can rotate their ears 180 degrees</span>
  <quiet-icon name="bed"></quiet-icon>
</quiet-marquee>

To remove the fade entirely, add the without-fade attribute.

A cat's nose print is as unique as a fingerprint Cats can jump up to six times their height Most cats have no eyelashes
<quiet-marquee without-fade label="Sharp-edged cat facts">
  <span>A cat's nose print is as unique as a fingerprint</span>
  <quiet-icon name="paw"></quiet-icon>
  <span>Cats can jump up to six times their height</span>
  <quiet-icon name="fish"></quiet-icon>
  <span>Most cats have no eyelashes</span>
  <quiet-icon name="cat"></quiet-icon>
</quiet-marquee>

Pausing and playing Jump to heading

The marquee pauses while the user hovers over it and while anything inside it has focus. Add without-pause-on-hover to keep it moving under the pointer.

To pause it programmatically, set the paused attribute or property. The paused custom state applies whenever the marquee isn't moving for any reason, so you can style it with :state(paused). The example below uses it to draw a dashed outline while the marquee is paused.

🐱 🐈 🐱 🐈‍⬛ 🐱 Pause
<div id="marquee__pausing">
  <quiet-marquee label="Pausable cat parade" without-pause-on-hover>
    <span>🐱</span>
    <span>🐈</span>
    <span>🐱</span>
    <span>🐈‍⬛</span>
    <span>🐱</span>
  </quiet-marquee>
  <quiet-button toggle="off">Pause</quiet-button>
</div>

<script>
  const container = document.getElementById('marquee__pausing');
  const marquee = container.querySelector('quiet-marquee');
  const button = container.querySelector('quiet-button');

  button.addEventListener('click', () => {
    marquee.paused = button.toggle === 'on';
  });
</script>

<style>
  #marquee__pausing {
    quiet-marquee {
      font-size: 2rem;
      margin-block-end: 1rem;

      &:state(paused) {
        outline: 2px dashed var(--quiet-neutral-stroke-soft);
        outline-offset: 0.25rem;
      }
    }
  }
</style>

A play/pause button is included in the marquee's corner so all users can stop the motion. On devices that support hovering, it stays hidden until you hover over the marquee or focus the button with the keyboard. On touch devices, it's always visible. You can restyle it with the pause-button part, or remove it with the without-controls attribute. This is only recommended for presentational content, as removing these options hinders accessibility for some users.

Press the button to give the cats a break They will wait exactly where you left them Unlike real cats
<quiet-marquee 
  label="A well-controlled cat parade" 
  without-pause-on-hover
  without-controls
>
  <span>Press the button to give the cats a break</span>
  <quiet-icon name="paw"></quiet-icon>
  <span>They will wait exactly where you left them</span>
  <quiet-icon name="hourglass"></quiet-icon>
  <span>Unlike real cats</span>
  <quiet-icon name="heart"></quiet-icon>
</quiet-marquee>

Auto-playing content that lasts more than five seconds must have a visible way to pause it, per WCAG 2.2.2 . If you use without-controls, provide your own pause control.

Looping a set number of times Jump to heading

Use the loop attribute to stop after a specific number of cycles. One cycle is one full pass of the content. The marquee dispatches quiet-animation-complete when it finishes, and you can call restart() to play it again. The default, 0, loops forever.

Marquees never animate while scrolled out of view, so the laps don't start counting until the marquee enters the viewport and won't tick down while it's offscreen.

Three laps around the living room, then a nap 🏁 Restart
<div id="marquee__loop">
  <quiet-marquee loop="3" speed="120" label="Three lap sprint">
    <span>Three laps around the living room, then a nap</span>
    <span>🏁</span>
  </quiet-marquee>
  <quiet-button>Restart</quiet-button>
</div>

<script>
  const container = document.getElementById('marquee__loop');
  const marquee = container.querySelector('quiet-marquee');
  const button = container.querySelector('quiet-button');

  marquee.addEventListener('quiet-animation-complete', () => {
    console.log('The marquee finished looping');
  });

  button.addEventListener('click', () => {
    marquee.restart();
  });
</script>

<style>
  #marquee__loop {
    quiet-marquee {
      margin-block-end: 1rem;
    }
  }
</style>

More examples Jump to heading

Marquees show up in more places than you might think. The classic "trusted by" logo wall renders its logos desaturated until you hover, which pairs nicely with the marquee pausing while you point at it.

<quiet-marquee id="marquee__logos" speed="40" label="Tools we love">
  <quiet-icon name="brand-github"></quiet-icon>
  <quiet-icon name="brand-figma"></quiet-icon>
  <quiet-icon name="brand-firefox"></quiet-icon>
  <quiet-icon name="brand-docker"></quiet-icon>
  <quiet-icon name="brand-chrome"></quiet-icon>
  <quiet-icon name="brand-gitlab"></quiet-icon>
  <quiet-icon name="brand-vscode"></quiet-icon>
  <quiet-icon name="brand-slack"></quiet-icon>
</quiet-marquee>

<style>
  #marquee__logos {
    --gap: 3rem;

    quiet-icon {
      opacity: 0.6;
      filter: grayscale(1);
      font-size: 3rem;
      transition:
        200ms filter ease,
        200ms opacity ease;

      &:hover {
        opacity: 1;
        filter: none;
      }
    }
  }
</style>

You can also style the marquee as a full-width promo bar. Links work inside it, and tabbing to one pauses the marquee so keyboard users can activate it. On a colored background, style links and their focus rings so they stay visible.

Complimentary shipping on orders over $50 · The autumn collection has arrived Discover · Now open: our atelier in Whiskerville ·
<quiet-marquee id="marquee__announcement" speed="20" label="Announcements">
  <span>Complimentary shipping on orders over $50</span>
  <span class="separator">·</span>
  <span>The autumn collection has arrived <a href="#">Discover</a></span>
  <span class="separator">·</span>
  <span>Now open: our atelier in Whiskerville</span>
  <span class="separator">·</span>
</quiet-marquee>

<style>
  #marquee__announcement {
    border-radius: var(--quiet-border-radius-md);
    background-color: light-dark(#1a1a1a, #f5f2ec);
    color: light-dark(#f5f2ec, #1a1a1a);
    font-size: 0.8125rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;

    .separator {
      opacity: 0.5;
    }

    a {
      color: inherit;
      text-decoration: underline;
      text-underline-offset: 0.25em;

      &:focus-visible {
        outline: 2px solid currentColor;
        outline-offset: 2px;
      }
    }
  }
</style>

Keyboard support Jump to heading

Tab to the marquee's pause button, then use the keys below.

Key Action
Space / Enter Pauses or resumes the marquee.

API Jump to heading

Importing Jump to heading

The autoloader is the recommended way to import components but, if you prefer to do it manually, the following code snippets will be helpful.

CDN Self-hosted

To manually import <quiet-marquee> from the CDN, use the following code.

import 'https://cdn.quietui.org/v6.0.0/components/marquee/marquee.js';

To manually import <quiet-marquee> from a self-hosted distribution, use the following code. Remember to replace /path/to/quiet with the appropriate local path.

import '/path/to/quiet/components/marquee/marquee.js';

Slots Jump to heading

Marquee supports the following slots. Learn more about using slots

Name Description
(default) The content to scroll. The marquee clones it as needed to fill the loop, hiding the clones from assistive devices so the content is only announced once.

Properties Jump to heading

Marquee has the following properties that can be set with corresponding attributes. In many cases, the attribute's name is the same as the property's name. If an attribute is different, it will be displayed after the property. Learn more about attributes and properties

Property Description Reflects Type Default
label A custom label for the marquee. This won't be displayed on the screen, but it will be announced by assistive devices. string ''
orientation The direction content scrolls. 'horizontal' | 'vertical' 'horizontal'
reverse Reverses the scroll direction. boolean false
speed The scrolling speed in pixels per second. number 50
paused Pauses the marquee. boolean false
loop The number of times the marquee loops before stopping. Use 0, the default, to loop forever. number 0
withoutPauseOnHover
without-pause-on-hover
By default, the marquee pauses while the user hovers over it. Use this attribute to disable that behavior. boolean false
withoutFade
without-fade
By default, content fades in and out at the marquee's edges. Use this attribute to remove the fade. boolean false
withoutControls
without-controls
Removes the built-in pause button. If you do this, make sure to provide another visible way to pause the marquee so it remains accessible. boolean false
ignoreReducedMotion
ignore-reduced-motion
By default, no animation will occur when the user indicates a preference for reduced motion. Use this attribute to override this behavior when necessary. boolean false

Methods Jump to heading

Marquee supports the following methods. You can obtain a reference to the element and call them like functions in JavaScript. Learn more about methods

Name Description Arguments
restart() Restarts the marquee from the beginning, e.g. after a finite number of loops has finished.

Events Jump to heading

Marquee dispatches the following custom events. You can listen to them the same way was native events. Learn more about custom events

Name Description
quiet-animation-complete Emitted when a finite number of loops has finished playing. Only emitted when the loop attribute is set to a positive number.

CSS custom properties Jump to heading

Marquee supports the following CSS custom properties. You can style them like any other CSS property. Learn more about CSS custom properties

Name Description Default
--gap The space between items and between each repetition of the content. 1rem
--fade-size The size of the fade mask at the marquee's edges. Use the without-fade attribute to remove the fade entirely. 2rem

CSS parts Jump to heading

Marquee exposes internal elements that can be styled with CSS using the selectors shown below. Learn more about CSS parts

Name Description CSS selector
content The container that clips the scrolling content. ::part(content)
track The moving container that holds the content and its clones. ::part(track)
pause-button The pause/play toggle button, a <button> element. ::part(pause-button)

Custom States Jump to heading

Marquee has the following custom states. You can target them with CSS using the selectors shown below. Learn more about custom states

Name Description CSS selector
paused Applied when the marquee isn't scrolling for any reason, including the paused attribute, hovering, focus, reduced motion, and being scrolled out of view. :state(paused)

Dependencies Jump to heading

Marquee automatically imports the following elements. Sub-dependencies are also included in this list.

Search this website Toggle dark mode View the code on GitHub Follow @quietui.org on Bluesky Follow @quiet_ui on X

    No results found