Arc marquee
A line of text runs without end along a wide arc, the edge of a circle much larger than the screen: a dome over what follows, or a bowl under it.
Documentation
-
Copy to Webflow
Add Instrument Sans and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: Webflow removes any font your site has not installed. -
Add custom CSS
Custom CSS in WebflowThis CSS already ships inside the Webflow embed, so a pasted section needs nothing added. Paste it yourself, in the page or the site custom code, only if the effect runs on several pages. The tutorial explains where.
See the tutorialThe type of the arc is the type of the element: set its font, size, weight, tracking and colour on its class. Its height follows from the arc, so give it none. While the scroll bends the arc deeper, a dome reaches below its box and a bowl above it: leave some air there, or setflexto 0. -
Add custom JS
Custom JavaScript in WebflowThis script already ships inside the Webflow embed, settings block included. You paste it yourself, before the closing body tag of the page or the site custom code, only when the effect has to run on several pages, and the tutorial says how to choose.
See the tutorial -
Use it on your own elements
How to add a custom attribute in WebflowA custom attribute is a pair, a name and a value, typed in the Settings panel of the element you selected, under Custom attributes. The name is what the script looks for, the value is what it reads. The tutorial shows the panel.
See the tutorialCustom attributesSelect the element on the canvas and open the Settings panel. Under Custom attributes, add a pair: the name on the left, the value on the right.Custom attribute What it does data-arc-marquee Goes on the heading or the paragraph whose words run along the arc, and takes no value: leave the value field empty. That element becomes the box of the arc, as wide as it is and as tall as the arc needs, and its font, size, weight and colour are the arc's. The settings below go on the same element. data-arc-marquee-item Marks one phrase inside that element, and takes no value. The engine draws the separator mark between two phrases. Without any item, the whole text of the element is one phrase. [ Optional ] data-arc-marquee-source Written by the engine on the span that keeps the words as written, hidden from sight only so a screen reader reads them once. Never written by hand; destroy() gives the words back. [ Optional ] data-arc-marquee-svg Written by the engine on the drawing it adds inside the element, hidden from screen readers. Never written by hand. [ Optional ] data-arc-marquee-state Written by the engine on the element: running, paused while it is off screen, still under reduced motion or when there is nothing to draw. Never written by hand, and a page can style on it. [ Optional ] SettingsEvery setting is a custom attribute too, added the same way and on the same element. An attribute always wins over the defaults written in the code.Attribute Default What it is data-arc-marquee-accent currentColor The colour of the mark between two phrases. Options: any CSS colour; currentColor takes the colour of the text. data-arc-marquee-mode auto What moves the words. Options: auto (they run on their own and the scroll speeds them up), scroll (they move with the page only, and stand still when it does). data-arc-marquee-direction left Which way the words run along the arc. Options: left, right. data-arc-marquee-speed 70 How fast the words run on their own, in pixels a second, in auto mode. data-arc-marquee-boost 1 How much of the scroll is handed to the words, as a factor. In scroll mode, the pixels of travel for each pixel scrolled. data-arc-marquee-max-speed 2000 The ceiling, in pixels a second; also the speed at which the arc reaches its full extra bend. data-arc-marquee-ease 0.1 How quickly the words and the bend take up the scroll, 0 to 1. data-arc-marquee-bend 0.12 The rise of the arc at rest, as a share of the width of the element: how much higher its middle stands than its two ends. Negative turns the dome into a bowl. data-arc-marquee-flex 1.2 How much deeper the arc bends at top speed, in times its resting bend. data-arc-marquee-reverse true Whether scrolling up sends the words back the other way, in auto mode. Options: false (the page only ever pushes them forward), true. data-arc-marquee-separator ✦ The mark drawn between two phrases, any text. Options: one character like ✦, ◆ or /, a word, or empty for no mark. data-arc-marquee-gap 0.45 The space on either side of the mark, in ems of the text.
Two files and their pictures (3), no build step.
It runs in the browser only. Load it with a script tag in plain HTML, in a
client-side script in Astro, never in the frontmatter. In React, call
ArcMarquee.mount(ref.current) in useEffect and
destroy() in its cleanup.
-
Tune it, either way
Per element with an attribute, or once for the whole site by editingDEFAULTSat the top of the file.Attribute Default What it is Lower Higher data-arc-marquee-accent currentColor The colour of the mark between two phrases. Options: any CSS colour; currentColor takes the colour of the text. data-arc-marquee-mode auto What moves the words. Options: auto (they run on their own and the scroll speeds them up), scroll (they move with the page only, and stand still when it does). data-arc-marquee-direction left Which way the words run along the arc. Options: left, right. data-arc-marquee-speed 70 How fast the words run on their own, in pixels a second, in auto mode. Nearly still between two scrolls Runs on briskly with no help data-arc-marquee-boost 1 How much of the scroll is handed to the words, as a factor. In scroll mode, the pixels of travel for each pixel scrolled. The scroll barely shows in the words A flick of the wheel throws them along the arc data-arc-marquee-max-speed 2000 The ceiling, in pixels a second; also the speed at which the arc reaches its full extra bend. A calm line that refuses to race, and bends at the slightest pull A line that can turn into a blur, and needs a hard scroll to bend data-arc-marquee-ease 0.1 How quickly the words and the bend take up the scroll, 0 to 1. Long and heavy, keeps going after the scroll stops Glued to the scroll, close to nervous data-arc-marquee-bend 0.12 The rise of the arc at rest, as a share of the width of the element: how much higher its middle stands than its two ends. Negative turns the dome into a bowl. Flatter, 0 is a straight line; -0.14 a bowl A deeper dome, up to 0.4 data-arc-marquee-flex 1.2 How much deeper the arc bends at top speed, in times its resting bend. 0 keeps the arc as it is; negative values flatten it while the scroll pulls The arc curls hard as soon as the page moves data-arc-marquee-reverse true Whether scrolling up sends the words back the other way, in auto mode. Options: false (the page only ever pushes them forward), true. data-arc-marquee-separator ✦ The mark drawn between two phrases, any text. Options: one character like ✦, ◆ or /, a word, or empty for no mark. data-arc-marquee-gap 0.45 The space on either side of the mark, in ems of the text. Phrases and marks close together A wide pause between two phrases -
Drive it from JavaScript
When the arc has to stop and start on its own, when its words change after load, or when the page reacts to its speed.const element = document.querySelector('[data-arc-marquee]'); element.addEventListener('arcmarquee:ready', (e) => { console.log(e.detail); }); const arc = ArcMarquee.mount(element); // the element auto() mounted, or a new one arc.pause(); // out of the frame loop, and arc.play() back into it arc.speed(); // px/s along the arc now; arc.bend() the bend of the moment arc.measure(); // read the words and the width again, after a change of your own // Text added after the page loaded: mount everything marked inside it. ArcMarquee.auto(document.body); // When the arc leaves the page: ArcMarquee.destroy(element); // removes the drawing, gives the words back
-
Fit it to your page
- Mark the text, not the section:
data-arc-marqueegoes on the heading or the paragraph itself. Its font, size, weight, tracking and colour are the arc's, so a class on that element styles the arc. A page can hold as many as it likes, each with its own settings. - Write the words once. Mark each phrase
data-arc-marquee-itemto get a mark between them, or leave the text as it is for a single phrase. The engine repeats the phrases as many times as the arc needs, and counts again when the width, the font or the words change. - The arc spans the element's width. Give the element the full width of the page for an arc that leaves both edges of the screen, which is what makes it read as the edge of a much larger circle. Its height follows: the rise of the arc plus room for the letters, with no height to set.
- Dome or bowl: a positive
bendraises the middle (a dome, with the letters on its outer side), a negative one lowers it (a bowl, the letters standing inside it). - Auto or scroll:
autoruns on its own and the scroll adds to it, up tomaxSpeed;scrollmoves only while the page moves, byboostpixels for each pixel scrolled, and goes back when the reader scrolls back. In scroll mode the words sit in their resting place when the element is in the middle of the window. - Settings go on the element, as more attributes:
data-arc-marquee-mode="scroll",data-arc-marquee-bend="-0.14". Three levels, least specific first:DEFAULTS, thenwindow.ArcMarqueeSettings = { speed: 90 }declared before the engine, then the element's own attributes. - From JavaScript:
ArcMarquee.mount(el, overrides)returns{ element, settings, speed(), bend(), period(), measure(), play(), pause(), destroy() }, and hands back the existing handle for an element already mounted.ArcMarquee.destroy(el),ArcMarquee.auto(root)andArcMarquee.defaultscomplete it. Events, both bubbling, withelementin thedetail:arcmarquee:ready(period,mode,still) andarcmarquee:measure(period,rounds,width,reason). - Screen readers read the words once. The words as written stay in the element, hidden from sight only, and the drawing is
aria-hidden. A heading stays a heading with its own text. - It costs nothing off screen. An
IntersectionObservertakes an arc out of the frame loop the moment it leaves the window, and the loop stops when the last one leaves. - **Under
prefers-reduced-motion: reduce** the arc is drawn at rest and nothing moves, neither with time nor with the scroll. The media query is listened to, so turning the setting on or off answers without a reload. - Touch: a finger scrolling the page drives the arc exactly like a wheel. Nothing answers the pointer itself, and the drawing lets clicks through.
- The words of the markup are the preview's, and they ship as they are: pasted, the page reads the way the preview did. Put the user's own words in their place whenever they want, in the page's language.
- On Webflow, add the fonts first. Add Instrument Sans and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: the paste names them, and the Designer drops a font the site does not have.
- Mark the text, not the section:
-
Avoid the pitfalls
- The stylesheet matters. Without
arc-marquee.cssthe source words are not hidden, so they show twice, and the drawing does not take the width of its element. The engine says so in the console after a second and a half when it measures nothing. - The arc runs past its box while the scroll bends it. The box is laid out for the arc at rest; a dome that bends deeper reaches below its box, a bowl above it, over whatever sits there, and comes back when the scroll stops. The sides are clipped (
overflow-x: clip), the top and the bottom are not. Leave some air above a bowl and below a dome, or setflexto0. - **Anything placed to meet the arc depends on
bend.** The preview pulls a photograph up under the dome by its resting rise (margin-top: calc(0.2em - 16vw)for a bend of0.16), and the bowl up under a window (margin-top: calc(-14vw + 0.25em)for a bend of-0.14). Change the bend and change those margins with it. - Plain text only on the arc. The phrases are read as text: links, images or icons inside the element stay in the hidden source for screen readers and are not drawn. A link belongs next to the arc, not inside it.
- One line. Line breaks and runs of spaces in the words are read as a single space.
- The type is the element's. Set the size in your class, in any unit;
clamp()works and the arc follows when the window is resized. - **An element with
display: inline** getsdisplay: blockfrom the component once mounted, so it takes the width of its parent. - A pointer state never survives the paste (AB-41): a colour change on hover lives in the CSS of the component or in your own class, never in a Webflow class variant.
- The stylesheet matters. Without
One click on Copy the AI prompt copies everything an assistant needs to build this effect: the complete code, the markup, the dependency, every setting and every pitfall.
What it does
Arc marquee is a text animation for Webflow and vanilla JavaScript, with no library. Also called curved text marquee, text on a path animation or arc text scroll animation.
A line of text runs without end along a wide arc, the edge of a circle much larger than the screen: a dome over what follows, or a bowl under it. On its own it cruises at a steady speed and the scroll throws it faster, or back the other way when the reader scrolls up; in scroll mode it moves with the page only, like a scrubbed timeline. In both modes the arc bends deeper while the scroll pulls and settles back when it stops. The words are written once and stay real text: the engine keeps them as written for screen readers, which read them once, and draws them along an SVG path as many times as the arc needs, with the kerning and the tracking of the page. The repeat is seamless and measured from the page itself, nothing runs off screen, and under reduced motion the arc stands still. Pattern type curved marquee, text on a path, arc text ticker, scroll velocity text.
Updated
Questions
Select a heading or a paragraph that spans the full width of the page, open the Settings panel and add the custom attribute data-arc-marquee with an empty value. Wrap each phrase in a span marked data-arc-marquee-item to get a mark between them; the engine draws the words along an arc as wide as the element and repeats them as often as the arc needs.
Yes: set data-arc-marquee-mode="scroll" and the words move with the page only, by boost pixels for each pixel scrolled, and go back when you scroll back. The default, auto, runs on its own at speed pixels a second, 70 by default, and the scroll throws it faster or, with reverse on, back the other way.
Give data-arc-marquee-bend a negative value. The bend is the rise of the middle of the arc as a share of the element's width, 0.12 by default: positive is a dome with the letters on its outer side, negative a bowl with the letters standing inside it, and 0 a straight line.
That is flex: at top speed the arc reaches 1 + flex times its resting bend, 2.2 times with the default of 1.2, and settles back when the scroll stops. Set data-arc-marquee-flex="0" to keep the arc as it is, or a negative value to flatten it while the page moves.
No. The words as written stay in the element, hidden from sight only, and are read once; the drawing of the arc, with its repeated copies, is hidden from screen readers. A heading marked with data-arc-marquee stays a heading with its own text, and the arc stands still when the reader asks for reduced motion.