コーディングTips

WordPressの404ページを、ターミナルのエラー風にした。肉球が0を押しのけるCSSアニメ

公開 約6分で読めます

TL;DRこの記事の要点

  • 404ページを、ターミナルでcatしたときのエラー風にした
  • 0を肉球が押しのける動きは、gridで重ねてCSSだけで作った
  • URLの表示はesc_html()、動きを減らす設定の人には止まった絵
  • 作り直したら、404を返しているかをcurlで確かめる
目次7項目
  1. できあがった404ページ
  2. 404.phpでやっていること
  3. 0を肉球が押しのける仕組み
  4. 動きを止めたい人には、止まった絵で見せる
  5. ボタンは、新着記事の下に置いた
  6. 見た目を変えても、404を返しているか確かめる
  7. まとめ

このブログの404ページを、ターミナルのエラー風に作り直しました。
先に結論を書くと、「404」の真ん中の0を肉球が押しのける動きは、0と肉球をCSSのgridで同じ場所に重ねて、CSSのアニメーションだけで作っています。
JavaScriptは使っていません。

🎥 この記事の内容は動画でも解説しています。

404ページは、存在しないURLを開いたときに出るページです。
404とは何か、という基本は404とは?の記事に書いています。
この記事は、その404ページの見た目を変えたときの記録です。

できあがった404ページ

存在しないURLを開くと、黒い画面にこう出ます。

  • 大きな「404」。読み込んで1秒後に、肉球がバンッと押されて、その途中で真ん中の0が弾き飛ばされる
  • 見出しの「ページが見つかりません」と、短い説明
  • ターミナル風の3行。開いたURLをcatして、No such file or directoryと返ってきたように見せる
  • その下に、キーワード検索と新着記事。いちばん下に「トップページへ戻る」ボタン

ターミナル風の3行は、たとえば/abc/を開いたら、こう出ます。

$ cat /abc/
cat: /abc/: No such file or directory
$

最後の$のうしろでは、カーソルが点滅しています。
自分のブログはWeb制作とAIの話が中心なので、ターミナルの見た目が合うと思って、この形にしました。

404.phpでやっていること

WordPressでは、使っているテーマのフォルダ(wp-content/themes/テーマ名/)の直下に404.phpを置くと、ページが見つからないときにそれが使われます。
実装はClaude Codeにやってもらいました。

開いたURLをターミナル風の行に出すところは、こうなっています(説明のために一部を抜き出しています)。

$req = isset( $_SERVER['REQUEST_URI'] )
	? wp_parse_url( wp_unslash( $_SERVER['REQUEST_URI'] ), PHP_URL_PATH )
	: '';
$req = $req ? mb_strimwidth( $req, 0, 80, '…' ) : '/';

echo '<span class="c-notfound__prompt">$ cat ' . esc_html( $req ) . '</span>';
echo '<span class="c-notfound__error">cat: ' . esc_html( $req ) . ': No such file or directory</span>';

気をつけたのは2つです。

  • URLは、パスの部分だけを取り出して、表示幅80で省略する(長いURLで画面が崩れないように。全角は2つ分に数えられます)
  • 画面に出すときはesc_html()を通す(URLに変な文字を入れられても、HTMLとして動かないように)

URLをそのまま画面に出すと、そこにスクリプトを混ぜられるおそれがあります。
見た目のための遊びでも、ここだけは外せません。

0を肉球が押しのける仕組み

「404」は、こういうHTMLにしています。

<p class="c-notfound__code" aria-hidden="true">
  <span>4</span>
  <span class="c-notfound__slot">
    <span class="c-notfound__zero">0</span>
    <span class="c-notfound__paw"></span>
  </span>
  <span>4</span>
</p>

大事なのは、真ん中のc-notfound__slotです。
中の0と肉球を、gridで同じマスに重ねています。

.c-notfound__slot {
	display: grid;
	place-items: center;

	> * {
		grid-area: 1 / 1;
	}
}

どちらもgrid-area: 1 / 1なので、同じ場所にぴったり重なります。
position: absoluteで重ねるより、0と肉球の大きさがずれにくいのがいいところです。

