[CSS Marquee] テキストをスクロールさせるCSS

かつて、電光掲示板のようにテキストをスクロールさせることができる、marquee要素 というものがありました。

今回は、現在の HTML の仕様では非推奨になっている、その marquee要素 を、CSS で再現してみます。

完成したもの

See the Pen CSS Marquee by nov (@numerofive) on CodePen.

2種類の CSS Marquee を作ってみました。一つはコンテナ要素の子要素をスクロールさせるもの、もう一つはコンテナ要素に生成した擬似要素をスクロールさせるものです。

子要素をスクロールさせる CSS Marquee の解説

…
<style>
@keyframes MARQUEE {
  0% {
    transform: var(--1_transform);
  }
  100% {
    transform: var(--2_transform);
  }
}
[class*="__marquee__"] {
  & {
    --1_transform: translateX(0);
    --2_transform: translateX(-100%);
    --duration: 8s;
    --play-state: running;
    border: solid 1px #808080;
    border-radius: 4px;
    box-sizing: border-box;
    display: flex;
    overflow: hidden;
    padding-block: calc(0.5lh - 0.5em);
  }
  &:dir(rtl) {
    --2_transform: translateX(100%);
  }
  &:is(:hover, :focus-visible) {
    --play-state: paused;
  }
  & > * {
    & {
      animation: MARQUEE var(--duration) linear infinite both var(--play-state);
      display: block;
      flex: none;
      margin: 0;
      padding-inline: 100% 0;
    }
    &:has(> li) {
      display: flex;
      column-gap: 2em;
    }
    & > li {
      display: block;
      flex: none;
      margin: 0;
    }
  }
}
</style>
…
<div class="__marquee__" style="--duration: 12s;">
  <p>…</p>
</div>
…
<div class="__marquee__" style="--duration: 52s;">
  <ul>
    <li>…</li>
    …
  </ul>
</div>
…

コンテナ要素 ([class*="__marquee__"]) の中には子要素が1つあります。この子要素がスクロールするアニメーションを違和感無くループさせるためには、元の位置に戻すための、完全に消える瞬間を作る必要があります。

最初の状態では padding-inline: 100% 0; によって文字列が表示領域外に押し出されています。そこから transformプロパティの translateX() で反対の表示領域外までスクロールして最初の状態に戻ります。

 

擬似要素をスクロールさせる CSS Marquee の解説

…
<style>
@keyframes MARQUEE {
  0% {
    transform: var(--1_transform);
  }
  100% {
    transform: var(--2_transform);
  }
}
[class*="__marquee__"][style~="--content:"] {
  & {
    --1_transform: translateX(0);
    --2_transform: translateX(-100%);
    --duration: 8s;
    --play-state: running;
    border: solid 1px #808080;
    border-radius: 4px;
    box-sizing: border-box;
    display: flex;
    overflow: hidden;
    padding-block: calc(0.5lh - 0.5em);
  }
  &:dir(rtl) {
    --2_transform: translateX(100%);
  }
  &:is(:hover, :focus-visible) {
    --play-state: paused;
  }
  & > * {
    block-size: 1px;
    border: none;
    clip-path: inset(50%);
    inline-size: 1px;
    margin: 0;
    padding: 0;
    position: absolute;
  }
  &::before,
  &::after {
    animation: MARQUEE var(--duration) linear infinite both var(--play-state);
    box-sizing: border-box;
    content: var(--content);
    display: block;
    flex: none;
    min-inline-size: 100%;
    padding-inline: 0 2em;
  }
}
</style>
…
<div class="__marquee__" style="--duration: 8s; --content: '…';">
  <p>…</p>
</div>
…

コンテナ要素 ([class*="__marquee__"][style~="--content:"]) の中の子要素をスクリーンリーダーの読み上げ用として非表示にし、子要素内の文章と同じ文字列を設定したCSS変数 --content::before擬似要素と::after擬似要素で表示してスクロールさせます。

擬似要素を2つ使う理由は、文字列の左端が消え始める要素と現れ始める要素に分けるためです。

擬似要素には min-inline-size: 100%; が設定してあるため、最初の状態では::after擬似要素は::before擬似要素に表示領域外に押し出されています。::after擬似要素が::before擬似要素のあった場所までスクロールさせて最初の状態に戻ります。

 

使うときのポイント

紹介した2種類の CSS Marquee は、どちらも --durationスクロールする時間 = スクロールの速さ を設定できますが、表示領域の幅や、スクロールする文字数、更には文字のフォントやサイズによって、スクロールの速さは変わります。

ある幅のときのスクロールする速さを求めるには、次の式で算出できます。

子要素・擬似要素の幅 / 表示領域の幅 * アニメーション1回にかける時間

使うときの注意点

スクロールさせる文字列の一部にリンク等を設定し、Tabキー でフォーカスした場合、フォーカスによって移動した要素がフォーカス解除時に元に戻らず、アニメーションにずれが生じる場合があります。リンクを設定したい場合は、CSS Marquee 全体をリンクにするしかなさそうです。

marquee要素 は1995年に公開された Internet Explorer 2 で使えるようになった、30年近く前の独自要素なんですよね。子供がおじさん・おばさんになってしまわれる程の歳月!

関連記事