<!--
`marquee` class turns elements into <marquee>-like elements
`data-text` attribute is used to define <marquee> text content
-->
<figure class="marquee marquee--navy" data-text="Like a &lt;marquee&gt; element">
  <!-- A visually-hidden `<span>` allows screen reader to still read content -->
  <span class="sr-only">Like a &lt;marquee&gt; element</span>
</figure>

<!-- `marquee--reverse` class reverses the scroll animation -->
<figure class="marquee marquee--reverse marquee--beet" data-text="Powered by some CSS">
  <span class="sr-only">Powered by some CSS</span>
</figure>
<figure class="marquee marquee--flamingo" data-text="Like a &lt;marquee&gt; element">
  <span class="sr-only">Like a &lt;marquee&gt; element</span>
</figure>
<figure class="marquee marquee--reverse marquee--ochre" data-text="Powered by some CSS">
  <span class="sr-only">Powered by some CSS</span>
</figure>
<figure class="marquee marquee--butter" data-text="Like a &lt;marquee&gt; element">
  <span class="sr-only">Like a &lt;marquee&gt; element</span>
</figure>
<figure class="marquee marquee--reverse marquee--mantis" data-text="Powered by some CSS">
  <span class="sr-only">Powered by some CSS</span>
</figure>
/**
 * Core, likely what you want to copy/paste with little change
 */
.marquee {
  overflow: hidden;
  display: block;
}

.marquee::before {
  display: block;
  white-space: pre;
  /* Abusing `content` property and `attr` function to duplicate content in CSS */
  content: attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text) "        " attr(data-text);
  width: fit-content;
  transform: translate3d(-2%, 0, 0);
  will-change: transform;
}

/**
 * Only run animations when reduced motion is not set
 * Thanks @patrick_h_lauke@mastodon.social for the heads up!
 */
@media (prefers-reduced-motion: no-preference) {
  .marquee::before {
    animation: marquee 6s linear infinite;
  }
  
  .marquee--reverse::before {
    animation-direction: reverse;
  }
}

@keyframes marquee {
	0% {
    /**
     * -2% is used to start scrolling with an offset (visual only)
     */
		transform: translate3d(-2%, 0, 0);
	}
	100% {
    /**
     * -2% offset has to be preserved
     *
     * Content is duplicated 20 times
     * 100% / 20 = 5 % = "1 content length"
     * 
     * -1px seems to prevent a small jump between each loop
     */
		transform: translate3d(calc(-2% - 5% - 1px), 0, 0);
	}
}

/**
 * From here, style marquee however you'd like
 */
.marquee {
  margin: 0;
  display: flex;
  align-items: center;
  font-size: clamp(1rem, 4vh, 3rem);
  flex: 1;
}

.marquee--navy {
  background: #54669c;
}
.marquee--beet {
  background: #a54a5e;
}
.marquee--flamingo {
  background: #e84311;
}
.marquee--ochre {
  background: #f27502;
}
.marquee--butter {
  background: #ffb005;
}
.marquee--mantis {
  background: #759f53;
}

body {
  margin: 0;
  font-family: sans-serif;
  background: #fffefe;
  color: #1f1919;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.