Media

Marquee

Content scrolling sideways without end, in CSS alone: seamless, paused on hover, still when motion is reduced.

#animation#marketing#media#no-js

Demo

Preview
FastAccessibleNo build stepOne fileDark modeKeyboard firstForms that post
111222333444555666777888999111012211332

The first pauses under the pointer; the second runs right and never stops.

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/marquee.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/marquee.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/marquee.qwe
let marquee_demo_words = ["Fast", "Accessible", "No build step", "One file", "Dark mode", "Keyboard first", "Forms that post"]view MarqueeDemo() =    <div.demo-stack>        <Marquee speed={18} label="What people say">            {for w in marquee_demo_words => <span style="padding: 6px 14px; border: 1px solid var(--ui-border); border-radius: 999px; background: var(--ui-subtle)">{w}</span>}        </Marquee>        <Marquee direction="right" speed={30} gap={24} pause={false} label="Numbers">            {for n in range(1, 13) => <span style="font: 600 22px var(--ui-mono); color: var(--ui-muted)">{n * 111}</span>}        </Marquee>        <p.demo-note>The first pauses under the pointer; the second runs right and never stops.</p>    </div>

Source

ui/marquee.qwe
// Marquee: content that scrolls sideways without end: logos, headlines, a line// of tags. Pure CSS: the content is drawn twice and slid by its own width, so// the loop has no seam. It pauses while the pointer or the focus is on it, and// for people who ask their system for less motion it does not move at all (it// can be scrolled by hand instead).////   <Marquee speed={25}>//       {for l in logos => <img src={l.src} alt={l.name} height="32"/>}//   </Marquee>//   <Marquee direction="right" gap={64} label="Partners">...</Marquee>//// children: what scrolls (any elements; they are laid in a row).// speed: seconds for one full loop (20); less is faster. direction: "left"// (default) or "right". gap: the space between items, and between the end and// the start again, in pixels (48). pause: stops while hovered or focused (true).// label: what screen readers call it ("Scrolling content"); they read the// content once, as the copy is hidden from them.import ui/theme.qwe use ui_clslet marquee_css = css`.ui-marquee {  position: relative; overflow: hidden; width: 100%; font: 14px/1.4 var(--ui-font); color: var(--ui-fg);  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);  -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);}.ui-marquee-track { display: flex; width: max-content; animation: ui-marquee var(--t, 20s) linear infinite; }.ui-marquee-right .ui-marquee-track { animation-direction: reverse; }.ui-marquee-pause:hover .ui-marquee-track, .ui-marquee-pause:focus-within .ui-marquee-track { animation-play-state: paused; }.ui-marquee-group { display: flex; flex: none; align-items: center; gap: var(--g, 48px); padding-right: var(--g, 48px); }.ui-marquee-group > * { flex: none; }@keyframes ui-marquee { to { transform: translateX(-50%); } }@media (prefers-reduced-motion: reduce) {  .ui-marquee { overflow-x: auto; mask-image: none; -webkit-mask-image: none; }  .ui-marquee-track { animation: none; }  .ui-marquee-group[aria-hidden] { display: none; }}`view Marquee(speed: num = 20, direction: str = "left", gap: int = 48, pause: bool = true, label: str = "Scrolling content",             class: str? = null, children) {    style global marquee_css    <div class={ui_cls(["ui-marquee", direction == "right" && "ui-marquee-right", pause && "ui-marquee-pause", class])}         role="marquee" aria-label={label} style={`--t: ${speed}s; --g: ${gap}px`}>        <div.ui-marquee-track>            <div.ui-marquee-group>{children}</div>            <div.ui-marquee-group aria-hidden="true">{children}</div>        </div>    </div>}
Thread
86Useful? Like it, or add to its thread below.

Thread 0 posts

On Marquee: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.