初めに
初めまして!未経験からエンジニアを目指している者です!
今回は自分が現在進行形で作成している作品の一部を公開します!
長くなるため概要は省きますが、現在HTML/CSS/JavaScriptのみを用いたアニメーション100本ノックと疑似サイト(まずは5つつほど用意し、レベルに応じて必要な技術や制約を増やしていく)を作成し、楽しくプログラミングをしよう!的なサイトをNext.js等を用いて公開しようと思っています!
経緯は長くなるので簡潔に話しますが、ただアニメーションを作るのではなく周りの人に少しでも参考になればいいなと思い、作成するに至りました!
今回は疑似サイトを一個公開、例を挙げます。
疑似サイトは下記のクライアント要件に沿って作成していくというものです!
🎄 クリスマス特設LP制作課題:仕様書
1. プロジェクト概要
クライアント: Aさん
目的: クリスマスシーズンのキャンペーン用LP制作
コンセプト: 訪問者がワクワクするような、少しリッチなアニメーション付きサイト
ページ構成: 1画面完結型(スクロールなし)。ファーストビューで全てを魅せる。
2. 技術要件・制約
🛠 HTML(構造・セマンティクス)
タグ: div だけでなく header, main, h1, button 等をセマンティックに使用する。
SEO対策: 以下の5つのキーワードをメタタグや本文に自然に含める。
クリスマス
2026
特集
ギフト
キャンペーン
メインコンテンツ:
画面中央にタイトルとメッセージを配置。
タイトル: 筆記体で「𝑀𝑒𝑟𝑟𝑦 𝐶ℎ𝑟𝑖𝑠𝑡𝑚𝑎𝑠」
CTAボタン: ユーザーの目を引くアクションボタン。
🎨 CSS(デザイン・装飾)
フォント: Google Fonts(Great Vibes / Playball 等)を使用し、リッチな斜体風に。
ヘッダー:
高さを抑えたコンパクトな設計。
下部にゴールドのボーダーラインを引く。
吊り下げ装飾:
ヘッダーのラインから紐が垂れ下がり、その先に星(★)がついたデザイン。
個数:6つ(オーナメント風)。
デザイン:星の上に棒(紐)がついている形。
タイトル装飾:
色:ゴールド。
配置:中央より少し上部。
効果:text-shadow等で発光感を出す。
背景: 雪が映える暗めの色、または夜空のグラデーション。
ボタンUI:
押しやすいサイズと余白(Padding)。
バウンド(跳ねる)アニメーションでクリックを誘発。
レスポンシブ: スマホ・PC両対応。
⚙️ JavaScript(動的挙動・パフォーマンス)
雪のアニメーション:
画面上部から雪を降らせる。
requestAnimationFrameおよびCSSアニメーションを使用しパフォーマンス最適化。
ただ落ちるだけでなく、左右に揺れながら落ちる演出を入れる。
星のイルミネーション:
吊り下げられた6つの星が、ランダムなタイミングで落ちてきてランダムなタイミングでピカピカ光る(明滅)。
インタラクション(画面遷移):
CTAボタンクリック → メインコンテンツ(タイトル・雪など)消失 → 「Topに戻る」ボタン出現。
「Topに戻る」クリック → 初期状態にふわっと0.5秒かけて復帰。
※完成画面 ファイル圧縮したため画質乱れてるかもです(答えはこれ限りではありません!)