動きは、こんな順番でタイミングを合わせています。

  1. 肉球: 読み込みの1秒後に、大きく傾いた状態から縮みながら着地する(押されたように見せる)
  2. 0: 肉球が縮んでいく途中で、右上へ回りながら小さくなって消える(弾き飛ばされたように見せる)
  3. 0が飛ぶのと同時に、水色の輪が広がる。数字全体も少し沈んで戻る

0を飛ばすところは、こんな形です。

.c-notfound__zero {
	animation: notfound-knock 0.5s 1.12s ease-in forwards;
}

@keyframes notfound-knock {
	to {
		opacity: 0;
		transform: translate(0.55em, -0.7em) rotate(55deg) scale(0.5);
	}
}

動かす量をemで書いているので、スマホで数字が小さくなっても、飛び方の比率は変わりません。
肉球は着地したあと、数秒おきに小さく揺れ続けます。

肉球の形は、ブログのロゴの画像をmaskで切り抜いて、白で塗っています。
画像の色に関係なく、CSSで色を決められます。

.c-notfound__paw {
	width: 100%;
	aspect-ratio: 160 / 146;
	background: #fff;
	mask: url("assets/img/icon.png") center / contain no-repeat;
	opacity: 0;
	animation: notfound-stamp 0.42s 1s cubic-bezier(0.2, 1.4, 0.4, 1) forwards;
}

@keyframes notfound-stamp {
	0% {
		opacity: 0;
		transform: scale(2.8) rotate(-12deg);
	}

	55% {
		opacity: 1;
		transform: scale(0.86) rotate(4deg);
	}

	100% {
		opacity: 1;
		transform: none;
	}
}

ここに載せたのは着地の部分だけです(揺れ続ける動きは省いています)。
最初は透明にしておいて、1秒待ってから着地させます。
0のほうは1.12秒から動き出すので、肉球が縮みきる少し前に、0が飛び始めます。

動きを止めたい人には、止まった絵で見せる

OSの設定で「視差効果を減らす」(iPhone)や「動きを減らす」(Mac)をオンにしている人には、アニメーションを止めています。

@media (prefers-reduced-motion: reduce) {
	.c-notfound__paw {
		opacity: 1;
		animation: none;
	}

	.c-notfound__zero {
		display: none;
	}
}

止めるだけだと、肉球が見えないまま0が残ってしまいます。
なので、最初から肉球を出して、0は消しておく。
動いたあとの絵が、最初から出ている状態です。

大きな「404」とターミナル風の行は、見た目のための飾りです。
どちらにもaria-hidden="true"を付けて、読み上げの対象から外しています。
ページの中身は、「ページが見つかりません」の見出しと説明から読まれます。

ボタンは、新着記事の下に置いた

最初は「トップページへ戻る」ボタンを、説明のすぐ下に置いていました。
ただ、404ページに来た人がまず探すのは、元々見たかった記事のはずです。

なので、検索と新着記事を先に出して、ボタンはいちばん下にしました。
これは自分が実際の画面を見て、決めて直してもらったところです。

見た目を変えても、404を返しているか確かめる

404ページを作り直したときに、いちばん気をつけたいのがここです。
見た目が「ページが見つかりません」でも、サーバーが200(成功)を返していると、検索エンジンには普通のページに見えてしまいます。

本番で、存在しないURLを開いたときの応答を確かめました。

$ curl -sI https://cats-hand.pro/nonexistent-xyz/ | head -1
HTTP/2 404

ちゃんと404を返しています。
404.phpはWordPressが404と判断したときに使われるテンプレートなので、普通に作れば404のままです。
ただ、プラグインや設定で変わることもあるので、作り直したら一度は確かめておくと安心です。

まとめ

404ページは、見た目でかなり遊べるページです。
ただ、来た人がしたいのは、元々見たかった記事を探すこと。
なので、演出は最初の数秒だけにして、その下に検索と新着記事を並べました。

URLを出すところはesc_html()、動きを止めたい人には止まった絵、作り直したら404を返しているかの確認。
ここだけ押さえておけば、上の飾りは好きに変えて大丈夫だと思います。

この記事が参考になったら、応援クリックをお願いします

Cat's Handこの記事を書いた人

Web制作歴10年以上。WordPressのテーマカスタマイズやコーディングを中心に、副業・本業ともにweb制作に携わっています。このブログでは、実際に手を動かして分かったことを書いています。