"GitHub Copilot" の練習に HTML 占いアプリを作ってみました。こちらで紹介します。
"GitHub Copilot" と "Microsoft Visual Studio Code" を使って、簡単な HTML 占いアプリを作ってみました。
Agent モードで "GPT-5 mini" を使用し、5分強 という短時間の作業で完成することができました。
人によるソースコード直接編集を一切行っておりません。背景画像 ("fortune_teller.png") だけ事前準備して開始しています。

"GitHub Copilot" を使って "HTML 占いアプリ" を作成する全作業を以下のビデオ紹介します。
"GitHub Copilot" が作成した全コードを以下で紹介します。
"README.md"
# おみくじ占い (簡易) これはシンプルな HTML/CSS/JS で作られた占いアプリのサンプルです。 使い方: - `index.html`, `style.css`, `script.js` を同じディレクトリに置いてブラウザで `index.html` を開いてください。 - または簡易サーバーで提供するには、Node.js があれば次を実行できます。 ```powershell # 簡易サーバー (PowerShell) python -m http.server 8000 # ブラウザで http://localhost:8000 を開く ``` 機能: - 名前(任意)を入力して今日の運勢を占う - ラッキーナンバーの表示 - 結果のコピー / シェア 履歴: - 占った結果はブラウザの localStorage に保存されます(表示はページ内の「履歴」セクション)。 - 履歴は「履歴を消去」ボタンで削除できます。
"index.html"
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>おみくじ占い</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="container">
<h1>今日の運勢を占う</h1>
<form id="fortuneForm">
<label for="name">あなたの名前(任意)</label>
<input id="name" name="name" type="text" placeholder="例: 太郎" autocomplete="off" />
<div class="controls">
<button type="submit" id="drawBtn">占う</button>
</div>
</form>
<section id="result" class="result" aria-live="polite" hidden>
<div class="card" id="card">
<h2 id="fortuneTitle"></h2>
<p id="fortuneDesc"></p>
<p id="luckyNumber"></p>
<div class="actions">
<button id="shareBtn">結果をコピー</button>
<button id="againBtn">もう一度</button>
</div>
</div>
</section>
<section id="historySection" class="history">
<h3>履歴</h3>
<p class="history-empty" id="historyEmpty">まだ履歴がありません。</p>
<ul id="historyList" class="history-list" aria-live="polite"></ul>
<div class="history-actions">
<button id="clearHistoryBtn" class="clear">履歴を消去</button>
</div>
</section>
<footer class="footer">ローカルで開くか、簡易サーバーで動かしてください。</footer>
</main>
<script src="script.js"></script>
</body>
</html>
"style.css"
:root{
--bg:#f8f7ff;
--card:#ffffff;
--accent:#6b5b95;
--muted:#666;
}
*{box-sizing:border-box}
html,body,#root{height:100%}
.body-bg{
position:fixed;inset:0;z-index:-1;background-size:cover;background-position:center;filter:brightness(0.95);
}
body{
margin:0;padding:24px;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Hiragino Kaku Gothic ProN",Meiryo,sans-serif;color:#222;min-height:100vh;position:relative;
/* 背景画像 + 暗めのオーバーレイでテキストを読みやすくする */
background-image:linear-gradient(rgba(6,6,10,0.36),rgba(6,6,10,0.12)), url('fortune_teller.png');
background-size:cover;
background-position:center;
}
.container{max-width:680px;margin:24px auto}
.container h1{font-size:1.6rem;margin:0 0 16px;color:#fff;text-shadow:0 2px 8px rgba(0,0,0,0.45)}
form{display:grid;gap:12px;background:var(--card);padding:16px;border-radius:10px;box-shadow:0 6px 18px rgba(102,102,102,0.08)}
label{font-size:0.9rem;color:var(--muted)}
input[type=text]{padding:10px 12px;border-radius:8px;border:1px solid #ddd;font-size:1rem}
.controls{display:flex;gap:8px}
button{background:var(--accent);color:#fff;border:0;padding:10px 14px;border-radius:8px;cursor:pointer}
button[disabled]{opacity:0.6;cursor:not-allowed}
.result{margin-top:16px}
.card{background:linear-gradient(180deg,#fff,#fbfbff);padding:16px;border-radius:10px;border:1px solid #eee}
.card h2{margin:0 0 8px}
.card p{margin:6px 0}
.actions{display:flex;gap:8px;margin-top:12px}
.footer{margin-top:20px;color:rgba(255,255,255,0.85);font-size:0.9rem}
.history{margin-top:18px;background:rgba(255,255,255,0.06);padding:12px;border-radius:8px;color:#fff}
.history h3{margin:0 0 8px}
.history-list{list-style:none;padding:0;margin:0;max-height:220px;overflow:auto}
.history-item{padding:8px;border-bottom:1px dashed rgba(255,255,255,0.06)}
.history-item .date{display:block;color:rgba(255,255,255,0.7);font-size:0.85rem}
.history-item .desc{margin-top:6px;color:rgba(255,255,255,0.95)}
.history-item .ln{margin-top:6px;font-weight:600}
.history-actions{margin-top:8px}
.clear{background:transparent;border:1px solid rgba(255,255,255,0.14);color:#fff;padding:8px 10px;border-radius:8px}
.history-empty{color:rgba(255,255,255,0.8);margin:6px 0}
@media (max-width:520px){
.container{padding:8px}
h1{font-size:1.3rem}
}
"script.js"
// 占いロジック
const fortunes = [
{ title: '大吉', desc: '今日は何をやっても上手くいきます。自信を持って進んでください。'},
{ title: '中吉', desc: '良い運気です。小さなチャレンジが実を結びます。'},
{ title: '小吉', desc: '穏やかな一日。急がず着実に行動を。'},
{ title: '凶', desc: '注意が必要です。慌てずリスクを確認しましょう。'},
{ title: '大凶', desc: '思いがけない出来事に注意。無理は禁物です。'}
];
function randomInt(min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}
document.addEventListener('DOMContentLoaded',()=>{
const form = document.getElementById('fortuneForm');
const result = document.getElementById('result');
const drawBtn = document.getElementById('drawBtn');
const againBtn = document.getElementById('againBtn');
const shareBtn = document.getElementById('shareBtn');
const fortuneTitle = document.getElementById('fortuneTitle');
const fortuneDesc = document.getElementById('fortuneDesc');
const luckyNumber = document.getElementById('luckyNumber');
const historyList = document.getElementById('historyList');
const historyEmpty = document.getElementById('historyEmpty');
const clearHistoryBtn = document.getElementById('clearHistoryBtn');
// 履歴: localStorage を使って保存
const HISTORY_KEY = 'fortune_history_v1';
function loadHistory(){
const raw = localStorage.getItem(HISTORY_KEY);
try{
const arr = raw? JSON.parse(raw) : [];
return Array.isArray(arr)? arr : [];
}catch(e){
console.error('履歴の読み込みでエラー', e);
return [];
}
}
function saveHistory(arr){
try{ localStorage.setItem(HISTORY_KEY, JSON.stringify(arr)); }catch(e){ console.error(e); }
}
function renderHistory(){
const arr = loadHistory();
historyList.innerHTML = '';
if(arr.length === 0){ historyEmpty.hidden = false; return; }
historyEmpty.hidden = true;
arr.slice().reverse().forEach(item => {
const li = document.createElement('li');
li.className = 'history-item';
li.innerHTML = `<strong>${item.title}</strong> <span class="date">${item.date}</span><div class="desc">${item.desc}</div><div class="ln">${item.lucky}</div>`;
historyList.appendChild(li);
});
}
clearHistoryBtn.addEventListener('click', ()=>{
if(!confirm('履歴を本当に消去しますか?')) return;
saveHistory([]);
renderHistory();
});
form.addEventListener('submit',(e)=>{
e.preventDefault();
drawBtn.disabled = true;
// シンプルな演出
drawBtn.textContent = '占っています...';
setTimeout(()=>{
const idx = randomInt(0,fortunes.length-1);
const f = fortunes[idx];
const name = (document.getElementById('name').value || '').trim();
fortuneTitle.textContent = `${f.title}${name? ' — '+name : ''}`;
fortuneDesc.textContent = f.desc;
const num = randomInt(1,99);
luckyNumber.textContent = `ラッキーナンバー: ${num}`;
result.hidden = false;
drawBtn.textContent = '占う';
drawBtn.disabled = false;
// スクロールして結果へ
result.scrollIntoView({behavior:'smooth'});
// 履歴に保存
try{
const arr = loadHistory();
arr.push({
title: fortuneTitle.textContent,
desc: fortuneDesc.textContent,
lucky: luckyNumber.textContent,
date: new Date().toLocaleString()
});
saveHistory(arr);
renderHistory();
}catch(e){ console.error('履歴保存失敗', e); }
},700);
});
againBtn.addEventListener('click',()=>{
result.hidden = true;
document.getElementById('name').focus();
});
shareBtn.addEventListener('click',async ()=>{
const title = fortuneTitle.textContent || '';
const desc = fortuneDesc.textContent || '';
const num = luckyNumber.textContent || '';
const text = `今日の運勢: ${title}\n${desc}\n${num}`;
try{
if(navigator.share){
await navigator.share({title:'今日の運勢',text});
} else if(navigator.clipboard){
await navigator.clipboard.writeText(text);
alert('結果をクリップボードにコピーしました。');
} else {
// フォールバック
const ta = document.createElement('textarea');
ta.value = text; document.body.appendChild(ta); ta.select();
document.execCommand('copy'); document.body.removeChild(ta);
alert('結果をコピーしました。');
}
}catch(err){
console.error(err);
alert('共有に失敗しました。');
}
});
});
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
| 2025-11-03 | - | 新規作成 |