Marquee is a visual effect where text automatically scrolls horizontally within a confined area, allowing long strings to be displayed in a compact container. Historically, the term comes from the HTML tag <marquee>, popular on the web in the 1990s. In modern mobile development, scrolling text is used in players, news tickers, and status bars. According to Apple Human Interface Guidelines and Google Material Design, marquee is only acceptable in limited scenarios where static truncation with ellipsis degrades UX.
Key Takeaways
Marquee is a UI effect where text moves horizontally within a fixed area, cyclically scrolling so that the user can read all content that does not fit in the container. Unlike ellipsize, which hides part of the text, marquee shows the entire text, but in motion.
The term marquee became established thanks to the HTML tag <marquee>, which was added in early versions of Netscape and Internet Explorer browsers. The tag allowed creating scrolling text without JavaScript or CSS, but was never part of the HTML standard. Modern browsers continue to support it for backward compatibility, but its use is not recommended.
In mobile development, marquee solves the problem of long labels in compact containers: track names in a player, currency tickers, news tickers on TV screens. However, due to animation, it creates a burden on accessibility and may annoy users — therefore it is used selectively.
According to Nielsen Norman Group research (2023), moving text reduces reading speed by 15–20% and increases cognitive load. Use marquee only when static truncation (ellipsize) is truly unacceptable.
On Android, marquee is implemented by the built-in TextView mechanism through the android:ellipsize="marquee" attribute. This is the only ellipsize mode that does not truncate text but animates its scrolling.
To activate marquee, three conditions must be met: singleLine="true" (or maxLines="1"), ellipsize="marquee", and setSelected(true) in code. Without calling setSelected(), the animation will not start.
<!-- XML layout -->
<TextView
android:id="@+id/marquee_text"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever" />
// Kotlin: activate marquee
textView.setSelected(true)
textView.isSingleLine = true
The marqueeRepeatLimit attribute determines the number of animation repeats: marquee_forever (infinite) or a specific number. The default is marquee_forever. If the text is shorter than the container width, the animation does not start — TextView displays static text.
An important feature: setSelected(true) only works when the TextView has focus or is in selected mode. In RecyclerView, this can cause problems — animation only starts for one item. The solution is to use a custom MarqueeTextView that overrides isFocused() and always returns true.
Starting with Android 9 (API 28), Google improved marquee behavior — the animation became smoother and does not freeze when scrolling a list. However, on older devices, marquee may stutter, especially at high pixel densities.
iOS has no built-in equivalent of android:ellipsize="marquee". Marquee is implemented manually using Core Animation or SwiftUI animation. The most common approach is animating layer.position combined with a clipping container.
In UIKit, marquee is built from three elements: a UILabel container with clipsToBounds = true, the text itself, and a CABasicAnimation for shifting the text along X with infinite repetition and autoreverses = false, so the line scrolls in one direction and jumps back.
func startMarquee(for label: UILabel) {
label.clipsToBounds = true
label.numberOfLines = 1
let textWidth = (label.text as? NSString)?
.size(withAttributes: [.font: label.font as any]).width ?? 0
guard textWidth > label.bounds.width else { return }
let animation = CABasicAnimation(keyPath: "position.x")
animation.fromValue = label.layer.position.x + label.bounds.width / 2
animation.toValue = label.layer.position.x - textWidth - label.bounds.width / 2
animation.duration = CFTimeInterval(textWidth / 60)
animation.repeatCount = .infinity
animation.autoreverses = false
label.layer.add(animation, forKey: "marquee")
}
Calculating duration = textWidth / 60 gives a speed of approximately 60 pixels per second — a comfortable reading pace. If the speed exceeds 120 px/s, the text becomes unreadable. If below 30 px/s, the animation looks sluggish. Adjust the speed for your specific scenario.
In SwiftUI, marquee is implemented more simply — using the offset() modifier with repeatable animation and .animation(.linear(duration:).repeatForever(autoreverses: false)). SwiftUI automatically manages the animation lifecycle but requires GeometryReader to determine the text width.
On the web, the deprecated <marquee> tag is not recommended for use, although browsers continue to support it. The modern implementation of marquee is CSS animation with keyframes and transform: translateX.
Advantages of the CSS approach: full control over speed, pause, and animation curve, support for prefers-reduced-motion for accessibility, and the absence of a semantically incorrect tag.
.marquee {
overflow: hidden;
white-space: nowrap;
width: 300px;
}
.marquee-content {
display: inline-block;
animation: marquee 10s linear infinite;
}
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-100%); }
}
/* Pause on hover */
.marquee-content:hover {
animation-play-state: paused;
}
The key trick: display: inline-block on the content ensures that the element’s width equals the text width, and translateX(-100%) shifts it exactly by that width to the left. If the content is display: block, translateX(-100%) will equal the container width, not the text width, and the animation will break.
For accessibility, add @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } } — users with vestibular disorders will see static text with scrolling capability.
Marquee is justified in scenarios where the user cannot expand the container themselves and the full text is critically important: the current track in a player on a locked screen, a news ticker on a TV panel, or a marquee in a ride-hailing app (order status).
Marquee should not be used in tables, lists, forms, and dialogs — anywhere the user actively interacts with the interface. Animation distracts from the goal, increases element search time, and creates discomfort. According to WebAIM (2024), 1 in 3 users with vestibular disorders report discomfort when viewing animated text.
A rule of thumb: if the text can be shortened or wrapped to the next line — do it. Marquee is a last resort, not a universal solution for long labels.
Marquee is one of the most problematic UI animations from an accessibility standpoint. Moving text can cause dizziness, nausea, and disorientation in people with vestibular disorders. According to WCAG 2.2, any animation lasting more than 5 seconds must have a pause or disable mechanism.
On iOS, use the UIAccessibility.isReduceMotionEnabled property to check the system setting. If reduceMotion is enabled — do not start the animation, show static text with ellipsize.
On Android, check the system animation via Settings.Global.ANIMATOR_DURATION_SCALE. If global animation is disabled (duration scale = 0), marquee should not be enabled.
On the web, use the prefers-reduced-motion media feature: @media (prefers-reduced-motion: reduce) { .marquee-content { animation: none; } }. This is a standard CSS feature supported by all modern browsers. Check in DevTools: enable prefers-reduced-motion emulation and verify that the animation turns off.
Frequently Asked Questions
Ellipsize truncates text and adds an ellipsis, hiding part of the content. Marquee scrolls the entire text, but in motion. The choice depends on the priority: show everything (marquee) or maintain static display (ellipsize).
The built-in android:ellipsize="marquee" does not allow changing speed — it is determined by the system ScrollSpeed. For custom speed, use Animation or ValueAnimator manually.
Due to view recycling, setSelected(true) gets reset. Solution: create a custom MarqueeTextView that overrides isFocused() and returns true, or use custom animation in Adapter.onViewAttachedToWindow().
Yes, using translateY instead of translateX. Vertical marquee is used for credits or news tickers in vertical feeds. The principle is the same, but the animation axis changes.
In CSS: :hover { animation-play-state: paused; }. On Android: track focus via OnFocusChangeListener and reset setSelected(false). On iOS: remove the animation via layer.removeAnimation(forKey:).
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also