HTML/CSS/JSを組み合わせて作れる簡単なミニプロジェクト5選
HTML/CSS/JavaScript を組み合わせたミニプロジェクトは、基礎から実践的なスキルを楽しく学ぶのに最適です。ここでは、初心者でも短時間で完成できる5つのサンプルを紹介します。
各例には HTML 構造、CSS 例、JavaScript 例を含め、セキュリティやアクセシビリティへの配慮、レスポンシブ対応にも触れています。
プロジェクト1: カウンターアプリ
ボタンをクリックして数値を増減するシンプルなアプリです。textContent
は文字列なので、parseInt()
で数値に変換し、数値以外のときは 0 として扱います。
HTML 構造例
<div id="counter">
<button id="decr">-</button>
<span id="count">0</span>
<button id="incr">+</button>
</div>
JavaScript 例
const countEl = document.getElementById('count');
document.getElementById('incr').addEventListener('click', () => {
let count = parseInt(countEl.textContent, 10) || 0;
countEl.textContent = count + 1;
});
document.getElementById('decr').addEventListener('click', () => {
let count = parseInt(countEl.textContent, 10) || 0;
countEl.textContent = count - 1;
});
プロジェクト2: TODOリスト
タスクを追加・削除し、localStorage
に保存すると、ページを再読み込みしても内容が残ります。入力が空のときには追加しないバリデーションを加えています。
HTML 構造例
<form id="todo-form">
<input type="text" id="todo-input" placeholder="タスクを入力">
<button type="submit">追加</button>
</form>
<ul id="todo-list"></ul>
JavaScript 例(バリデーション+localStorage)
const form = document.getElementById('todo-form');
const input = document.getElementById('todo-input');
const list = document.getElementById('todo-list');
let todos = JSON.parse(localStorage.getItem('todos') || '[]');
function saveTodos() {
localStorage.setItem('todos', JSON.stringify(todos));
}
function renderTodos() {
list.innerHTML = '';
todos.forEach((text, i) => {
const li = document.createElement('li');
const btn = document.createElement('button');
li.textContent = text;
btn.textContent = '削除';
btn.addEventListener('click', () => {
todos.splice(i, 1);
saveTodos();
renderTodos();
});
li.appendChild(btn);
list.appendChild(li);
});
}
form.addEventListener('submit', e => {
e.preventDefault();
const text = input.value.trim();
if (!text) return; // 空文字を防止
todos.push(text);
saveTodos();
renderTodos();
input.value = '';
});
// 初期表示
renderTodos();
プロジェクト3: 電卓アプリ
数式を文字列で組み立て、計算ボタンで結果を表示します。eval()
はセキュリティリスクがあるため学習用途のみに限定し、実務では Function
コンストラクタや専用ライブラリを検討してください。
HTML 構造例
<div class="calculator">
<input type="text" id="display" aria-label="計算結果" readonly>
<div class="keys">
<button class="key">1</button> ... <button class="key">+</button>
<button class="key">C</button> <button class="key">=</button>
</div>
</div>
JavaScript 例
const display = document.getElementById('display');
document.querySelectorAll('.key').forEach(key => {
key.addEventListener('click', () => {
const val = key.textContent;
if (val === '=') {
try {
// 学習用: evalはセキュリティリスクあり
// Function() は渡された文字列を実行して結果を返すコンストラクタ
display.value = Function('"use strict";return (' + display.value + ')')();
} catch {
alert('数式にエラーがあります');
}
} else if (val === 'C') {
display.value = '';
} else {
display.value += val;
}
});
});
プロジェクト4: 画像スライダー
複数の画像を横並びにし、「次へ/前へ」ボタンで切り替えます。display: flex;
と transition: transform 0.5s ease;
で滑らかな動きを実現し、メディアクエリでレスポンシブ対応も行います。
HTML 構造例
<div class="slider">
<button id="prev">前へ</button>
<div class="slides">
<div class="slide">
<img src="https://via.placeholder.com/400x200?text=1" alt="画像1">
</div>
<div class="slide">
<img src="https://via.placeholder.com/400x200?text=2" alt="画像2">
</div>
<div class="slide">
<img src="https://via.placeholder.com/400x200?text=3" alt="画像3">
</div>
</div>
<button id="next">次へ</button>
</div>
CSS 例
.slider {
position: relative;
overflow: hidden;
max-width: 400px;
margin: 1em auto;
}
.slides {
display: flex;
transition: transform 0.5s ease;
}
.slide {
flex: 0 0 100%;
}
.slide img {
width: 100%;
display: block;
}
.slider button {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 10;
background: rgba(255, 255, 255, 0.8);
border: none;
padding: 0.5em 1em;
cursor: pointer;
}
#prev { left: 10px; }
#next { right: 10px; }
JavaScript 例
(function(){
const slider = document.querySelector('.slider');
const slidesWrapper = slider.querySelector('.slides');
const slides = slidesWrapper.children;
const count = slides.length;
// 最初と最後をクローン
const firstClone = slides[0].cloneNode(true);
const lastClone = slides[count - 1].cloneNode(true);
slidesWrapper.appendChild(firstClone);
slidesWrapper.insertBefore(lastClone, slidesWrapper.firstChild);
let idx = 1; // クローン込みインデックス
let slideWidth = slider.clientWidth;
// 初期表示
slidesWrapper.style.transition = 'none';
slidesWrapper.style.transform = `translateX(-${slideWidth * idx}px)`;
// 繰り返し無限ループ用調整
slidesWrapper.addEventListener('transitionend', () => {
if (idx === 0) {
slidesWrapper.style.transition = 'none';
idx = count;
slidesWrapper.style.transform = `translateX(-${slideWidth * idx}px)`;
}
if (idx === count + 1) {
slidesWrapper.style.transition = 'none';
idx = 1;
slidesWrapper.style.transform = `translateX(-${slideWidth * idx}px)`;
}
});
// ボタン制御
document.getElementById('next').addEventListener('click', () => {
idx++;
slidesWrapper.style.transition = 'transform 0.5s ease';
slidesWrapper.style.transform = `translateX(-${slideWidth * idx}px)`;
});
document.getElementById('prev').addEventListener('click', () => {
idx--;
slidesWrapper.style.transition = 'transform 0.5s ease';
slidesWrapper.style.transform = `translateX(-${slideWidth * idx}px)`;
});
// レスポンシブ対応
window.addEventListener('resize', () => {
slideWidth = slider.clientWidth;
slidesWrapper.style.transition = 'none';
slidesWrapper.style.transform = `translateX(-${slideWidth * idx}px)`;
});
})();
プロジェクト5: モーダルウィンドウ
背景を暗くして中央にポップアップを表示するモーダルです。ARIA 属性や visibility
、pointer-events
を利用し、表示切り替え時のアニメーションも滑らかに実現しています。
HTML 構造例
<button id="openModal">モーダルを開く</button>
<div id="modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="modal-content">
<h2 id="modal-title">タイトル</h2>
<p>モーダルの内容をここに記述します。</p>
<button id="closeModal">閉じる</button>
</div>
</div>
CSS 例
.modal {
visibility: hidden;
opacity: 0;
pointer-events: none;
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.5);
transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}
.modal.active {
visibility: visible;
opacity: 1;
pointer-events: auto;
transition-delay: 0s;
}
.modal-content {
background: #fff;
margin: 10% auto;
padding: 20px;
width: 80%;
max-width: 400px;
border-radius: 8px;
}
JavaScript 例
document.getElementById('openModal').addEventListener('click', () => {
document.getElementById('modal').classList.add('active');
});
document.getElementById('closeModal').addEventListener('click', () => {
document.getElementById('modal').classList.remove('active');
});
おわりに
以上の5つのミニプロジェクトを通して、HTML の構造作成から CSS レイアウト、JavaScript による動的処理まで一連の流れを体験できました。
慣れてきたらさらに機能追加やデザイン拡張、モバイル向けメディアクエリ、さらなるアクセシビリティ強化にも挑戦してみてください。