途中からの再生ですがこういった感じで自分の想像力を働かせながらクライアントの要件に作成していくというものです!
今回作成するうえでHTMLの構造やCSS、JavaScriptのよりよい最適化方法や綺麗な書き方を自分なりに調べてコードに落とし込むという必要があったため、また公開するに至ってただ動くだけのコードを公開するわけにはいかなかったので疲れました(笑)
製作期間を一週間と決めてちまちま進めていましたがアニメーション考えてたら止まらなくなり、結局最後までやってしまいました。作成時間はトータル20時間ほどだと思います(昨日15時間ほど使いましたw)
長くなるためこの記事で全部は書けませんが今回の技術ポイントをいくつか書き残します!
1.HTMLのセマンティクスな構造
<header>
<div class="top">MERRY CHRISTMAS</div>
</header>
<main>
<section id="title">
<h1 class="title-name">MERRY CHRISTMAS</h1>
<div class="content-wrapper">
<p class="information">
<span aria-label="Merry Christmas 2026"
>🎄𝑀𝑒𝑟𝑟𝑦 𝐶ℎ𝑟𝑖𝑠𝑡𝑚𝑎𝑠 2026🎄</span
><br />
当サイトではクリスマスに関わる最高の特集を取り揃えております!
</p>
<p class="click-to-explore">▼ まずはこちらをクリックしてみてね ▼</p>
</div>
<div class="button-wrapper">
<button id="btn-campaign" class="btn-campaign" type="button">
キャンペーン詳細はこちら
</button>
</div>
</section>
<section id="clear">
<h2 class="clear-back">キャンペーンは終了しました</h2>
<button id="clear-btn" class="btn-clear" type="button">
TOPに戻る
</button>
</section>
</main>
-
前までは何でもdiv!div!って感じだったのでタグの構造を考えながら書くという事が学べました。
-
aria-labelを指定すると要素の中身ではなくテキストをスクリーンリーダーで読み取ってくれるのでより詳しい詳細を与えたい時に有効です。特にアイコンだけの時やa hrefでリンクを指定する際、分かりづらいと思ったところに付けてあげればアクセシブルな名前を付けることが出来ます。今回はあまり意味ないかもですがスクリーンリーダーは特殊文字を読まないことがあるらしいので付けました(CSSでフォントを指定したほうが良かったかもですね)。
ただ、あまり多用するのはよろしくないので適切に使い分けられるようにしたいですね。 -
type="button"をつけることでクリックできることを促すことが出来ます。今回はフォームのデータをサーバーに送信submit等は指定してませんが予期せぬ挙動を防ぐことが出来る為、つけておくと安全な設計になります。
2.CSS: rootを活用した可読性と保守性、 !importantの排除
- CSSは長くなるのとよりよい設計にするのはプログラミング同様答えが難しく、時間がかかるなと思いせめてもの学びを載せられたらなと思います
※一部カットしております
:root {
/* 雪用カラー */
--snow-color: rgb(183, 221, 255);
--glow-color: rgba(225, 255, 254, 0.9);
/* --- 🖌️ 2. Gradients(グラデーション) --- */
/* 背景:スポットライト風 */
--bg-dark-blue: radial-gradient(circle at center, #1a2a6c, #060053);
/* ボタン:赤~オレンジ */
--btn-grad: linear-gradient(to right, var(--color-red), var(--color-orange));
/* ボタン:光沢カラー */
--btn-shiny-grad: linear-gradient(
to top,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.5) 25%,
rgba(255, 255, 255, 0.8) 50%,
/* 真ん中を強く光らせる */ rgba(255, 255, 255, 0.3) 100%
);
/* --- ⏱️ 4. Animations(時間設定) --- */
--anim-shiny-duration: 3s; /* ボタンのキラキラ周期 */
--anim-snow-duration: 25s; /* 雪の落下の基準時間 */
/* --- 📏 5. Layout(レイアウト計算) --- */
--header-h: clamp(60px, 8vh, 100px);
--unit: min(calc(100vw / 192), calc(100vh / 108));
/* --- 📐 rotate(角度) --- */
/* ボタン: 光沢の角度 */
--btn-rotate: 170deg;
/* --- 🗺 6. Z-Index Layers (重なり順の地図) --- */
/* 固定表示 */
--z-header: 1000; /* ヘッダー(常に一番手前) */
/* コンテンツ */
--z-information: 50; /* 説明文など */
--z-title: 100; /* #title や ボタン */
/* ボタン*/
--z-btn: 10;
/* 装飾 */
--z-snow: 1; /* 雪(一番奥) */
--z-star: 5; /* 星飾り(雪より手前、コンテンツより奥) */
--z-negative: -1; /* 装飾のパーツ(紐など) */
/* 仮の遷移ページ */
--z-result: 200; /* リザルト画面(メインより手前) */
}
:rootを使うことでプログラミングでいう変数として纏めることが出来ます。
- それと名前を付ける事が出来、役割を明確にすることが出来ます。
- また可読性と保守性のしやすさが向上するので積極的に活用していきましょう。
例:ボタンの色を変えたいとき
background: var(--btn-grad);は一番上のrootの--btn-gradを変えるだけでいいのでいちいちスクロールして変えてという手間が省けます。
標準で使う単位を纏めておくことで修正が一箇所で済みます。
また、z-indexも管理しておくことでコードが肥大化して重なり順が迷子になるといったことが防げます。
なんでも!importantを多用しない。
こちらは要素を強制的に上書きするというものでスタイルがどこで適用されてるかわからないときに強引に上書きしてしまいます。デバッグで使うには便利かもですが本番で使ってしまうとバグの温床になったり修正が困難になってしまいます。なのでリリース時には使わないように心がけましょう(自分のタイピングゲームは使いまくりでそもそも保守性と可読性の観点で誰が見ても構造が終わってるのでのちに修正しますとほほ)
CSSは答えを求めると果てしないため、せめてレスポンシブに少しでもきれいに書くことを意識していきます!
JavaScript: 最適化の追求
JavaScriptの構成も可読性やパフォーマンス性とアクセシビリティを意識してみました。
const shouldSkipAnimation = () => {
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
};
こちらは視差効果を減らすやwindowアニメーションの設定がオフになっているときにアニメーションを再生しないようにすることが出来ます。
酔い防止等の配慮で入れております。
初期化関数でガード処理を入れています
// 視覚効果を減らす設定がONなら何もしない(アクセシビリティ対応)
if (shouldSkipAnimation()) return;
ただJSで動かさないCSSのアニメーションは@media (prefers-reduced-motion: reduce)で別途書く必要があります。
document.createDocumentFragment を使用する
こちらを使用することでDOM操作を一回で済ませることが出来ます。
使用例
function renderSnowflakes() {
const fallsContainer = document.getElementById('snow-falls'); // ここで取得
if (!fallsContainer) return;
// まず、フラグメントという箱を用意する
const fragment = document.createDocumentFragment();
const isMobile = window.innerWidth < MOBILE_BREAKPOINT;
// デバイスのサイズで雪を降らす個数を決める
const COUNT = isMobile ? MOBILE_COUNT : DESKTOP_COUNT;
for (let i = 0; i < COUNT; i++) {
// 要素をCOUNT分箱に詰め込む
fragment.appendChild(createSnow(isMobile)); // 引数を渡す
}
// ここで箱の中身を一気に放出
fallsContainer.replaceChildren(fragment);
}
何故これがいいのか
要素をfragmentという仮の箱に詰めて置き、最後に描画計算を行うのでブラウザの計算回数が一回で済み、パフォーマンスが向上します。
使わない例
function renderSnowflakes() {
const fallsContainer = document.getElementById('snow-falls'); // ここで取得
if (!fallsContainer) return;
const isMobile = window.innerWidth < MOBILE_BREAKPOINT;
// デバイスのサイズで雪を降らす個数を決める
const COUNT = isMobile ? MOBILE_COUNT : DESKTOP_COUNT;
for (let i = 0; i < COUNT; i++) {
// ここで計算する為COUNT分ブラウザが計算しないといけない。
fallscontainer.appendChild(createSnow(isMoblie));
}
}
何が起きているか: 1個追加するたびに リストの高さが変わったな…再計算だ! とブラウザが働き、それがCOUNT分繰り返されます。これを 「リフロー(Reflow)」 と呼び、処理が重くなる原因になります。
スマホのCPUだと目も当てられませんね...
買い物でたとえるならこんな感じです
❌ 使わない場合(地獄の買い物)
雪(商品)を1個かごに入れる。
レジに行く(描画計算発生!)。
家に帰る。
また店に行って、雪を1個かごに入れる。
またレジに行く(描画計算発生!)。
...これを100回繰り返す。 → 足(CPU)が棒になって倒れる。
⭕ 使う場合(スマートな買い物)
雪(商品)を1個カゴに入れる(計算なし)。
同じく1個カゴに入れる(計算なし)。
...カゴがいっぱいになるまで繰り返す。
最後に1回だけレジに行く(描画計算発生!)。 → 超効率的!
今後こういった書き方を効率的な癖付けていきます
requestAnimationFrame を使用
ページ読み込み時は、HTMLの解析や他のリソース読み込みでブラウザに負荷がかかりやすいタイミングです。 そこで、雪や星のDOM生成(重い処理)をそのまま実行するのではなく、requestAnimationFrame を使用してブラウザの描画準備が整ったタイミングで実行することによって滑らかなアニメーションを設計しました。
使用例
/**
* 指揮官: モジュールの初期化
*/
function initModule() {
// 視覚効果を減らす設定がONなら何もしない(アクセシビリティ対応)
if (shouldSkipAnimation()) return;
// 監視のセットアップ
setupController();
// 初回描画の予約
requestAnimationFrame(() => {
renderSnowflakes();
initStarDecoration();
});
}
ただ便利なわけでなく正確な時間に処理を実行させたいときやバックグラウンド時も進行する処理には不向きなのでsetIntervalやWeb Worker等他の手法を考えていきたいところです。
終わりに
長くなりましたが意識した点は他にもありますが長くなるため切り抜きました。
どんだけ小さなプロダクトでも品質等に気を配って書いていきたいと思いました。
それとAIを使って動くものは作れますがなぜこのコードを使うのか等の判断をするのは自分なので知識が無いと最高の品質とパフォーマンスを引き出せないという事を改めて学ぶことが出来ました。
最適解やベストプラクティス等はあるものの明確にこれだって言える答えがないのがプログラミングの醍醐味ですね!今後もっと引き出しを増やしていき、技術に関して深く語れるようになります!
現在独学で進めているため、間違い等ございましたら遠慮なくお申し付けください!
おそらく長い時間がかかりますが完成させて自身の成長だけでなく他の人にもためになるようなものを作っていきたいのでこれからも頑張ってまいります!
ここまで読んでいただきありがとうございました!
おまけ 因みにキャンペーン詳細を押すと...

おい!!!となりますがこの疑似サイトのレベルはSPA(シングルページアプリケーション)と画面遷移を経験する練習という事で簡素な作りになってます(笑)
気に食わねえ!となったら書き換えても自由なのです😂
みんなの書いたサンプルコードを載せたり別の言語でお題を考えるのも楽しいかも!まだそこまで手が回りませんが!