<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>JobFinder - 求人掲示板</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: sans-serif; background: #f4f4f4; margin: 0; }
header { background: #007bff; color: white; padding: 1rem; text-align: center; }
main { padding: 2rem; max-width: 900px; margin: auto; }
.search-bar, .post-form { background: white; padding: 1rem; border-radius: 8px; margin-bottom: 2rem; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.job-card {
background: white; padding: 1rem; margin-bottom: 1rem;
border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.job-card h2 { margin-top: 0; }
.job-card button {
background: #007bff; color: white;
border: none; padding: 0.5rem 1rem;
border-radius: 4px; cursor: pointer;
}
.job-card button:hover { background: #0056b3; }
.modal {
display: none; position: fixed; z-index: 999;
left: 0; top: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.5);
justify-content: center; align-items: center;
}
.modal-content {
background: white; padding: 2rem;
border-radius: 8px; max-width: 500px;
}
label, input, textarea, select {
display: block; width: 100%; margin-bottom: 10px;
}
button[type="submit"] {
background: #28a745;
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
}
</style>
</head>
<body>
<header>
<h1>JobFinder - 求人掲示板</h1>
</header>
<main>
<!-- 🔍 検索バー -->
<div class="search-bar">
<h3>🔍 求人検索</h3>
<input type="text" id="searchLocation" placeholder="勤務地で検索(例: 東京)">
<button onclick="filterJobs()">検索</button>
</div>
<!-- 📋 求人一覧 -->
<div id="jobList"></div>
<!-- 📝 求人投稿フォーム -->
<div class="post-form">
<h3>📝 新しい求人を投稿</h3>
<input type="text" id="jobTitle" placeholder="職種(例: Webエンジニア)">
<input type="text" id="jobCompany" placeholder="会社名">
<input type="text" id="jobLocation" placeholder="勤務地(例: 東京)">
<textarea id="jobDescription" rows="3" placeholder="仕事内容を入力..."></textarea>
<button onclick="addJob()">求人を投稿</button>
</div>
</main>
<!-- 📄 モーダル -->
<div class="modal" id="jobModal">
<div class="modal-content" id="modalContent"></div>
</div>
<script>
let jobs = [
{
title: "Webエンジニア",
company: "株式会社テック",
location: "東京",
description: "ReactやVueを用いた開発。経験者優遇。"
},
{
title: "営業職",
company: "セールス株式会社",
location: "大阪",
description: "法人営業経験者歓迎。"
}
];
function displayJobs(filteredJobs = jobs) {
const jobList = document.getElementById("jobList");
jobList.innerHTML = "";
filteredJobs.forEach((job, index) => {
const card = document.createElement("div");
card.className = "job-card";
card.innerHTML = `
<h2>${job.title}</h2>
<p><strong>会社:</strong> ${job.company}</p>
<p><strong>勤務地:</strong> ${job.location}</p>
<button onclick="showDetails(${index})">詳細を見る</button>
`;
jobList.appendChild(card);
});
}
function showDetails(index) {
const job = jobs[index];
const modal = document.getElementById("jobModal");
const modalContent = document.getElementById("modalContent");
modalContent.innerHTML = `
<h2>${job.title}</h2>
<p><strong>会社:</strong> ${job.company}</p>
<p><strong>勤務地:</strong> ${job.location}</p>
<p>${job.description}</p>
<button onclick="alert('応募完了しました!')">応募する</button>
<button onclick="document.getElementById('jobModal').style.display='none'">閉じる</button>
`;
modal.style.display = "flex";
}
function addJob() {
const title = document.getElementById("jobTitle").value;
const company = document.getElementById("jobCompany").value;
const location = document.getElementById("jobLocation").value;
const description = document.getElementById("jobDescription").value;
if (title && company && location && description) {
jobs.push({ title, company, location, description });
displayJobs();
document.getElementById("jobTitle").value = "";
document.getElementById("jobCompany").value = "";
document.getElementById("jobLocation").value = "";
document.getElementById("jobDescription").value = "";
} else {
alert("すべての項目を入力してください");
}
}
function filterJobs() {
const keyword = document.getElementById("searchLocation").value.trim();
const filtered = jobs.filter(job => job.location.includes(keyword));
displayJobs(filtered);
}
// 初期表示
displayJobs();
window.onclick = function(event) {
const modal = document.getElementById("jobModal");
if (event.target === modal) {
modal.style.display = "none";
}
}
</script>
</body>
</html>
カテゴリー: HTML
note風WEBサービス
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>MiniNote - Note風ブログ with フィルタ・いいね・検索機能</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body { font-family: sans-serif; background: #f9f9f9; margin: 0; }
header { background: #2c3e50; color: white; padding: 1em; text-align: center; }
nav { background: #34495e; text-align: center; padding: 0.5em; }
nav button { margin: 0 8px; padding: 8px 20px; cursor: pointer; background: #ecf0f1; border: none; border-radius: 5px; }
#main { max-width: 900px; margin: 2em auto; padding: 1em; background: white; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
article { padding: 1em; border-bottom: 1px solid #ddd; }
h2 { margin: 0.5em 0; }
.meta { font-size: 0.9em; color: #999; margin-bottom: 0.5em; }
textarea, input[type="text"] { width: 100%; padding: 10px; margin: 10px 0; border-radius: 5px; border: 1px solid #ccc; }
button.submit {
background: #3498db; color: white; border: none;
padding: 10px 20px; border-radius: 6px;
cursor: pointer; font-weight: bold;
}
.hidden { display: none; }
.tag { color: #3498db; margin-right: 5px; font-size: 0.9em; cursor: pointer; }
.actions button { margin-right: 10px; }
.like { color: #e74c3c; cursor: pointer; margin-left: 10px; }
</style>
</head>
<body>
<header><h1>📘 MiniNote - あなたのNote風ブログ</h1></header>
<nav>
<button onclick="showView('home')">🏠 ホーム</button>
<button onclick="showView('new')">✍ 新規投稿</button>
<input type="text" id="searchInput" placeholder="検索" oninput="renderPosts()" style="padding:5px; width: 200px;">
</nav>
<div id="main">
<div id="homeView">
<div id="postList"></div>
</div>
<div id="newView" class="hidden">
<h2>新しい投稿を作成</h2>
<input type="text" id="titleInput" placeholder="タイトル">
<textarea id="contentInput" rows="8" placeholder="本文"></textarea>
<input type="text" id="tagInput" placeholder="タグ(カンマ区切り)">
<button class="submit" onclick="addPost()">投稿する</button>
</div>
<div id="detailView" class="hidden">
<article>
<h2 id="detailTitle"></h2>
<div class="meta" id="detailDate"></div>
<p id="detailContent"></p>
<div id="detailTags"></div>
<div class="like" id="likeDisplay" onclick="likePost(currentIndex)">❤️ <span id="likeCount">0</span></div>
</article>
<div class="actions">
<button onclick="editPost(currentIndex)">✏ 編集</button>
<button onclick="deletePost(currentIndex)">🗑 削除</button>
<button onclick="showView('home')">← 戻る</button>
</div>
</div>
</div>
<script>
let posts = JSON.parse(localStorage.getItem("posts") || "[]");
let currentIndex = null;
let filterTag = null;
function showView(view) {
document.querySelectorAll('#main > div').forEach(div => div.classList.add('hidden'));
document.getElementById(view + "View").classList.remove("hidden");
if (view === "home") renderPosts();
}
function addPost() {
const title = document.getElementById("titleInput").value;
const content = document.getElementById("contentInput").value;
const tags = document.getElementById("tagInput").value.split(',').map(tag => tag.trim()).filter(Boolean);
if (!title || !content) return alert("タイトルと本文を入力してください");
const date = new Date().toLocaleString();
posts.unshift({ title, content, date, tags, likes: 0 });
localStorage.setItem("posts", JSON.stringify(posts));
clearForm();
showView("home");
}
function renderPosts() {
const list = document.getElementById("postList");
const search = document.getElementById("searchInput").value.toLowerCase();
list.innerHTML = "";
posts.filter(post => {
const inSearch = post.title.toLowerCase().includes(search) || post.content.toLowerCase().includes(search);
const inTag = !filterTag || post.tags.includes(filterTag);
return inSearch && inTag;
}).forEach((post, index) => {
const el = document.createElement("article");
el.innerHTML = `
<h2>${post.title}</h2>
<div class="meta">${post.date}</div>
<p>${post.content.substring(0, 80)}...</p>
<div>${post.tags.map(tag => `<span class='tag' onclick='filterByTag("${tag}")'>#${tag}</span>`).join(' ')}</div>
<div class="like">❤️ ${post.likes}</div>
<button onclick="showDetail(${index})">📖 詳細</button>
`;
list.appendChild(el);
});
}
function filterByTag(tag) {
filterTag = tag;
renderPosts();
}
function showDetail(index) {
const post = posts[index];
currentIndex = index;
document.getElementById("detailTitle").innerText = post.title;
document.getElementById("detailDate").innerText = post.date;
document.getElementById("detailContent").innerText = post.content;
document.getElementById("detailTags").innerHTML = post.tags.map(tag => `<span class='tag'>#${tag}</span>`).join(' ');
document.getElementById("likeCount").innerText = post.likes;
showView("detail");
}
function likePost(index) {
posts[index].likes++;
localStorage.setItem("posts", JSON.stringify(posts));
document.getElementById("likeCount").innerText = posts[index].likes;
renderPosts();
}
function editPost(index) {
const post = posts[index];
document.getElementById("titleInput").value = post.title;
document.getElementById("contentInput").value = post.content;
document.getElementById("tagInput").value = post.tags.join(', ');
posts.splice(index, 1);
localStorage.setItem("posts", JSON.stringify(posts));
showView("new");
}
function deletePost(index) {
if (!confirm("本当に削除しますか?")) return;
posts.splice(index, 1);
localStorage.setItem("posts", JSON.stringify(posts));
showView("home");
}
function clearForm() {
document.getElementById("titleInput").value = "";
document.getElementById("contentInput").value = "";
document.getElementById("tagInput").value = "";
}
showView("home");
</script>
</body>
</html>
MyCSSGrid
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My CSS Grid</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<ul>
<li>item 1</li>
<li>item 2 item 2 item 2 item 2 item 2</li>
<li>item 3</li>
<li>item 4</li>
</ul>
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3 item 3 item 3 item 3</li>
<li>item 4</li>
</ul>
</div>
</body>
</html>
style.css
@charset "utf-8";
.container {
display: grid;
grid-template-columns: 200px 200px;
grid-template-rows: repeat(4, auto);
}
ul {
grid-row: span 4;
display: grid;
grid-template-rows: subgrid;
}
ドメイン検索&購入(フロントエンド)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>ドメイン検索&購入</title>
<style>
body {
font-family: sans-serif;
background: #f9f9f9;
padding: 2rem;
margin: 0;
display: flex;
justify-content: center;
}
.container {
max-width: 600px;
width: 100%;
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
h1 {
color: #e60033;
text-align: center;
margin-bottom: 1.5rem;
}
.form {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
input {
flex: 1;
padding: 0.8rem;
border: 1px solid #ccc;
border-radius: 8px;
font-size: 1rem;
}
button {
padding: 0.8rem 1rem;
background-color: #e60033;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
}
.result {
margin-top: 1rem;
}
.item {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #eee;
padding: 0.5rem 0;
}
.available {
color: green;
font-weight: bold;
}
.unavailable {
color: red;
font-weight: bold;
}
.buy-btn {
background-color: #0070f3;
color: white;
padding: 0.3rem 0.7rem;
border-radius: 6px;
text-decoration: none;
font-size: 0.9rem;
}
</style>
</head>
<body>
<div class="container">
<h1>ドメイン検索</h1>
<div class="form">
<input type="text" id="domainInput" placeholder="例: mydomain" />
<button onclick="search()">検索</button>
</div>
<div class="result" id="resultArea"></div>
</div>
<script>
const takenDomains = ["example.com", "mydomain.net", "taken.jp"];
const tlds = [".com", ".net", ".jp"];
function search() {
const name = document.getElementById("domainInput").value.trim().toLowerCase();
const area = document.getElementById("resultArea");
area.innerHTML = "";
if (!name) {
area.innerHTML = "<p>⚠ ドメイン名を入力してください。</p>";
return;
}
tlds.forEach(tld => {
const domain = name + tld;
const available = !takenDomains.includes(domain);
const item = document.createElement("div");
item.className = "item";
item.innerHTML = `
<span>${domain}</span>
${available
? `<span class="available">取得可能</span>
<a href="https://buy.stripe.com/test_checkout_link" class="buy-btn" target="_blank">購入する</a>`
: `<span class="unavailable">登録済み</span>`}
`;
area.appendChild(item);
});
}
</script>
</body>
</html>
ゲームBGM自動生成サービス.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>🎮 ゲームBGM自動生成サービス</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
--bg: #121212;
--card: #1e1e2e;
--text: #ffffff;
--accent: #ffd700;
--shadow: rgba(0, 0, 0, 0.3);
--button: #ffd700;
}
[data-theme='light'] {
--bg: #f5f5f5;
--card: #ffffff;
--text: #111;
--accent: #ff9800;
--shadow: rgba(0, 0, 0, 0.1);
--button: #ff9800;
}
body {
margin: 0;
font-family: 'Segoe UI', sans-serif;
background: var(--bg);
color: var(--text);
transition: 0.3s;
padding: 1rem;
}
header {
text-align: center;
margin-bottom: 1rem;
}
h1 {
color: var(--accent);
font-size: 2rem;
}
.container {
max-width: 600px;
margin: auto;
background: var(--card);
border-radius: 12px;
box-shadow: 0 0 10px var(--shadow);
padding: 1.5rem;
}
label {
font-weight: bold;
display: block;
margin-top: 1rem;
}
select, button {
width: 100%;
padding: 0.6rem;
margin-top: 0.5rem;
border-radius: 8px;
border: none;
font-size: 1rem;
}
button {
background: var(--button);
color: #000;
font-weight: bold;
cursor: pointer;
transition: 0.3s;
}
button:disabled {
background: #999;
cursor: not-allowed;
}
button:hover:enabled {
opacity: 0.85;
}
.desc, .msg {
margin-top: 1rem;
font-size: 0.9rem;
color: #ccc;
}
.result, .history {
margin-top: 2rem;
}
audio {
width: 100%;
margin-top: 0.5rem;
}
.visualizer {
width: 100%;
height: 4px;
background: linear-gradient(90deg, #ffd700, #ff9800);
animation: pulse 2s infinite linear;
opacity: 0;
}
.playing .visualizer {
opacity: 1;
}
@keyframes pulse {
0% { transform: scaleX(1); }
50% { transform: scaleX(1.05); }
100% { transform: scaleX(1); }
}
.toggle-theme {
text-align: right;
margin-bottom: 0.5rem;
}
.accordion {
background: transparent;
color: var(--accent);
border: none;
font-weight: bold;
cursor: pointer;
margin-top: 1rem;
width: 100%;
text-align: left;
font-size: 1rem;
}
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.accordion.open + .accordion-content {
max-height: 600px;
}
</style>
</head>
<body>
<header>
<h1>🎮 ゲームBGM自動生成</h1>
</header>
<div class="toggle-theme">
<button onclick="toggleTheme()">🌓 テーマ切替</button>
</div>
<div class="container">
<label for="genre">🎼 ジャンル</label>
<select id="genre" onchange="updateDescription()">
<option value="fantasy">ファンタジー</option>
<option value="cyberpunk">サイバーパンク</option>
<option value="horror">ホラー</option>
<option value="symphonic">シンフォニック</option>
</select>
<label for="mood">🎭 雰囲気</label>
<select id="mood" onchange="updateDescription()">
<option value="mysterious">神秘的</option>
<option value="sad">悲しい</option>
<option value="heroic">勇ましい</option>
<option value="fun">楽しい</option>
</select>
<div class="desc" id="descText">選択内容に応じてBGMを生成します。</div>
<button id="generateBtn" onclick="generateBGM()">🎶 BGMを生成する</button>
<div class="msg" id="msg"></div>
<div class="result" id="result" style="display:none;">
<h3>🎧 再生中のBGM</h3>
<audio controls id="player" onplay="startVisualizer()" onpause="stopVisualizer()"></audio>
<div class="visualizer" id="visualizer"></div>
</div>
<button class="accordion" onclick="toggleAccordion(this)">📜 再生履歴</button>
<div class="accordion-content" id="historyList"></div>
</div>
<script>
const descMap = {
fantasy: "魔法の世界、冒険と伝説の音楽",
cyberpunk: "近未来都市とテクノ感の融合",
horror: "不安と恐怖を煽るBGM",
symphonic: "壮大で感動的なオーケストラ風",
mysterious: "謎解き、探検にぴったり",
sad: "涙や喪失感を表現する旋律",
heroic: "勇ましさ、戦い、勝利の象徴",
fun: "軽快で明るいリズム"
};
const history = [];
const maxHistory = 5;
function updateDescription() {
const g = document.getElementById('genre').value;
const m = document.getElementById('mood').value;
document.getElementById('descText').textContent = `🧠 ${descMap[g]} × ${descMap[m]}`;
}
function generateBGM() {
const genre = document.getElementById('genre').value;
const mood = document.getElementById('mood').value;
const btn = document.getElementById('generateBtn');
const player = document.getElementById('player');
const msg = document.getElementById('msg');
const result = document.getElementById('result');
btn.disabled = true;
msg.textContent = "⏳ BGMを生成中...";
const url = `https://example.com/bgm/${genre}_${mood}_${Math.floor(Math.random()*3)+1}.mp3`;
setTimeout(() => {
msg.textContent = "✅ BGM生成完了!再生できます。";
player.src = url;
result.style.display = 'block';
// 保存履歴
history.unshift({ genre, mood, url });
if (history.length > maxHistory) history.pop();
renderHistory();
btn.disabled = false;
}, 1500);
}
function renderHistory() {
const list = document.getElementById('historyList');
list.innerHTML = "";
history.forEach(item => {
const div = document.createElement('div');
div.innerHTML = `<strong>${item.genre} × ${item.mood}</strong><br><audio controls src="${item.url}"></audio><hr>`;
list.appendChild(div);
});
}
function toggleTheme() {
const theme = document.documentElement.getAttribute("data-theme");
document.documentElement.setAttribute("data-theme", theme === "light" ? "dark" : "light");
}
function toggleAccordion(el) {
el.classList.toggle('open');
}
function startVisualizer() {
document.getElementById('visualizer').style.opacity = '1';
}
function stopVisualizer() {
document.getElementById('visualizer').style.opacity = '0';
}
// 初期設定
document.documentElement.setAttribute("data-theme", "dark");
updateDescription();
</script>
</body>
</html>
MyAOS.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Site</title>
<link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" />
<link rel="stylesheet" href="style.css">
</head>
<body>
<section>
<h1>記事一覧</h1>
<article>
<img src="forest.png" width="240" height="160" data-aos="my-animation">
<div class="text">
<h2>タイトル</h2>
<p>こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。</p>
</div>
</article>
<article>
<img src="forest.png" width="240" height="160">
<div class="text">
<h2>タイトル</h2>
<p>こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。</p>
</div>
</article>
<article>
<img src="forest.png" width="240" height="160">
<div class="text">
<h2>タイトル</h2>
<p>こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。</p>
</div>
</article>
</section>
<section>
<h1>お客様の声</h1>
<div class="voices">
<section data-aos="fade-up">
<h2>すばらしい</h2>
<p>素晴らしいです。素晴らしいです。</p>
</section>
<section data-aos="fade-up" data-aos-delay="300">
<h2>すばらしい</h2>
<p>素晴らしいです。素晴らしいです。</p>
</section>
<section data-aos="fade-up" data-aos-delay="600">
<h2>すばらしい</h2>
<p>素晴らしいです。素晴らしいです。</p>
</section>
</div>
</section>
<footer id="footer">
(c) dotinstall.com
</footer>
<a href="#" class="to-top" data-aos="fade-up" data-aos-anchor="#footer" data-aos-offset="0">先頭へ</a>
<script src="https://unpkg.com/aos@next/dist/aos.js"></script>
<script>
AOS.init();
</script>
</body>
</html>
style.css
@charset "utf-8";
body {
margin: 0;
}
body>section {
margin: 0 32px;
}
h1 {
text-align: center;
padding: 160px 0;
}
article {
display: flex;
gap: 16px;
align-items: center;
}
article .text {
flex: 1;
}
article h2 {
margin: 0;
font-size: 22px;
}
article+article {
margin-top: 32px;
}
article:nth-child(even) {
flex-direction: row-reverse;
}
.voices {
display: flex;
gap: 16px;
}
.voices>section {
border-radius: 8px;
border: 1px solid #ccc;
padding: 16px 32px;
text-align: center;
}
footer {
margin-top: 160px;
text-align: center;
padding: 80px 0;
background: #eee;
}
.to-top {
position: fixed;
bottom: 16px;
right: 16px;
}
VRSNS
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>VR‑SNS Prototype v8 (Spatial Audio + PTT + エモート)</title>
<!-- Libraries -->
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-extras@6.1.1/dist/aframe-extras.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-peer@9.11.1/simplepeer.min.js"></script>
<style>
html,body{margin:0;padding:0;overflow:hidden;font-family:"Helvetica Neue",Arial,sans-serif;background:#111;color:#eee}
#ui{position:fixed;top:0;left:0;width:420px;height:100vh;overflow-y:auto;background:rgba(0,0,0,.9);backdrop-filter:blur(6px);padding:1rem;box-sizing:border-box;z-index:10;transition:transform .3s ease}
#ui.hidden{transform:translateX(-105%)}
#ui h1{font-size:1.5rem;margin:0 0 .8rem;text-align:center}
#ui label{display:block;font-size:.75rem;margin:.6rem 0 .15rem;color:#8fc}
#ui input,#ui select,#ui textarea{width:100%;box-sizing:border-box;margin-bottom:.7rem;padding:.56rem;border:none;border-radius:6px;font-size:.9rem;background:#222;color:#ddd}
#ui textarea{resize:vertical}
#ui button{width:100%;padding:.72rem;border:none;border-radius:6px;font-weight:bold;cursor:pointer;background:#06a;color:#fff;transition:background .2s}
#ui button:hover{background:#089}
#timeline{margin-top:1rem}
.post{background:#1a1a1a;border-radius:6px;padding:.75rem;margin-bottom:.75rem;word-break:break-word;font-size:.8rem;position:relative}
.post small{color:#999;font-size:.7rem}
.post img{max-width:100%;border-radius:4px;margin-top:.4rem}
.reactBar{display:flex;gap:5px;margin-top:.45rem}
.reactBtn{flex:1;background:#333;border:none;border-radius:4px;padding:3px 0;font-size:.75rem;cursor:pointer;color:#f88;display:flex;justify-content:center;align-items:center}
.reactBtn span{margin-left:4px;font-weight:bold;color:#ccc}
.hashTag{color:#6af;cursor:pointer}
#handle{position:absolute;top:50%;right:-18px;width:18px;height:80px;border-radius:0 6px 6px 0;background:#06a;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:.7rem;transform:translateY(-50%)}
#tagFilter{margin-bottom:.8rem;width:100%;padding:.56rem;border:none;border-radius:6px;background:#222;color:#ddd}
#onlineCount{font-size:.75rem;margin-bottom:.8rem;color:#9f9}
#roomInput{width:70%;display:inline-block}
#roomBtn{width:28%;display:inline-block;margin-left:2%}
#ttsToggleLbl,#muteToggleLbl,#pttInfo{display:flex;align-items:center;font-size:.75rem;margin-bottom:.7rem;gap:.5rem}
.sysMsg{color:#8ff;font-style:italic}
.speaking{animation:pulse 1s infinite}
@keyframes pulse{0%{opacity:1}50%{opacity:.4}100%{opacity:1}}
.emote{position:absolute;left:50%;transform:translateX(-50%);top:-0.4m;font-size:0.4m}
</style>
</head>
<body>
<div id="ui">
<div id="handle">⮜</div>
<h1>VR‑SNS</h1>
<label>ルーム ID</label>
<input id="roomInput" value="lobby"/><button id="roomBtn">入室</button>
<div id="onlineCount">オンライン: 0</div>
<label>ユーザー名</label>
<input id="usernameInput" placeholder="匿名" />
<label>アバター色</label>
<input id="avatarColor" type="color" value="#ff8800" />
<label>環境テーマ</label>
<select id="themeSelect"><option value="dusk">夕暮れ空</option><option value="midnight">真夜中</option><option value="day">晴れ昼</option></select>
<label>ハッシュタグ絞り込み</label>
<select id="tagFilter"><option value="all">すべて</option></select>
<label id="ttsToggleLbl"><input type="checkbox" id="ttsToggle"/> 投稿読み上げ</label>
<label id="muteToggleLbl"><input type="checkbox" id="muteToggle"/> マイク常時OFF</label>
<div id="pttInfo">Push‑To‑Talk: <strong>Vキー</strong> 押下中のみ送信</div>
<label>投稿</label>
<textarea id="postText" rows="3"></textarea>
<button id="postButton">ポスト</button>
<button id="clearButton" style="background:#a00;margin-top:.3rem">ローカル履歴削除</button>
<div id="timeline"></div>
</div>
<a-scene renderer="antialias:true" xr-mode-ui="true" background="color:#112" cursor="rayOrigin:mouse">
<a-entity environment="preset: forest; ground:y"></a-entity>
<a-entity id="ground" geometry="primitive:plane; width:150; height:150" rotation="-90 0 0" material="visible:false" static-body></a-entity>
<a-entity id="rig" position="0 1.6 4" movement-controls="fly:false; speed:0.22">
<a-entity id="head" camera look-controls></a-entity>
<a-entity id="leftHand" laser-controls="hand:left" raycaster="objects:.interactive" teleport-controls="button:trigger; collisionEntities:#ground; cameraRig:#rig"></a-entity>
<a-entity id="rightHand" laser-controls="hand:right" raycaster="objects:.interactive"></a-entity>
</a-entity>
</a-scene>
<script>
(()=>{
const gun=Gun({peers:['https://gun-manhattan.herokuapp.com/gun']});
const myId=Gun.text.random();
let room='lobby',roomRef;
const $=id=>document.getElementById(id);
const ui=$('ui'),handle=$('handle');
const roomInput=$('roomInput'),roomBtn=$('roomBtn'),onlineCount=$('onlineCount');
const usernameInput=$('usernameInput'),avatarColorIn=$('avatarColor');
const themeSelect=$('themeSelect'),tagFilter=$('tagFilter');
const ttsToggle=$('ttsToggle'),muteToggle=$('muteToggle');
const postText=$('postText'),postBtn=$('postButton'),clearBtn=$('clearButton');
const timelineEl=$('timeline');
const scene=document.querySelector('a-scene');
const head=$('head'),leftHand=$('leftHand'),rightHand=$('rightHand');
const store={get:(k,d)=>JSON.parse(localStorage.getItem('vrsns_'+k)||JSON.stringify(d)),set:(k,v)=>localStorage.setItem('vrsns_'+k,JSON.stringify(v))};
usernameInput.value=store.get('user','');avatarColorIn.value=store.get('color','#ff8800');themeSelect.value=store.get('theme','dusk');ttsToggle.checked=store.get('tts',false);
const postsMap=new Map();const tags=new Set();const remoteAvatars=new Map();const remotePeers=new Map();let micStream=null,aCtx=null;let pushTalking=false;
let presenceRef,posRef,presenceInterval,posInterval;
applyTheme(themeSelect.value);createOrUpdateAvatar();setupMic();enterRoom(room);
handle.onclick=()=>ui.classList.toggle('hidden');scene.addEventListener('enter-vr',()=>ui.classList.add('hidden'));scene.addEventListener('exit-vr',()=>ui.classList.remove('hidden'));
roomBtn.onclick=()=>{const r=roomInput.value.trim();if(r){leaveRoom();enterRoom(r)}};
themeSelect.onchange=e=>{const v=e.target.value;store.set('theme',v);applyTheme(v)};avatarColorIn.oninput=createOrUpdateAvatar;
ttsToggle.onchange=e=>store.set('tts',e.target.checked);
muteToggle.onchange=e=>updateMicState();
postBtn.onclick=submitPost;clearBtn.onclick=()=>{if(confirm('ローカル履歴を削除しますか?')){localStorage.removeItem('vrsns_posts_'+room);timelineEl.innerHTML='';}};
tagFilter.onchange=filterTimeline;
window.addEventListener('keydown',e=>{if(e.key==='v'&&!pushTalking){pushTalking=true;updateMicState();}});
window.addEventListener('keyup',e=>{if(e.key==='v'){pushTalking=false;updateMicState();}});
async function setupMic(){try{aCtx=new (window.AudioContext||window.webkitAudioContext)();micStream=await navigator.mediaDevices.getUserMedia({audio:true});updateMicState();}catch(err){alert('マイク利用不可');}}
function updateMicState(){if(!micStream)return;const enabled=!muteToggle.checked&&(pushTalking||muteToggle.checked===false&&ttsToggle);micStream.getAudioTracks()[0].enabled=enabled;}
function enterRoom(r){room=r;roomRef=gun.get('vrsns').get(room);roomInput.value=room;timelineEl.innerHTML='';postsMap.clear();tags.clear();rebuildTagFilter();onlineCount.textContent='オンライン: 0';
roomRef.get('posts').map().on(onPost);roomRef.get('posts').map().get('reactions').map().on(onReaction);
presenceRef=roomRef.get('presence').get(myId);presenceRef.put({ts:Gun.state(),color:avatarColorIn.value});presenceInterval=setInterval(()=>presenceRef.put({ts:Gun.state(),color:avatarColorIn.value}),10000);roomRef.get('presence').map().on(updateOnline);
posRef=roomRef.get('pos').get(myId);sendPos();posInterval=setInterval(sendPos,100);
roomRef.get('pos').map().on(onRemotePos);roomRef.get('signal').map().on(onSignal);sysMsg(`[${room}] 入室しました`);}
function leaveRoom(){clearInterval(presenceInterval);clearInterval(posInterval);presenceRef&&presenceRef.put(null);posRef&&posRef.put(null);roomRef.off();roomRef.get('signal').off();remoteAvatars.forEach(av=>av.remove());remoteAvatars.clear();remotePeers.forEach(p=>p.destroy());remotePeers.clear();}
function submitPost(){const user=usernameInput.value.trim()||'匿名',text=postText.value.trim();if(!text)return;store.set('user',user);store.set('color',avatarColorIn.value);const id=Gun.text.random();const post={id,user,text,time:Date.now(),color:avatarColorIn.value,reactions:{'❤':0,'😂':0,'😮':0,'😢':0},tags:extractTags(text)};roomRef.get('posts').get(id).put(post);postText.value='';if(ttsToggle.checked)speak(`${user} さん: ${text.replace(/#/g,'')}`);}
function onPost(post){if(!post||postsMap.has(post.id))return;postsMap.set(post.id,post);collectTags(post);rebuildTagFilter();renderPost2D(post);renderPost3D(post);filterTimeline();}
function onReaction(v,field,key){const pid=field,emoji=key;const p=postsMap.get(pid);if(!p)return;p.reactions[emoji]=v;updatePostCard(pid,p);updatePost3D(pid,p);}
function react(id,emo){roomRef.get('posts').get(id).get('reactions').get(emo).once(v=>roomRef.get('posts').get(id).get('reactions').get(emo).put((v||0)+1));}
function updateOnline(){let c=0;roomRef.get('presence').map().once(()=>c++);setTimeout(()=>onlineCount.textContent=`オンライン: ${c}`,200);}
function sendPos(){const p=head.object3D.position,l=leftHand.object3D.position,r=rightHand.object3D.position;posRef.put({x:p.x,y:p.y,z:p.z,l:{x:l.x,y:l.y,z:l.z},r:{x:r.x,y:r.y,z:r.z},color:avatarColorIn.value,ts:Gun.state()});}
function onRemotePos(data,id){if(id===myId||!data)return;let av=remoteAvatars.get(id);if(!av){av=createAvatar(id,data.color);remoteAvatars.set(id,av);connectVoice(id);}av.setAttribute('position',`${data.x} 0 ${data.z}`);av.querySelectorAll('[geometry]').forEach(g=>g.setAttribute('color',data.color||'#fff'));['l','r'].forEach(h=>{let el=av.querySelector('.'+h);if(!el){el=document.createElement('a-sphere');el.classList.add(h);el.setAttribute('radius',0.05);av.appendChild(el);}const hp=data[h];if(hp)el.setAttribute('position',`${hp.x} ${hp.y} ${hp.z}`);});}
function createAvatar(id,color){const root=document.createElement('a-entity');root.id='av_'+id;root.innerHTML=`<a-sphere radius='0.25' color='${color||'#fff'}' position='0 1.6 0'></a-sphere><a-cylinder height='0.8' radius='0.2' color='${color||'#fff'}' position='0 1 0'></a-cylinder>`;scene.appendChild(root);return root;}
function connectVoice(rid){if(remotePeers.has(rid)||!micStream)return;const init=myId>rid;const peer=new SimplePeer({initiator:init,trickle:true,stream:micStream});remotePeers.set(rid,peer);peer.on('signal',d=>roomRef.get('signal').get(myId).get(rid).put(JSON.stringify(d)));roomRef.get('signal').get(rid).get(myId).on(sig=>sig&&peer.signal(JSON.parse(sig)));peer.on('stream',s=>{const av=remoteAvatars.get(rid);if(!av)return;const a=new Audio();a.srcObject=s;const src=aCtx.createMediaStreamSource(s);const panner=aCtx.createPanner();panner.panningModel='HRTF';panner.distanceModel='inverse';panner.maxDistance=20;src.connect(panner).connect(aCtx.destination);function upd(){if(!av.parentNode)return;const pos=av.object3D.position;panner.setPosition(pos.x,pos.y,pos.z);requestAnimationFrame(upd);}upd();a.play();});peer.on('close',()=>remotePeers.delete(rid));peer.on('error',()=>remotePeers.delete(rid));}
function onSignal(){}
function renderPost2D(p){const isImg=/\.(gif|jpe?g|png|webp)$/i.test(p.text.trim());const card=document.createElement('div');card.className='post';card.dataset.id=p.id;card.dataset.tags=p.tags.join(',');card.innerHTML=`<strong style='color:${p.color}'>${escapeHTML(p.user)}</strong>: ${isImg?`<img src='${escapeHTML(p.text)}'/>`:linkify(escapeHTML(p.text))}${p.tags.map(t=>` <span class='hashTag'>#${t}</span>`).join('')}<br><small>${new Date(p.time).toLocaleString()}</small>`;const bar=document.createElement('div');bar.className='reactBar';['❤','😂','😮','😢'].forEach(e=>{const b=document.createElement('button');b.className='reactBtn';b.dataset.e=e;b.innerHTML=`${e} <span>${p.reactions[e]||0}</span>`;b.onclick=()=>react(p.id,e);bar.appendChild(b);});card.appendChild(bar);card.querySelectorAll('.hashTag').forEach(el=>el.onclick=()=>{tagFilter.value=el.textContent.slice(1).toLowerCase();filterTimeline();});timelineEl.insertBefore(card,timelineEl.firstChild);}
function updatePostCard(id,p){const card=document.querySelector(`.post[data-id='${id}']`);if(card)card.querySelectorAll('.reactBtn').forEach(btn=>btn.querySelector('span').textContent=p.reactions[btn.dataset.e]||0);}
function format3D(p){const top=Object.entries(p.reactions||{}).sort((a,b)=>b[1]-a[1])[0];const r=top&&top[1]>0?` ${top[0]}×${top[1]}`:'';return `${p.user}${r}\n${p.text}`;}
function renderPost3D(p){const ent=document.createElement('a-entity');ent.classList.add('interactive');ent.id='p3d_'+p.id;ent.setAttribute('text',{value:format3D(p),align:'center',width:4,color:p.color});ent.onclick=()=>react(p.id,'❤');positionEntity(ent);scene.appendChild(ent);setTimeout(()=>ent.remove(),300000);if(/\.(gif|jpe?g|png|webp)$/i.test(p.text.trim())){const plane=document.createElement('a-plane');plane.setAttribute('src',p.text);plane.setAttribute('width',1.8);plane.setAttribute('height',1.2);plane.classList.add('interactive');positionEntity(plane,0.6);scene.appendChild(plane);setTimeout(()=>plane.remove(),300000);}}
function updatePost3D(id,p){const e=$('p3d_'+id);if(e)e.setAttribute('text','value',format3D(p));}
function positionEntity(e,y=0){const ang=Math.random()*Math.PI,rad=5+Math.random()*5;e.setAttribute('position',`${Math.cos(ang)*rad} ${2+y} ${-Math.sin(ang)*rad}`);}
function collectTags(p){p.tags.forEach(t=>tags.add(t.toLowerCase()));}
function rebuildTagFilter(){const cur=tagFilter.value;tagFilter.innerHTML='<option value="all">すべて</option>'+[...tags].sort().map(t=>`<option value='${t}'>#${t}</option>`).join('');tagFilter.value=cur||'all';}
function filterTimeline(){const f=tagFilter.value;document.querySelectorAll('.post').forEach(el=>el.style.display=(f==='all'||el.dataset.tags.includes(f))?'':'none');}
function escapeHTML(s){return s.replace(/[&<>"']/g,ch=>({'&':'&','<':'<',">":">","\"":""","'":"'"}[ch]));}
function extractTags(t){return(t.match(/#(\w+)/g)||[]).map(x=>x.slice(1).toLowerCase());}
function linkify(t){return t.replace(/https?:\/\/\S+/g,u=>`<a href='${u}' target='_blank'>${u}</a>`);}
function applyTheme(t){let sky=scene.querySelector('a-sky');if(!sky){sky=document.createElement('a-sky');scene.appendChild(sky);}sky.setAttribute('color',t==='midnight'?'#000022':t==='day'?'#86cefa':'#112');}
function createOrUpdateAvatar(){const c=avatarColorIn.value;store.set('color',c);let av=$('myAvatar');const body=`<a-sphere radius='0.25' color='${c}' position='0 1.6 0'></a-sphere><a-cylinder height='0.8' radius='0.2' color='${c}' position='0 1 0'></a-cylinder>`;if(!av){av=document.createElement('a-entity');av.id='myAvatar';av.innerHTML=body;scene.appendChild(av);}else av.innerHTML=body;}
function speak(msg){speechSynthesis.speak(new SpeechSynthesisUtterance(msg));}
function sysMsg(t){const el=document.createElement('div');el.className='post sysMsg';el.textContent=t;timelineEl.insertBefore(el,timelineEl.firstChild);}
})();
</script>
</body>
</html>
WEBOS
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>仮想OS Pro</title>
<style>
body {
margin: 0;
background: #2c3e50;
font-family: 'Segoe UI', sans-serif;
overflow: hidden;
}
#desktop {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 40px;
background: linear-gradient(#2980b9, #34495e);
}
.icon {
width: 70px;
text-align: center;
margin: 20px;
cursor: pointer;
color: white;
}
.window {
position: absolute;
width: 300px;
height: 200px;
background: white;
border: 2px solid #555;
display: none;
box-shadow: 4px 4px 10px rgba(0,0,0,0.5);
}
.window-header {
background: #3498db;
padding: 5px;
cursor: move;
color: white;
}
.window-body {
padding: 10px;
}
#taskbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 40px;
background: #2c3e50;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
color: white;
}
</style>
</head>
<body onload="playStartupSound(); updateClock(); setInterval(updateClock, 1000);">
<div id="desktop">
<div class="icon" onclick="openWindow('memo')">📝<br>メモ帳</div>
<div class="icon" onclick="openWindow('calc')">🧮<br>電卓</div>
</div>
<div id="taskbar">
<div>仮想OS Pro</div>
<div id="clock"></div>
</div>
<!-- メモ帳 -->
<div class="window" id="memo">
<div class="window-header" onmousedown="dragWindow(event, this.parentElement)">メモ帳</div>
<div class="window-body">
<textarea style="width: 100%; height: 100px;">メモを入力してください</textarea>
</div>
</div>
<!-- 電卓 -->
<div class="window" id="calc">
<div class="window-header" onmousedown="dragWindow(event, this.parentElement)">電卓</div>
<div class="window-body">
<input type="text" id="calcDisplay" style="width:100%; font-size: 1.2em;" />
<button onclick="calculate()">計算</button>
</div>
</div>
<!-- 起動音 -->
<audio id="bootSound" src="https://upload.wikimedia.org/wikipedia/commons/2/2f/Windows_95_startup.ogg" preload="auto"></audio>
<script>
function openWindow(id) {
document.getElementById(id).style.display = 'block';
}
function dragWindow(e, el) {
e.preventDefault();
let offsetX = e.clientX - el.offsetLeft;
let offsetY = e.clientY - el.offsetTop;
function move(e) {
el.style.left = (e.clientX - offsetX) + 'px';
el.style.top = (e.clientY - offsetY) + 'px';
}
function stop() {
document.removeEventListener('mousemove', move);
document.removeEventListener('mouseup', stop);
}
document.addEventListener('mousemove', move);
document.addEventListener('mouseup', stop);
}
function calculate() {
let result;
try {
result = eval(document.getElementById('calcDisplay').value);
} catch {
result = "エラー";
}
document.getElementById('calcDisplay').value = result;
}
function playStartupSound() {
document.getElementById("bootSound").play();
}
function updateClock() {
const now = new Date();
const time = now.toLocaleTimeString();
document.getElementById("clock").textContent = time;
}
</script>
</body>
</html>
C#入門サイト
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="description" content="C# オールインワン入門サイト">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>オールインワン C# 入門サイト</title>
<!-- ★ スタイル(CSS)をまとめて定義 ★ -->
<style>
/* ページ全体 */
body {
margin: 0;
padding: 0;
font-family: sans-serif;
line-height: 1.6;
background: #f9f9f9;
}
/* ヘッダー */
header {
background: #f0f0f0;
padding: 10px;
}
header h1 {
margin: 0;
font-size: 1.5em;
}
/* ナビゲーション */
nav ul {
list-style: none;
padding: 0;
display: flex;
gap: 10px;
margin-top: 5px;
}
nav a {
text-decoration: none;
color: #333;
font-weight: bold;
}
nav a:hover {
text-decoration: underline;
}
/* メインコンテンツ */
main {
max-width: 900px;
margin: 20px auto;
padding: 20px;
background: #fff;
box-shadow: 0 0 4px rgba(0,0,0,0.1);
}
main h2 {
margin-top: 0;
border-left: 6px solid #333;
padding-left: 8px;
margin-bottom: 15px;
}
main h3 {
margin-bottom: 5px;
}
/* コードブロック */
pre {
background: #fafafa;
padding: 10px;
overflow-x: auto;
}
code {
font-family: Consolas, monospace;
}
/* フォームなど(クイズ用) */
form section {
margin-bottom: 20px;
}
#result {
margin-top: 20px;
background: #eef;
padding: 10px;
border: 1px solid #ccf;
display: inline-block;
}
/* フッター */
footer {
text-align: center;
padding: 10px;
background: #f0f0f0;
margin-top: 20px;
}
/* セクション間の余白調整 */
section {
margin-bottom: 40px;
}
</style>
</head>
<body>
<!-- ★ ヘッダーとナビゲーション ★ -->
<header>
<h1>オールインワン C# 入門サイト</h1>
<nav>
<ul>
<li><a href="#top">トップ</a></li>
<li><a href="#environment">環境構築 & .NET</a></li>
<li><a href="#basics">C#の基本</a></li>
<li><a href="#syntax">基本文法</a></li>
<li><a href="#control-flow">条件分岐・ループ</a></li>
<li><a href="#oop">オブジェクト指向</a></li>
<li><a href="#advanced">応用トピック</a></li>
<li><a href="#debugging">デバッグ & テスト</a></li>
<li><a href="#quiz">クイズ</a></li>
<li><a href="#example">サンプル</a></li>
</ul>
</nav>
</header>
<!-- ★ メインコンテンツ ★ -->
<main>
<!-- トップ (id="top") -->
<section id="top">
<h2>このサイトについて</h2>
<p>
ここでは、C# を初めて学ぶ方や、基礎を復習したい方向けに、
C#と.NETの概要から開発環境構築、文法、オブジェクト指向、さらにデバッグやテスト、
一歩進んだ応用トピック(Generics, LINQ, 非同期など)まで幅広くカバーしています。
</p>
<p>
ページ上部のナビゲーションから各セクションへ移動できます。
一通り学習した後は、クイズに挑戦したり、サンプルプログラムを動かしてみましょう。
</p>
</section>
<!-- 環境構築 (id="environment") -->
<section id="environment">
<h2>環境構築 & .NET</h2>
<article>
<h3>.NETエコシステムの概要</h3>
<p>
C# は .NET 上で動作する言語です。
近年はクロスプラットフォーム対応の「.NET (Core)」が主流であり、
Windows だけでなく Mac や Linux でも利用可能です。
</p>
</article>
<article>
<h3>Windows での開発</h3>
<p>
<strong>Visual Studio</strong> (Community 版) または <strong>VS Code</strong> が一般的です。
<code>dotnet</code> CLI を使ってプロジェクト作成・ビルド・実行が可能です。
</p>
<pre><code>
// 例: コンソールアプリプロジェクトの作成
dotnet new console -o MyApp
cd MyApp
dotnet run
</code></pre>
</article>
<article>
<h3>Mac / Linux での開発</h3>
<p>
公式サイトから .NET SDK をインストールすることで、同様に C# を利用できます。
VS Code + C#拡張機能があればブレークポイントによるデバッグも可能です。
</p>
</article>
</section>
<!-- C#の基本 (id="basics") -->
<section id="basics">
<h2>C#の基本</h2>
<article>
<h3>C#とは?</h3>
<p>
C# (シーシャープ) は、マイクロソフトが開発したオブジェクト指向言語です。
Javaに似た構文を持ち、GenericsやLINQ、非同期などモダンな機能を数多く備えています。
</p>
</article>
<article>
<h3>Hello World</h3>
<pre><code class="language-csharp">
using System;
namespace HelloWorldApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
</code></pre>
<p>このように、<code>Main</code> メソッドがエントリポイントとして呼び出されます。</p>
</article>
</section>
<!-- 基本文法 (id="syntax") -->
<section id="syntax">
<h2>基本文法</h2>
<article>
<h3>変数とデータ型</h3>
<pre><code class="language-csharp">
int number = 10;
double pi = 3.14;
bool isActive = true;
string message = "Hello C#";
</code></pre>
<p>
C# は強い型付け言語であり、<code>var</code> キーワードで型推論も可能です。
</p>
</article>
<article>
<h3>演算子</h3>
<p>
算術演算子、比較演算子、論理演算子などを使用できます。
</p>
<pre><code class="language-csharp">
int x = 5;
int y = 3;
Console.WriteLine(x + y); // 8
Console.WriteLine(x > y); // true
</code></pre>
</article>
</section>
<!-- 条件分岐・ループ (id="control-flow") -->
<section id="control-flow">
<h2>条件分岐・ループ</h2>
<article>
<h3>if / else / else if</h3>
<pre><code class="language-csharp">
int score = 85;
if(score >= 80) {
Console.WriteLine("Excellent!");
} else if(score >= 60) {
Console.WriteLine("Good!");
} else {
Console.WriteLine("Keep trying!");
}
</code></pre>
</article>
<article>
<h3>switch</h3>
<pre><code class="language-csharp">
int dayOfWeek = 2;
switch(dayOfWeek)
{
case 0:
Console.WriteLine("日曜日");
break;
case 1:
Console.WriteLine("月曜日");
break;
case 2:
Console.WriteLine("火曜日");
break;
default:
Console.WriteLine("不明な曜日");
break;
}
</code></pre>
</article>
<article>
<h3>for / while / do-while</h3>
<pre><code class="language-csharp">
// forループ
for(int i = 0; i < 5; i++) {
Console.WriteLine(i);
}
// whileループ
int j = 0;
while(j < 5) {
Console.WriteLine(j);
j++;
}
// do-whileループ
int k = 0;
do {
Console.WriteLine(k);
k++;
} while(k < 5);
</code></pre>
</article>
</section>
<!-- オブジェクト指向 (id="oop") -->
<section id="oop">
<h2>オブジェクト指向</h2>
<article>
<h3>クラスとオブジェクト</h3>
<pre><code class="language-csharp">
class Person
{
public string Name;
public int Age;
public void Greet()
{
Console.WriteLine($"こんにちは、{Name}です。");
}
}
class Program
{
static void Main()
{
Person p = new Person();
p.Name = "Taro";
p.Age = 20;
p.Greet();
}
}
</code></pre>
</article>
<article>
<h3>継承とポリモーフィズム</h3>
<pre><code class="language-csharp">
class Animal
{
public string Name { get; set; }
public virtual void Speak()
{
Console.WriteLine("何かを話す");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}
</code></pre>
</article>
<article>
<h3>インターフェイス</h3>
<pre><code class="language-csharp">
interface IFlyable
{
void Fly();
}
class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("羽ばたいて飛ぶ");
}
}
</code></pre>
</article>
</section>
<!-- 応用トピック (id="advanced") -->
<section id="advanced">
<h2>応用トピック</h2>
<article>
<h3>Generics (ジェネリクス)</h3>
<pre><code class="language-csharp">
List<int> numbers = new List<int>();
numbers.Add(10);
numbers.Add(20);
class Box<T>
{
public T Value { get; set; }
public Box(T value)
{
Value = value;
}
}
</code></pre>
</article>
<article>
<h3>デリゲート・イベント</h3>
<pre><code class="language-csharp">
public delegate void MyDelegate(string msg);
public class Publisher
{
public event MyDelegate OnPublish;
public void Publish(string msg)
{
OnPublish?.Invoke(msg);
}
}
</code></pre>
</article>
<article>
<h3>LINQ</h3>
<pre><code class="language-csharp">
int[] data = {1,2,3,4,5,6};
var evens = from x in data
where x % 2 == 0
select x;
</code></pre>
</article>
<article>
<h3>非同期 (async/await)</h3>
<pre><code class="language-csharp">
static async Task Main()
{
Console.WriteLine("開始");
await Task.Delay(1000);
Console.WriteLine("終了");
}
</code></pre>
</article>
</section>
<!-- デバッグ & テスト (id="debugging") -->
<section id="debugging">
<h2>デバッグ & テスト</h2>
<article>
<h3>Visual Studioでのデバッグ</h3>
<ol>
<li>行番号の左をクリックしてブレークポイントを設定</li>
<li>「デバッグ実行」ボタンで開始</li>
<li>停止したらローカル変数やステップ実行を確認</li>
</ol>
</article>
<article>
<h3>例外処理</h3>
<pre><code class="language-csharp">
try
{
int num = int.Parse("abc");
}
catch(FormatException fe)
{
Console.WriteLine("フォーマットエラー: " + fe.Message);
}
finally
{
Console.WriteLine("終了処理");
}
</code></pre>
</article>
<article>
<h3>ユニットテスト</h3>
<pre><code class="language-csharp">
using Xunit;
public class CalcTests
{
[Fact]
public void AddTest()
{
int result = Calc.Add(2, 3);
Assert.Equal(5, result);
}
}
public static class Calc
{
public static int Add(int x, int y) => x + y;
}
</code></pre>
<p>
xUnit、NUnit、MSTest などが有名です。<code>dotnet test</code> でテスト実行できます。
</p>
</article>
</section>
<!-- クイズ (id="quiz") -->
<section id="quiz">
<h2>C#クイズ</h2>
<p>ラジオボタンで答えを選んで「採点する」ボタンを押してください。</p>
<form id="quizForm">
<article>
<h3>Q1. C# コンソールアプリのエントリポイントは?</h3>
<label>
<input type="radio" name="q1" value="A">
public static void start()
</label><br>
<label>
<input type="radio" name="q1" value="B">
private void main()
</label><br>
<label>
<input type="radio" name="q1" value="C">
static void Main(string[] args)
</label><br>
<label>
<input type="radio" name="q1" value="D">
run()
</label>
</article>
<article>
<h3>Q2. 次のうち整数型ではないのは?</h3>
<label>
<input type="radio" name="q2" value="A">
int
</label><br>
<label>
<input type="radio" name="q2" value="B">
double
</label><br>
<label>
<input type="radio" name="q2" value="C">
long
</label><br>
<label>
<input type="radio" name="q2" value="D">
short
</label>
</article>
<article>
<h3>Q3. C# の例外処理に使われるキーワードの組み合わせは?</h3>
<label>
<input type="radio" name="q3" value="A">
try / catch / else
</label><br>
<label>
<input type="radio" name="q3" value="B">
try / check / throw
</label><br>
<label>
<input type="radio" name="q3" value="C">
try / catch / finally
</label><br>
<label>
<input type="radio" name="q3" value="D">
try / except / ensure
</label>
</article>
<button type="button" onclick="gradeQuiz()">採点する</button>
</form>
<!-- 採点結果を表示する領域 -->
<div id="result"></div>
</section>
<!-- サンプル (id="example") -->
<section id="example">
<h2>サンプルプログラム</h2>
<article>
<h3>1. ユーザー入力を受け取る</h3>
<pre><code class="language-csharp">
using System;
namespace SampleApp
{
class Program
{
static void Main()
{
Console.WriteLine("名前を入力してください:");
string name = Console.ReadLine();
Console.WriteLine($"こんにちは、{name}さん!");
}
}
}
</code></pre>
</article>
<article>
<h3>2. 例外処理</h3>
<pre><code class="language-csharp">
using System;
namespace SampleApp
{
class Program
{
static void Main()
{
Console.WriteLine("整数を入力してください:");
try
{
int number = int.Parse(Console.ReadLine());
Console.WriteLine("二乗:" + (number * number));
}
catch(Exception e)
{
Console.WriteLine("エラー:" + e.Message);
}
}
}
}
</code></pre>
</article>
<article>
<h3>3. LINQ を使ったフィルタリング</h3>
<pre><code class="language-csharp">
using System;
using System.Linq;
using System.Collections.Generic;
namespace SampleApp
{
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 4, 7, 2, 9, 12 };
var evenNumbers = numbers.Where(x => x % 2 == 0);
Console.WriteLine("偶数のみ抽出:");
foreach(var n in evenNumbers)
{
Console.WriteLine(n);
}
}
}
}
</code></pre>
</article>
<article>
<h3>4. 非同期処理</h3>
<pre><code class="language-csharp">
using System;
using System.Threading.Tasks;
namespace SampleApp
{
class Program
{
static async Task Main()
{
Console.WriteLine("開始");
await Task.Delay(2000);
Console.WriteLine("2秒後に終了");
}
}
}
</code></pre>
</article>
</section>
</main>
<!-- フッター -->
<footer>
<p>© 2025 C# Tutorial Site</p>
</footer>
<!-- ★ クイズ判定用JavaScriptをまとめて定義 ★ -->
<script>
// 正解のマッピング(q1,q2,q3,...)
const correctAnswers = {
q1: "C", // static void Main(string[] args)
q2: "B", // doubleは整数型ではない
q3: "C" // try / catch / finally
};
function gradeQuiz() {
const form = document.getElementById("quizForm");
let score = 0;
let total = Object.keys(correctAnswers).length;
// 各問題について、ラジオボタンの選択値をチェック
for(const [question, answer] of Object.entries(correctAnswers)) {
const userAnswer = form.elements[question].value;
if(userAnswer === answer) {
score++;
}
}
// 結果表示
const resultDiv = document.getElementById("result");
resultDiv.innerHTML = `
<h3>採点結果: ${score} / ${total} 正解</h3>
<p>${getComment(score, total)}</p>
`;
}
// 点数に応じたコメントを返す
function getComment(score, total) {
if(score === total) {
return "パーフェクト!素晴らしいです!";
} else if(score >= total - 1) {
return "惜しい!あともう少し!";
} else {
return "まだまだ勉強が必要です。がんばりましょう!";
}
}
</script>
</body>
</html>
Gamestore
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>GameStore Ultra - Upload & Persist Data</title>
<meta name="description" content="高度なインタラクションと洗練されたデザイン、アップロードデータの保持機能を実現したゲームストアサイト。">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
/*────────────────────────────
グローバルリセット&変数設定
────────────────────────────*/
:root {
--primary: #ff6600;
--accent: #e55a00;
--secondary: #222;
--light: #fff;
--dark: #333;
--text: #333;
--shadow: 0 2px 8px rgba(0,0,0,0.15);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { font-family: 'Helvetica Neue', Arial, sans-serif; background: #f8f8f8; color: var(--text); }
a { text-decoration: none; color: inherit; }
img { display: block; max-width: 100%; height: auto; }
/*────────────────────────────
ヘッダー&ナビゲーション
────────────────────────────*/
header {
background: var(--secondary);
color: var(--light);
padding: 10px 20px;
position: sticky;
top: 0;
z-index: 1000;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: var(--shadow);
}
.logo { display: flex; align-items: center; gap: 10px; }
.logo img { width: 40px; height: 40px; }
.logo h1 { font-size: 24px; }
nav { background: var(--dark); }
.nav-container {
max-width: 1200px;
margin: auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
}
.nav-menu { list-style: none; display: flex; gap: 20px; }
.nav-menu li { position: relative; }
.nav-menu li a {
color: var(--light);
padding: 8px 10px;
transition: background 0.3s;
}
.nav-menu li a:hover { background: #444; }
/* ドロップダウン */
.dropdown {
position: absolute;
top: 100%;
left: 0;
background: var(--dark);
display: none;
flex-direction: column;
min-width: 150px;
}
.dropdown li a { padding: 8px 10px; }
.nav-menu li:hover .dropdown { display: flex; }
/* アップロードリンク(管理者向け) */
.nav-menu li.upload-link { margin-left: auto; }
/* ハンバーガーメニュー(モバイル) */
.hamburger {
display: none;
flex-direction: column;
gap: 4px;
cursor: pointer;
}
.hamburger span { width: 25px; height: 3px; background: var(--light); }
@media (max-width: 768px) {
.nav-menu { display: none; flex-direction: column; width: 100%; }
.nav-menu.active { display: flex; }
.hamburger { display: flex; }
.nav-container { flex-direction: column; align-items: flex-start; }
}
.search-container { position: relative; }
.search-container input[type="text"] {
padding: 6px 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.search-container button {
position: absolute;
right: 0;
top: 0;
bottom: 0;
background: var(--primary);
border: none;
padding: 6px 10px;
border-radius: 0 4px 4px 0;
color: var(--light);
cursor: pointer;
}
/*────────────────────────────
ヒーロースライダー
────────────────────────────*/
.hero-slider {
position: relative;
overflow: hidden;
height: 600px;
}
.hero-slide {
position: absolute;
top: 0; left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
opacity: 0;
transition: opacity 1s ease;
display: flex;
align-items: center;
justify-content: center;
}
.hero-slide.active { opacity: 1; position: relative; }
.hero-content {
background: rgba(0, 0, 0, 0.6);
padding: 20px 30px;
border-radius: 8px;
text-align: center;
color: var(--light);
}
.hero-content h2 { font-size: 48px; margin-bottom: 15px; }
.hero-content p { font-size: 20px; margin-bottom: 20px; }
.hero-content .btn {
background: var(--primary);
padding: 12px 24px;
border-radius: 4px;
transition: background 0.3s;
}
.hero-content .btn:hover { background: var(--accent); }
.hero-controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 8px;
}
.hero-control {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--light);
opacity: 0.5;
cursor: pointer;
}
.hero-control.active { opacity: 1; }
/*────────────────────────────
スクロール時フェードインアニメーション
────────────────────────────*/
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.fade-in.visible { opacity: 1; transform: none; }
/*────────────────────────────
セクション共通スタイル
────────────────────────────*/
section {
padding: 60px 20px;
max-width: 1200px;
margin: auto;
}
.section-title {
font-size: 32px;
margin-bottom: 20px;
border-left: 5px solid var(--primary);
padding-left: 10px;
}
.section-description {
font-size: 18px;
margin-bottom: 30px;
color: #666;
}
/*────────────────────────────
ゲームカードグリッド
────────────────────────────*/
.game-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
}
.game-card {
background: var(--light);
border-radius: 8px;
box-shadow: var(--shadow);
overflow: hidden;
cursor: pointer;
position: relative;
transition: transform 0.3s;
}
.game-card:hover { transform: translateY(-5px); }
.game-card img {
width: 100%;
height: 180px;
object-fit: cover;
}
.game-card-content { padding: 15px; }
.game-card-content h3 { font-size: 22px; margin-bottom: 10px; }
.game-card-content p { font-size: 16px; color: #666; margin-bottom: 10px; }
.game-card-content .price { font-size: 20px; color: var(--primary); }
.card-overlay {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.3);
opacity: 0;
transition: opacity 0.3s;
}
.game-card:hover .card-overlay { opacity: 1; }
/*────────────────────────────
カテゴリセクション
────────────────────────────*/
.categories {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 20px;
}
.category-card {
position: relative;
border-radius: 8px;
overflow: hidden;
cursor: pointer;
}
.category-card img {
width: 100%;
height: 150px;
object-fit: cover;
filter: brightness(70%);
transition: filter 0.3s;
}
.category-card:hover img { filter: brightness(100%); }
.category-card span {
position: absolute;
bottom: 10px;
left: 10px;
background: rgba(0,0,0,0.5);
color: var(--light);
padding: 8px 12px;
border-radius: 4px;
font-size: 18px;
}
/*────────────────────────────
ユーザーレビュー・スライダー
────────────────────────────*/
.reviews-slider {
position: relative;
overflow: hidden;
padding: 20px 0;
}
.reviews-track {
display: flex;
gap: 20px;
transition: transform 0.5s ease-in-out;
}
.review-card {
background: var(--light);
border-radius: 8px;
box-shadow: var(--shadow);
padding: 20px;
min-width: 320px;
}
.review-card h4 { font-size: 20px; margin-bottom: 10px; }
.review-card .rating { margin-bottom: 10px; }
.review-card .rating span { color: gold; font-size: 22px; }
.review-card p { font-size: 16px; color: #444; }
/*────────────────────────────
ブログセクション
────────────────────────────*/
.blog-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.blog-card {
background: var(--light);
border-radius: 8px;
box-shadow: var(--shadow);
overflow: hidden;
}
.blog-card img {
width: 100%;
height: 200px;
object-fit: cover;
}
.blog-card-content { padding: 15px; }
.blog-card-content h3 { font-size: 22px; margin-bottom: 10px; }
.blog-card-content p { font-size: 16px; color: #666; margin-bottom: 10px; }
.blog-card-content .btn {
background: var(--primary);
padding: 8px 16px;
border-radius: 4px;
transition: background 0.3s;
font-size: 16px;
}
.blog-card-content .btn:hover { background: var(--accent); }
/*────────────────────────────
モーダル(商品詳細、ログイン、登録、カート、アップロード)
────────────────────────────*/
.modal-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.6);
display: none;
align-items: center;
justify-content: center;
z-index: 2000;
}
.modal-overlay.active { display: flex; }
.modal {
background: var(--light);
max-width: 600px;
width: 90%;
border-radius: 8px;
overflow: hidden;
animation: modalFade 0.3s;
position: relative;
}
@keyframes modalFade {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
.modal-header, .modal-footer { padding: 15px; background: var(--dark); color: var(--light); }
.modal-body { padding: 20px; }
.modal-close {
position: absolute;
top: 15px;
right: 15px;
font-size: 24px;
cursor: pointer;
color: var(--light);
}
/*────────────────────────────
Back-to-Top ボタン
────────────────────────────*/
#backToTop {
position: fixed;
bottom: 40px;
right: 40px;
background: var(--primary);
color: var(--light);
padding: 12px;
border-radius: 50%;
display: none;
cursor: pointer;
box-shadow: var(--shadow);
transition: background 0.3s;
z-index: 1500;
}
#backToTop:hover { background: var(--accent); }
/*────────────────────────────
フィルターサイドバー(デスクトップ)
────────────────────────────*/
.filter-sidebar {
position: fixed;
top: 100px;
left: 20px;
width: 250px;
background: var(--light);
border: 1px solid #ccc;
border-radius: 8px;
padding: 20px;
box-shadow: var(--shadow);
z-index: 1200;
}
.filter-sidebar h3 { font-size: 20px; margin-bottom: 15px; }
.filter-sidebar label { display: block; margin-bottom: 8px; font-size: 16px; }
.filter-sidebar input, .filter-sidebar select {
width: 100%;
padding: 6px 8px;
margin-bottom: 12px;
border: 1px solid #ccc;
border-radius: 4px;
}
.filter-sidebar button {
width: 100%;
padding: 10px;
background: var(--primary);
border: none;
color: var(--light);
border-radius: 4px;
font-size: 16px;
transition: background 0.3s;
cursor: pointer;
}
.filter-sidebar button:hover { background: var(--accent); }
@media (max-width: 1024px) { .filter-sidebar { display: none; } }
</style>
</head>
<body>
<!--────────────────────────────
ヘッダー
────────────────────────────-->
<header>
<div class="logo">
<img src="images/logo.png" alt="GameStore Logo" loading="lazy">
<h1>GameStore Ultra</h1>
</div>
<div class="hamburger" id="hamburger">
<span></span>
<span></span>
<span></span>
</div>
</header>
<!--────────────────────────────
ナビゲーション
────────────────────────────-->
<nav>
<div class="nav-container">
<ul class="nav-menu" id="navMenu">
<li><a href="#">ホーム</a></li>
<li>
<a href="#">新作</a>
<ul class="dropdown">
<li><a href="#">今週の新作</a></li>
<li><a href="#">今月の新作</a></li>
</ul>
</li>
<li>
<a href="#">人気タイトル</a>
<ul class="dropdown">
<li><a href="#">ランキング</a></li>
<li><a href="#">レビュー</a></li>
</ul>
</li>
<li><a href="#">セール</a></li>
<li><a href="#">ブログ</a></li>
<li class="upload-link"><a href="#" id="uploadLink">アップロード</a></li>
</ul>
<div class="search-container">
<input type="text" placeholder="ゲームを検索..." id="searchInput">
<button id="searchButton">検索</button>
</div>
</div>
</nav>
<!--────────────────────────────
フィルターサイドバー(デスクトップ用)
────────────────────────────-->
<aside class="filter-sidebar" id="filterSidebar">
<h3>フィルター</h3>
<label for="filterGenre">ジャンル:</label>
<select id="filterGenre">
<option value="">すべて</option>
<option value="アクション">アクション</option>
<option value="RPG">RPG</option>
<option value="シューティング">シューティング</option>
<option value="パズル">パズル</option>
</select>
<label for="filterPrice">価格上限:</label>
<input type="number" id="filterPrice" placeholder="例: 5000">
<button id="applyFilter">フィルター適用</button>
</aside>
<!--────────────────────────────
ヒーロースライダー
────────────────────────────-->
<div class="hero-slider" id="heroSlider">
<div class="hero-slide active" style="background-image: url('images/hero1.jpg');">
<div class="hero-content">
<h2>最新作が続々登場!</h2>
<p>冒険、戦略、アクション…全てのゲームがここに。</p>
<a href="#" class="btn">詳しく見る</a>
</div>
</div>
<div class="hero-slide" style="background-image: url('images/hero2.jpg');">
<div class="hero-content">
<h2>セール開催中!</h2>
<p>期間限定、最大50%OFF!</p>
<a href="#" class="btn">セールを見る</a>
</div>
</div>
<div class="hero-slide" style="background-image: url('images/hero3.jpg');">
<div class="hero-content">
<h2>ユーザーレビューで選ぶ</h2>
<p>実際の評価を元に、あなたにピッタリのゲームを見つけよう。</p>
<a href="#" class="btn">レビューを見る</a>
</div>
</div>
<div class="hero-controls" id="heroControls"></div>
</div>
<!--────────────────────────────
新作ゲームセクション
────────────────────────────-->
<section id="new-releases" class="fade-in">
<h2 class="section-title">新作ゲーム</h2>
<p class="section-description">最新のゲームタイトルをチェック!</p>
<div class="game-grid" id="newReleasesGrid">
<div class="game-card" data-title="新作ゲーム1" data-genre="アクション" data-price="3500円" data-description="迫力のアクションと最新技術が融合した作品。">
<img src="images/new1.jpg" alt="新作ゲーム1" loading="lazy">
<div class="game-card-content">
<h3>新作ゲーム1</h3>
<p>ジャンル: アクション</p>
<p class="price">3500円</p>
</div>
<div class="card-overlay"></div>
</div>
<div class="game-card" data-title="新作ゲーム2" data-genre="RPG" data-price="4200円" data-description="壮大な物語と美しい世界観が魅力。">
<img src="images/new2.jpg" alt="新作ゲーム2" loading="lazy">
<div class="game-card-content">
<h3>新作ゲーム2</h3>
<p>ジャンル: RPG</p>
<p class="price">4200円</p>
</div>
<div class="card-overlay"></div>
</div>
<div class="game-card" data-title="新作ゲーム3" data-genre="シューティング" data-price="2900円" data-description="高速なアクションとリアルなグラフィックが特長。">
<img src="images/new3.jpg" alt="新作ゲーム3" loading="lazy">
<div class="game-card-content">
<h3>新作ゲーム3</h3>
<p>ジャンル: シューティング</p>
<p class="price">2900円</p>
</div>
<div class="card-overlay"></div>
</div>
<div class="game-card" data-title="新作ゲーム4" data-genre="パズル" data-price="1500円" data-description="頭脳を刺激する独自のパズル体験。">
<img src="images/new4.jpg" alt="新作ゲーム4" loading="lazy">
<div class="game-card-content">
<h3>新作ゲーム4</h3>
<p>ジャンル: パズル</p>
<p class="price">1500円</p>
</div>
<div class="card-overlay"></div>
</div>
</div>
</section>
<!--────────────────────────────
注目特集セクション
────────────────────────────-->
<section id="featured" class="fade-in">
<h2 class="section-title">注目の特集</h2>
<p class="section-description">今週の注目タイトルや特集記事をお届けします。</p>
<div class="game-grid">
<div class="game-card" data-title="特集タイトル1" data-genre="特集" data-price="記事を見る" data-description="最新トレンドを徹底解説。">
<img src="images/featured1.jpg" alt="特集1" loading="lazy">
<div class="game-card-content">
<h3>特集タイトル1</h3>
<p>期間限定特集:最新トレンド</p>
<p class="price">特集記事を見る</p>
</div>
<div class="card-overlay"></div>
</div>
<div class="game-card" data-title="特集タイトル2" data-genre="特集" data-price="記事を見る" data-description="インディーゲームの革新を追う。">
<img src="images/featured2.jpg" alt="特集2" loading="lazy">
<div class="game-card-content">
<h3>特集タイトル2</h3>
<p>話題のインディーゲーム特集</p>
<p class="price">特集記事を見る</p>
</div>
<div class="card-overlay"></div>
</div>
</div>
</section>
<!--────────────────────────────
アップロード済みゲーム表示(管理者アップロードデータ)
────────────────────────────-->
<section id="my-uploads" class="fade-in">
<h2 class="section-title">アップロードしたゲーム</h2>
<p class="section-description">管理者がアップロードしたゲームデータです。</p>
<div class="game-grid" id="myUploadsGrid"></div>
</section>
<!--────────────────────────────
ジャンル別セクション
────────────────────────────-->
<section id="categories" class="fade-in">
<h2 class="section-title">ジャンル別</h2>
<p class="section-description">お気に入りのジャンルからゲームを探してみましょう。</p>
<div class="categories">
<a href="#" class="category-card">
<img src="images/cat-action.jpg" alt="アクション" loading="lazy">
<span>アクション</span>
</a>
<a href="#" class="category-card">
<img src="images/cat-rpg.jpg" alt="RPG" loading="lazy">
<span>RPG</span>
</a>
<a href="#" class="category-card">
<img src="images/cat-shooting.jpg" alt="シューティング" loading="lazy">
<span>シューティング</span>
</a>
<a href="#" class="category-card">
<img src="images/cat-puzzle.jpg" alt="パズル" loading="lazy">
<span>パズル</span>
</a>
<a href="#" class="category-card">
<img src="images/cat-adventure.jpg" alt="アドベンチャー" loading="lazy">
<span>アドベンチャー</span>
</a>
</div>
</section>
<!--────────────────────────────
ユーザーレビューセクション
────────────────────────────-->
<section id="reviews" class="fade-in">
<h2 class="section-title">ユーザーレビュー</h2>
<p class="section-description">実際にプレイしたユーザーの評価をチェック!</p>
<div class="reviews-slider">
<div class="reviews-track">
<div class="review-card">
<h4>最高のアクション体験!</h4>
<div class="rating"><span>★★★★★</span></div>
<p>アクションゲームの決定版。グラフィックも音楽も最高でした。</p>
</div>
<div class="review-card">
<h4>壮大なストーリーに引き込まれる</h4>
<div class="rating"><span>★★★★☆</span></div>
<p>RPGの世界観が素晴らしく、ストーリーにのめり込みました。</p>
</div>
<div class="review-card">
<h4>クセになる操作感!</h4>
<div class="rating"><span>★★★★★</span></div>
<p>シューティングゲームとして非常に爽快な操作感で、何度も挑戦しました。</p>
</div>
</div>
</div>
</section>
<!--────────────────────────────
ブログセクション
────────────────────────────-->
<section id="blog" class="fade-in">
<h2 class="section-title">最新ブログ</h2>
<p class="section-description">ゲーム業界の最新情報や攻略記事をお届けします。</p>
<div class="blog-grid">
<div class="blog-card">
<img src="images/blog1.jpg" alt="ブログ記事1" loading="lazy">
<div class="blog-card-content">
<h3>2025年の注目ゲームトレンド</h3>
<p>今年注目すべきゲームの傾向と今後の展望について解説します。</p>
<a href="#" class="btn">続きを読む</a>
</div>
</div>
<div class="blog-card">
<img src="images/blog2.jpg" alt="ブログ記事2" loading="lazy">
<div class="blog-card-content">
<h3>セール攻略:お得に買う方法</h3>
<p>賢く買い物するための裏技やポイントを紹介します。</p>
<a href="#" class="btn">続きを読む</a>
</div>
</div>
<div class="blog-card">
<img src="images/blog3.jpg" alt="ブログ記事3" loading="lazy">
<div class="blog-card-content">
<h3>レビューで選ぶ!おすすめタイトル</h3>
<p>実際のユーザーレビューをもとに、人気タイトルをピックアップ。</p>
<a href="#" class="btn">続きを読む</a>
</div>
</div>
</div>
</section>
<!--────────────────────────────
商品詳細モーダル
────────────────────────────-->
<div class="modal-overlay" id="productModal">
<div class="modal">
<div class="modal-header">
<h2 id="modalTitle">ゲーム詳細</h2>
<span class="modal-close" id="modalClose">×</span>
</div>
<div class="modal-body" id="modalBody">
<p id="modalDescription">詳細情報がここに表示されます。</p>
<p><strong>ジャンル:</strong> <span id="modalGenre"></span></p>
<p><strong>価格:</strong> <span id="modalPrice"></span></p>
</div>
<div class="modal-footer">
<button class="btn" id="modalBuyButton">今すぐ購入</button>
</div>
</div>
</div>
<!--────────────────────────────
ログインモーダル
────────────────────────────-->
<div class="modal-overlay" id="loginModal">
<div class="modal">
<div class="modal-header">
<h2>ログイン</h2>
<span class="modal-close" id="loginClose">×</span>
</div>
<div class="modal-body">
<form id="loginForm">
<label for="loginUsername">ユーザー名:</label>
<input type="text" id="loginUsername" required>
<label for="loginPassword">パスワード:</label>
<input type="password" id="loginPassword" required>
<button class="btn" type="submit">ログイン</button>
</form>
</div>
</div>
</div>
<!--────────────────────────────
新規登録モーダル
────────────────────────────-->
<div class="modal-overlay" id="registerModal">
<div class="modal">
<div class="modal-header">
<h2>新規登録</h2>
<span class="modal-close" id="registerClose">×</span>
</div>
<div class="modal-body">
<form id="registerForm">
<label for="registerUsername">ユーザー名:</label>
<input type="text" id="registerUsername" required>
<label for="registerEmail">メールアドレス:</label>
<input type="email" id="registerEmail" required>
<label for="registerPassword">パスワード:</label>
<input type="password" id="registerPassword" required>
<button class="btn" type="submit">登録</button>
</form>
</div>
</div>
</div>
<!--────────────────────────────
カートモーダル
────────────────────────────-->
<div class="modal-overlay" id="cartModal">
<div class="modal">
<div class="modal-header">
<h2>カート</h2>
<span class="modal-close" id="cartClose">×</span>
</div>
<div class="modal-body" id="cartBody">
<p>カートにアイテムはありません。</p>
</div>
<div class="modal-footer">
<button class="btn" id="checkoutButton">チェックアウト</button>
</div>
</div>
</div>
<!--────────────────────────────
アップロードモーダル(管理者向け)
────────────────────────────-->
<div class="modal-overlay" id="uploadModal">
<div class="modal">
<div class="modal-header">
<h2>ゲームアップロード</h2>
<span class="modal-close" id="uploadClose">×</span>
</div>
<div class="modal-body">
<form id="uploadForm" enctype="multipart/form-data">
<label for="uploadTitle">ゲームタイトル:</label>
<input type="text" id="uploadTitle" required>
<label for="uploadGenre">ジャンル:</label>
<input type="text" id="uploadGenre" required>
<label for="uploadPrice">価格:</label>
<input type="number" id="uploadPrice" required>
<label for="uploadDescription">説明:</label>
<textarea id="uploadDescription" rows="4" required></textarea>
<label for="uploadFile">画像アップロード:</label>
<input type="file" id="uploadFile" accept="image/*" required>
<button class="btn" type="submit">アップロード</button>
</form>
</div>
</div>
</div>
<!--────────────────────────────
フローティングカートボタン
────────────────────────────-->
<button id="cartButton" style="position: fixed; bottom: 100px; right: 40px; background: var(--primary); color: var(--light); padding:12px; border:none; border-radius:50%; box-shadow: var(--shadow); cursor:pointer; z-index:1500;">
🛒
</button>
<!--────────────────────────────
Back-to-Top ボタン
────────────────────────────-->
<div id="backToTop">⇧</div>
<!--────────────────────────────
JavaScript(各種機能の実装)
────────────────────────────-->
<script>
// ハンバーガーメニューの切替
const hamburger = document.getElementById('hamburger');
const navMenu = document.getElementById('navMenu');
hamburger.addEventListener('click', () => {
navMenu.classList.toggle('active');
});
// ヒーロースライダー(自動・手動切替)
const slides = document.querySelectorAll('.hero-slide');
const heroControls = document.getElementById('heroControls');
let currentSlide = 0;
slides.forEach((_, index) => {
const dot = document.createElement('div');
dot.classList.add('hero-control');
if(index === 0) dot.classList.add('active');
dot.addEventListener('click', () => showSlide(index));
heroControls.appendChild(dot);
});
function showSlide(index) {
slides[currentSlide].classList.remove('active');
heroControls.children[currentSlide].classList.remove('active');
currentSlide = index;
slides[currentSlide].classList.add('active');
heroControls.children[currentSlide].classList.add('active');
}
setInterval(() => {
let next = (currentSlide + 1) % slides.length;
showSlide(next);
}, 5000);
// Back-to-Top ボタン表示
const backToTop = document.getElementById('backToTop');
window.addEventListener('scroll', () => {
backToTop.style.display = window.scrollY > 300 ? 'block' : 'none';
});
backToTop.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
// IntersectionObserver によるフェードイン効果
const faders = document.querySelectorAll('.fade-in');
const appearOptions = { threshold: 0.1 };
const appearOnScroll = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, appearOptions);
faders.forEach(fader => appearOnScroll.observe(fader));
// 商品詳細モーダル
const gameCards = document.querySelectorAll('.game-card');
const productModal = document.getElementById('productModal');
const modalTitle = document.getElementById('modalTitle');
const modalDescription = document.getElementById('modalDescription');
const modalGenre = document.getElementById('modalGenre');
const modalPrice = document.getElementById('modalPrice');
const modalClose = document.getElementById('modalClose');
gameCards.forEach(card => {
card.addEventListener('click', () => {
modalTitle.textContent = card.getAttribute('data-title');
modalGenre.textContent = card.getAttribute('data-genre');
modalPrice.textContent = card.getAttribute('data-price');
modalDescription.textContent = card.getAttribute('data-description');
productModal.classList.add('active');
});
});
modalClose.addEventListener('click', () => productModal.classList.remove('active'));
productModal.addEventListener('click', (e) => { if(e.target === productModal) productModal.classList.remove('active'); });
// 検索フィルター
const searchInput = document.getElementById('searchInput');
const searchButton = document.getElementById('searchButton');
searchButton.addEventListener('click', () => {
const query = searchInput.value.toLowerCase();
const cards = document.querySelectorAll('.game-card');
cards.forEach(card => {
const title = card.getAttribute('data-title').toLowerCase();
const genre = card.getAttribute('data-genre').toLowerCase();
card.style.display = (title.includes(query) || genre.includes(query)) ? 'block' : 'none';
});
});
// フィルターサイドバー機能
const filterGenre = document.getElementById('filterGenre');
const filterPrice = document.getElementById('filterPrice');
const applyFilter = document.getElementById('applyFilter');
applyFilter.addEventListener('click', () => {
const genreValue = filterGenre.value;
const priceValue = filterPrice.value;
const cards = document.querySelectorAll('.game-card');
cards.forEach(card => {
const cardGenre = card.getAttribute('data-genre');
const cardPrice = parseInt(card.getAttribute('data-price')) || 0;
let matchesGenre = genreValue === "" || cardGenre === genreValue;
let matchesPrice = !priceValue || (cardPrice && cardPrice <= parseInt(priceValue));
card.style.display = (matchesGenre && matchesPrice) ? 'block' : 'none';
});
});
// ログインモーダル
const loginModal = document.getElementById('loginModal');
const loginClose = document.getElementById('loginClose');
loginClose.addEventListener('click', () => loginModal.classList.remove('active'));
loginModal.addEventListener('click', (e) => { if(e.target === loginModal) loginModal.classList.remove('active'); });
// 新規登録モーダル
const registerModal = document.getElementById('registerModal');
const registerClose = document.getElementById('registerClose');
registerClose.addEventListener('click', () => registerModal.classList.remove('active'));
registerModal.addEventListener('click', (e) => { if(e.target === registerModal) registerModal.classList.remove('active'); });
// カートモーダル
const cartModal = document.getElementById('cartModal');
const cartClose = document.getElementById('cartClose');
const cartButton = document.getElementById('cartButton');
cartButton.addEventListener('click', () => cartModal.classList.add('active'));
cartClose.addEventListener('click', () => cartModal.classList.remove('active'));
cartModal.addEventListener('click', (e) => { if(e.target === cartModal) cartModal.classList.remove('active'); });
// アップロードモーダル(管理者向け)
const uploadModal = document.getElementById('uploadModal');
const uploadLink = document.getElementById('uploadLink');
const uploadClose = document.getElementById('uploadClose');
uploadLink.addEventListener('click', (e) => {
e.preventDefault();
uploadModal.classList.add('active');
});
uploadClose.addEventListener('click', () => uploadModal.classList.remove('active'));
uploadModal.addEventListener('click', (e) => { if(e.target === uploadModal) uploadModal.classList.remove('active'); });
// アップロードフォーム送信(アップロードデータをlocalStorageへ保存)
const uploadForm = document.getElementById('uploadForm');
uploadForm.addEventListener('submit', (e) => {
e.preventDefault();
const title = document.getElementById('uploadTitle').value;
const genre = document.getElementById('uploadGenre').value;
const price = document.getElementById('uploadPrice').value + "円";
const description = document.getElementById('uploadDescription').value;
const fileInput = document.getElementById('uploadFile');
const file = fileInput.files[0];
if(file) {
const reader = new FileReader();
reader.onload = function(event) {
const imageData = event.target.result;
const gameData = { title, genre, price, description, image: imageData };
let uploadedGames = JSON.parse(localStorage.getItem('uploadedGames')) || [];
uploadedGames.push(gameData);
localStorage.setItem('uploadedGames', JSON.stringify(uploadedGames));
loadUploadedGames();
alert('アップロードが完了しました。');
uploadModal.classList.remove('active');
uploadForm.reset();
};
reader.readAsDataURL(file);
}
});
// アップロード済みゲームを読み込み表示する(「アップロードしたゲーム」セクション)
function loadUploadedGames() {
const grid = document.getElementById('myUploadsGrid');
let uploadedGames = JSON.parse(localStorage.getItem('uploadedGames')) || [];
grid.innerHTML = "";
uploadedGames.forEach(game => {
const card = document.createElement('div');
card.classList.add('game-card');
card.setAttribute('data-title', game.title);
card.setAttribute('data-genre', game.genre);
card.setAttribute('data-price', game.price);
card.setAttribute('data-description', game.description);
card.innerHTML = `
<img src="${game.image}" alt="${game.title}" loading="lazy">
<div class="game-card-content">
<h3>${game.title}</h3>
<p>ジャンル: ${game.genre}</p>
<p class="price">${game.price}</p>
</div>
<div class="card-overlay"></div>
`;
card.addEventListener('click', () => {
modalTitle.textContent = game.title;
modalGenre.textContent = game.genre;
modalPrice.textContent = game.price;
modalDescription.textContent = game.description;
productModal.classList.add('active');
});
grid.appendChild(card);
});
}
// ページ読み込み時にアップロード済みゲームを表示
loadUploadedGames();
</script>
</body>
<footer>
<div class="footer-container">
<div class="footer-col">
<h4>会社情報</h4>
<ul>
<li><a href="#">企業理念</a></li>
<li><a href="#">採用情報</a></li>
<li><a href="#">プレスリリース</a></li>
</ul>
</div>
<div class="footer-col">
<h4>サポート</h4>
<ul>
<li><a href="#">お問い合わせ</a></li>
<li><a href="#">FAQ</a></li>
<li><a href="#">利用規約</a></li>
</ul>
</div>
<div class="footer-col">
<h4>ニュースレター</h4>
<p>最新情報をメールで受け取る</p>
<form class="subscribe-form">
<input type="email" placeholder="メールアドレス" required>
<button type="submit">登録</button>
</form>
</div>
</div>
<div class="footer-bottom">© 2025 GameStore Ultra. All rights reserved.</div>
</footer>
</html>
