匿名相談・共感プラットフォーム

<!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: 'Segoe UI', sans-serif; background: #eef1f5; margin: 0; padding: 20px; }
    header { text-align: center; padding: 20px; background: #4a90e2; color: white; border-radius: 8px; }
    .container { max-width: 800px; margin: 20px auto; }
    .card { background: white; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 15px; margin-bottom: 20px; }
    textarea, input[type="text"] { width: 100%; padding: 10px; margin: 5px 0; border: 1px solid #ccc; border-radius: 5px; }
    button { background: #4a90e2; color: white; border: none; padding: 10px 15px; border-radius: 5px; cursor: pointer; margin-right: 5px; }
    button:hover { background: #357ab8; }
    .comment { background: #f5f5f5; border-left: 3px solid #4a90e2; padding: 8px; margin-top: 8px; border-radius: 5px; }
    .meta { font-size: 0.85em; color: #555; }
    .tag { display: inline-block; background: #ddd; border-radius: 5px; padding: 2px 8px; margin-right: 5px; font-size: 0.8em; }
    footer { text-align: center; font-size: 0.8em; color: #888; margin-top: 50px; }
    .search-bar { margin-bottom: 20px; }
    .delete-btn { background: #e94e4e; }
    .delete-btn:hover { background: #b73939; }
    .edit-btn { background: #f0ad4e; }
    .edit-btn:hover { background: #d98c00; }
    .copy-btn { background: #6cc070; }
    .copy-btn:hover { background: #4e9f50; }
  </style>
</head>
<body>

  <header>
    <h1>匿名相談・共感プラットフォーム</h1>
    <p>悩みや相談を匿名で投稿し、共感やコメントをもらおう</p>
  </header>

  <div class="container">
    <div class="card">
      <h3>新しい相談を投稿</h3>
      <input type="text" id="postTags" placeholder="タグ(カンマ区切り)">
      <textarea id="postText" placeholder="あなたの悩みや相談を書いてください..."></textarea>
      <button onclick="addPost()">投稿する</button>
    </div>

    <div class="card search-bar">
      <input type="text" id="searchInput" placeholder="投稿検索..." oninput="searchPosts()">
    </div>

    <div id="postsContainer"></div>
  </div>

  <footer>
    &copy; 2025 匿名相談・共感プラットフォーム
  </footer>

  <script>
    let posts = JSON.parse(localStorage.getItem('posts')) || [];

    function addPost() {
      const text = document.getElementById('postText').value.trim();
      const tags = document.getElementById('postTags').value.split(',').map(tag => tag.trim()).filter(tag => tag);
      if (text) {
        posts.unshift({ text, tags, empathy: 0, comments: [], date: new Date().toLocaleString() });
        document.getElementById('postText').value = '';
        document.getElementById('postTags').value = '';
        saveAndRender();
      }
    }

    function addEmpathy(index) {
      posts[index].empathy++;
      saveAndRender();
    }

    function addComment(index, commentId) {
      const input = document.getElementById(commentId);
      const commentText = input.value.trim();
      if (commentText) {
        posts[index].comments.push({ text: commentText, date: new Date().toLocaleString() });
        input.value = '';
        saveAndRender();
      }
    }

    function editPost(index) {
      const newText = prompt('投稿内容を編集してください:', posts[index].text);
      if (newText !== null) {
        posts[index].text = newText;
        saveAndRender();
      }
    }

    function deletePost(index) {
      if (confirm('この投稿を削除しますか?')) {
        posts.splice(index, 1);
        saveAndRender();
      }
    }

    function copyPost(index) {
      navigator.clipboard.writeText(posts[index].text)
        .then(() => alert('投稿内容をコピーしました'))
        .catch(() => alert('コピーに失敗しました'));
    }

    function saveAndRender() {
      localStorage.setItem('posts', JSON.stringify(posts));
      renderPosts();
    }

    function renderPosts(filteredPosts = posts) {
      const container = document.getElementById('postsContainer');
      container.innerHTML = '';

      filteredPosts.forEach((post, index) => {
        let postHtml = `
          <div class="card">
            <p>${post.text}</p>
            <div>${post.tags.map(tag => `<span class='tag'>#${tag}</span>`).join(' ')}</div>
            <p class="meta">投稿日: ${post.date}</p>
            <button onclick="addEmpathy(${index})">共感 (${post.empathy})</button>
            <button class="copy-btn" onclick="copyPost(${index})">コピー</button>
            <button class="edit-btn" onclick="editPost(${index})">編集</button>
            <button class="delete-btn" onclick="deletePost(${index})">削除</button>
            <h4>コメント</h4>
            <input type="text" id="commentInput${index}" placeholder="コメントを書く...">
            <button onclick="addComment(${index}, 'commentInput${index}')">送信</button>
        `;

        post.comments.forEach(comment => {
          postHtml += `<div class="comment">${comment.text}<div class="meta">${comment.date}</div></div>`;
        });

        postHtml += '</div>';
        container.innerHTML += postHtml;
      });
    }

    function searchPosts() {
      const query = document.getElementById('searchInput').value.toLowerCase();
      const filtered = posts.filter(post =>
        post.text.toLowerCase().includes(query) ||
        post.tags.some(tag => tag.toLowerCase().includes(query))
      );
      renderPosts(filtered);
    }

    renderPosts();
  </script>

</body>
</html>

AIキャラクター掲示板

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>AIキャラクター掲示板</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      background: #e0e0e0;
      margin: 0;
      padding: 0;
    }
    header {
      background: #3949ab;
      color: white;
      text-align: center;
      padding: 15px;
    }
    .container {
      max-width: 800px;
      margin: 20px auto;
      background: white;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 4px 10px rgba(0,0,0,0.1);
    }
    .post {
      border-bottom: 1px solid #ddd;
      padding: 10px 0;
      display: flex;
      align-items: flex-start;
    }
    .avatar {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      margin-right: 10px;
    }
    .post-content {
      flex-grow: 1;
    }
    .character {
      font-weight: bold;
      color: #3949ab;
    }
    .timestamp {
      font-size: 0.8em;
      color: gray;
    }
    .message {
      margin: 5px 0;
    }
    .likes {
      font-size: 0.9em;
      cursor: pointer;
      color: #ff5722;
    }
    .input-group {
      margin-top: 20px;
    }
    input, textarea {
      width: 100%;
      padding: 8px;
      margin-top: 5px;
      border-radius: 4px;
      border: 1px solid #ccc;
      box-sizing: border-box;
    }
    button {
      margin-top: 10px;
      padding: 10px 15px;
      background: #3949ab;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
    button:hover {
      background: #303f9f;
    }
  </style>
</head>
<body>

<header>
  <h1>AIキャラクター掲示板</h1>
  <p>投稿数: <span id="postCount">0</span></p>
</header>

<div class="container" id="board"></div>

<div class="container input-group">
  <label>あなたの名前:</label>
  <input type="text" id="username" placeholder="例: カナタ">
  
  <label>メッセージ:</label>
  <textarea id="userMessage" placeholder="メッセージを入力..."></textarea>
  
  <button onclick="postMessage()">投稿</button>
</div>

<script>
  let postCounter = 0;

  const aiCharacters = [
    { name: 'ネオン', avatar: 'https://via.placeholder.com/40/673ab7/ffffff?text=N', replies: ['それ面白いね!', '詳しく教えて!', 'すごい!'] },
    { name: 'ルナ', avatar: 'https://via.placeholder.com/40/ff4081/ffffff?text=L', replies: ['なるほど!', '私もそう思うわ。', '参考になったわ。'] },
    { name: 'ソラ', avatar: 'https://via.placeholder.com/40/03a9f4/ffffff?text=S', replies: ['良い話だね!', '素敵だ!', 'もっと教えて!'] }
  ];

  function postMessage() {
    const username = document.getElementById('username').value || '名無しさん';
    const message = document.getElementById('userMessage').value.trim();
    if (!message) return;

    addPost(username, message, 'https://via.placeholder.com/40/9e9e9e/ffffff?text=U');
    document.getElementById('userMessage').value = '';
    aiCharacters.forEach((char, index) => {
      setTimeout(() => aiReply(char), 1000 * (index + 1));
    });
  }

  function addPost(user, message, avatarUrl) {
    postCounter++;
    document.getElementById('postCount').textContent = postCounter;

    const board = document.getElementById('board');
    const time = new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
    const post = document.createElement('div');
    post.className = 'post';
    post.innerHTML = `
      <img src="${avatarUrl}" alt="avatar" class="avatar">
      <div class="post-content">
        <span class="character">${user}</span> <span class="timestamp">[${time}]</span>
        <p class="message">${message}</p>
        <span class="likes" onclick="likePost(this)">♡ いいね</span>
      </div>`;
    board.appendChild(post);
  }

  function aiReply(character) {
    const reply = character.replies[Math.floor(Math.random() * character.replies.length)];
    addPost(character.name, reply, character.avatar);
  }

  function likePost(element) {
    let current = element.textContent;
    if (current.includes('♡')) {
      element.textContent = current.replace('♡', '❤️');
    }
  }
</script>

</body>
</html>

ギルド掲示板

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>仮想ギルド掲示板「ドラゴンズクレスト」</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    :root {
      --bg: #1c1c2b;
      --panel: #2e2e4d;
      --card: #3a3a5f;
      --accent: #6c63ff;
      --text: #fff;
    }

    body {
      margin: 0;
      font-family: "Segoe UI", sans-serif;
      background: var(--bg);
      color: var(--text);
    }

    header {
      background: var(--panel);
      padding: 20px;
      text-align: center;
      font-size: 1.8em;
    }

    nav {
      display: flex;
      justify-content: center;
      gap: 20px;
      background: var(--card);
      padding: 10px;
    }

    nav button {
      background: transparent;
      border: none;
      color: #ddd;
      font-size: 1em;
      padding: 10px 20px;
      cursor: pointer;
    }

    nav button.active {
      border-bottom: 2px solid var(--accent);
      color: var(--accent);
    }

    .container {
      display: grid;
      grid-template-columns: 1fr 250px;
      gap: 20px;
      max-width: 1200px;
      margin: 20px auto;
      padding: 0 10px;
    }

    .main {
      background: var(--card);
      padding: 20px;
      border-radius: 12px;
    }

    .sidebar {
      background: var(--card);
      padding: 15px;
      border-radius: 12px;
    }

    .post-form textarea {
      width: 100%;
      height: 80px;
      border-radius: 6px;
      padding: 10px;
      border: none;
      resize: vertical;
    }

    .post-form button {
      margin-top: 10px;
      padding: 10px 20px;
      background: var(--accent);
      border: none;
      border-radius: 6px;
      color: #fff;
      cursor: pointer;
    }

    .post {
      background: #404070;
      border-radius: 10px;
      padding: 15px;
      margin-top: 15px;
      display: flex;
      gap: 10px;
      flex-direction: row;
    }

    .avatar {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      background: #6c63ff;
      display: flex;
      align-items: center;
      justify-content: center;
      font-weight: bold;
    }

    .post-content {
      flex: 1;
    }

    .meta {
      font-size: 0.9em;
      color: #aaa;
      margin-bottom: 5px;
    }

    .actions button {
      margin-right: 8px;
      background: #5a5a8f;
      border: none;
      color: white;
      padding: 4px 10px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 0.8em;
    }

    .reply-area {
      margin-top: 10px;
    }

    .reply-area textarea {
      width: 100%;
      height: 50px;
      margin-top: 5px;
      padding: 6px;
      border-radius: 4px;
      border: none;
    }

    .reply {
      margin-top: 5px;
      font-size: 0.85em;
      color: #ddd;
    }

    .guild-info, .members, .guild-schedule, .guild-rules {
      margin-bottom: 20px;
    }

    .members ul {
      list-style: none;
      padding: 0;
    }

    .members li {
      padding: 5px 0;
    }

    .members li.online::before {
      content: "●";
      color: #7fff7f;
      margin-right: 5px;
    }

    .members li.offline::before {
      content: "●";
      color: #888;
      margin-right: 5px;
    }

    @media (max-width: 800px) {
      .container {
        grid-template-columns: 1fr;
      }
    }
  </style>
</head>
<body>
  <header>仮想ギルド掲示板「ドラゴンズクレスト」</header>
  <nav>
    <button class="tab-button active" onclick="switchTab('general')">🏰 雑談</button>
    <button class="tab-button" onclick="switchTab('raid')">⚔️ レイド</button>
    <button class="tab-button" onclick="switchTab('trade')">💰 取引</button>
  </nav>
  <div class="container">
    <div class="main">
      <div class="post-form">
        <textarea id="postText" placeholder="ギルドのみんなに伝えたいことは?"></textarea>
        <button onclick="postMessage()">投稿する</button>
      </div>
      <div id="general" class="tab-content">
        <!-- 投稿はここに追加されます -->
      </div>
      <div id="raid" class="tab-content" style="display:none;"></div>
      <div id="trade" class="tab-content" style="display:none;"></div>
    </div>
    <div class="sidebar">
      <div class="guild-info">
        <h3>ギルド情報</h3>
        <p>設立:2025年<br>メンバー数:32名<br>ギルマス:Reina</p>
      </div>
      <div class="guild-schedule">
        <h3>今週のスケジュール</h3>
        <ul>
          <li>🗓️ 7月12日(土) 21:00〜:レイド「闇の塔」</li>
          <li>🗓️ 7月14日(月) 22:00〜:PvP練習会</li>
          <li>🗓️ 7月16日(水) 20:00〜:定例会議</li>
        </ul>
      </div>
      <div class="guild-rules">
        <h3>ギルドルール</h3>
        <ol>
          <li>他プレイヤーへの迷惑行為禁止</li>
          <li>無断脱退・長期不在時は一言ください</li>
          <li>レイド参加は20分前集合!</li>
        </ol>
      </div>
      <div class="members">
        <h3>メンバー(抜粋)</h3>
        <ul>
          <li class="online">Reina</li>
          <li class="online">Shiro</li>
          <li class="offline">Yuna</li>
          <li class="online">Kuro</li>
          <li class="offline">Mika</li>
        </ul>
      </div>
    </div>
  </div>
  <script>
    function postMessage() {
      const textarea = document.getElementById("postText");
      const content = textarea.value.trim();
      if (!content) return;

      const now = new Date().toLocaleString("ja-JP", { hour12: false });
      const post = document.createElement("div");
      post.className = "post";
      post.innerHTML = `
        <div class="avatar">Y</div>
        <div class="post-content">
          <div class="meta">あなた | ${now}</div>
          <div class="content">${content.replace(/\n/g, "<br>")}</div>
          <div class="actions">
            <button onclick="likePost(this)">👍 <span>0</span></button>
            <button onclick="toggleReply(this)">💬 返信</button>
            <button onclick="deletePost(this)">🗑 削除</button>
          </div>
          <div class="reply-area" style="display:none;">
            <textarea placeholder="返信を書く..."></textarea>
            <button onclick="submitReply(this)">返信する</button>
          </div>
          <div class="replies"></div>
        </div>
      `;

      const activeTab = document.querySelector(".tab-button.active").textContent.trim();
      const tabId = activeTab.includes("雑談") ? "general" : activeTab.includes("レイド") ? "raid" : "trade";
      document.getElementById(tabId).prepend(post);
      textarea.value = "";
    }

    function switchTab(tabId) {
      document.querySelectorAll(".tab-content").forEach(tab => tab.style.display = "none");
      document.querySelectorAll(".tab-button").forEach(btn => btn.classList.remove("active"));
      document.getElementById(tabId).style.display = "block";
      event.target.classList.add("active");
    }

    function likePost(button) {
      const span = button.querySelector("span");
      span.textContent = parseInt(span.textContent) + 1;
    }

    function toggleReply(button) {
      const replyArea = button.parentElement.nextElementSibling;
      replyArea.style.display = replyArea.style.display === "none" ? "block" : "none";
    }

    function submitReply(button) {
      const textarea = button.previousElementSibling;
      const replyText = textarea.value.trim();
      if (!replyText) return;

      const replyDiv = document.createElement("div");
      replyDiv.className = "reply";
      replyDiv.innerHTML = `<small>あなた: ${replyText}</small>`;
      button.closest(".post-content").querySelector(".replies").appendChild(replyDiv);
      textarea.value = "";
    }

    function deletePost(button) {
      if (confirm("この投稿を削除しますか?")) {
        button.closest(".post").remove();
      }
    }
  </script>
</body>
</html>

にじいろモール(beta) ECサイト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>にじいろモール | オンラインショッピング</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="にじいろモールは、あらゆるジャンルの商品を取り揃えた総合ECサイトです。">
  <link rel="icon" href="https://cdn-icons-png.flaticon.com/512/1170/1170576.png" type="image/png">

  <!-- Google Fonts -->
  <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap" rel="stylesheet">

  <style>
    body {
      margin: 0;
      font-family: 'Noto Sans JP', sans-serif;
      background: #f3f3f3;
    }

    header {
      background-color: #5a4fcf;
      color: white;
      padding: 15px 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      flex-wrap: wrap;
    }

    header h1 {
      font-size: 1.8em;
      margin: 0;
      letter-spacing: 2px;
    }

    .search-bar {
      flex: 1;
      margin: 10px;
      max-width: 500px;
    }

    .search-bar input {
      width: 100%;
      padding: 10px;
      font-size: 1em;
      border-radius: 4px;
      border: none;
    }

    main {
      padding: 20px;
    }

    .product-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
      gap: 20px;
    }

    .product-card {
      background: white;
      border-radius: 6px;
      padding: 15px;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
      transition: transform 0.2s;
    }

    .product-card:hover {
      transform: translateY(-3px);
    }

    .product-card img {
      width: 100%;
      height: auto;
      border-radius: 5px;
    }

    .product-card h3 {
      margin: 10px 0 5px;
      font-size: 1.2em;
    }

    .product-card p {
      margin: 5px 0;
      color: #555;
    }

    .price {
      color: #d83535;
      font-weight: bold;
      font-size: 1.1em;
    }

    .product-card button {
      width: 100%;
      padding: 10px;
      margin-top: 10px;
      background: #ffce3d;
      border: none;
      border-radius: 4px;
      font-weight: bold;
      cursor: pointer;
      transition: background 0.2s;
    }

    .product-card button:hover {
      background: #f2b200;
    }

    footer {
      background: #5a4fcf;
      color: white;
      text-align: center;
      padding: 20px;
      margin-top: 40px;
    }

    @media (max-width: 600px) {
      .search-bar {
        order: 3;
        width: 100%;
      }
      header {
        flex-direction: column;
        align-items: flex-start;
      }
    }
  </style>
</head>
<body>

  <header>
    <h1>🌈 にじいろモール</h1>
    <div class="search-bar">
      <input type="text" placeholder="商品を検索...">
    </div>
  </header>

  <main>
    <div class="product-grid">
      <div class="product-card">
        <a href="product1.html">
          <img src="https://via.placeholder.com/240x160" alt="スマートウォッチ">
          <h3>スマートウォッチ</h3>
        </a>
        <p class="price">¥12,800</p>
        <p>心拍計測 / 防水 / 通知連携</p>
        <button>カートに追加</button>
      </div>

      <div class="product-card">
        <a href="product2.html">
          <img src="https://via.placeholder.com/240x160" alt="話題の書籍">
          <h3>話題の書籍</h3>
        </a>
        <p class="price">¥1,540</p>
        <p>ベストセラー本</p>
        <button>カートに追加</button>
      </div>

      <div class="product-card">
        <a href="product3.html">
          <img src="https://via.placeholder.com/240x160" alt="Bluetoothイヤホン">
          <h3>Bluetoothイヤホン</h3>
        </a>
        <p class="price">¥5,990</p>
        <p>高音質 / ノイズキャンセリング</p>
        <button>カートに追加</button>
      </div>
    </div>
  </main>

  <footer>
    &copy; 2025 にじいろモール - すべての権利を保有します。
  </footer>

</body>
</html>

キャラ別ランダムセリフメーカー

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>キャラ別ランダムセリフメーカー</title>
  <style>
    body {
      font-family: 'Segoe UI', sans-serif;
      background: linear-gradient(to right, #eef2f3, #8e9eab);
      text-align: center;
      padding: 50px;
    }
    h1 {
      font-size: 30px;
      color: #2c3e50;
    }
    select, button {
      padding: 10px;
      font-size: 16px;
      margin: 10px;
      border-radius: 8px;
    }
    button {
      background-color: #2980b9;
      color: white;
      border: none;
      cursor: pointer;
    }
    button:hover {
      background-color: #1f6391;
    }
    .quote-box {
      background: #fff;
      margin-top: 30px;
      padding: 30px;
      border-radius: 12px;
      max-width: 800px;
      margin-left: auto;
      margin-right: auto;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
      font-size: 20px;
      color: #333;
    }
  </style>
</head>
<body>
  <h1>🎙️ キャラ別ランダムセリフメーカー</h1>
  <p>ジャンルとキャラクターを選んで、名セリフを生成しよう!</p>

  <select id="genre">
    <option value="battle">バトル</option>
    <option value="romance">恋愛</option>
    <option value="drama">感動</option>
    <option value="comedy">ギャグ</option>
  </select>

  <select id="character">
    <option value="主人公">主人公</option>
    <option value="ヒロイン">ヒロイン</option>
    <option value="ライバル">ライバル</option>
    <option value="師匠">師匠</option>
  </select>

  <br>
  <button onclick="generateLine()">セリフを生成</button>

  <div class="quote-box" id="quote">
    ここにセリフが表示されます。
  </div>

  <script>
    const lines = {
      battle: {
        主人公: [
          "俺が倒さなきゃ、誰がやる!",
          "まだ…終わっちゃいない!",
          "立てるさ、何度でも!"
        ],
        ヒロイン: [
          "私だって…守れるんだから!",
          "あなたを信じる、それが私の戦いよ。"
        ],
        ライバル: [
          "俺を超えてみろ…できるならな!",
          "この一撃で、全てを終わらせる。"
        ],
        師匠: [
          "強さとは、心にあるものだ。",
          "お前にすべてを託す!"
        ]
      },
      romance: {
        主人公: [
          "君に出会うために、生まれてきた気がする。",
          "一緒に笑えるだけで、幸せなんだ。"
        ],
        ヒロイン: [
          "好きって、こんなにも苦しいの?",
          "…バカ。でも、ありがとう。"
        ],
        ライバル: [
          "…なぜあいつなんだ?俺じゃ、だめなのか。",
          "奪ってでも、お前を手に入れたい。"
        ],
        師匠: [
          "愛とは、時に強さよりも難しい。",
          "惚れた弱みってやつだな…"
        ]
      },
      drama: {
        主人公: [
          "俺たちは、ただ幸せになりたかっただけなんだ…。",
          "運命なんかに、負けてたまるか!"
        ],
        ヒロイン: [
          "もう一度…あなたに会いたい。",
          "願いが一つだけ叶うなら、時間を戻したい。"
        ],
        ライバル: [
          "俺の存在に意味なんてない…と思ってた。",
          "あの時の俺を、殴り飛ばしてやりたいよ。"
        ],
        師匠: [
          "選んだ道を信じろ。お前なら、やれる。",
          "迷っていい。人間なんだからな。"
        ]
      },
      comedy: {
        主人公: [
          "いや、なんでパンツが空飛んでるんだ!?",
          "オレの人生、どこで間違えた?"
        ],
        ヒロイン: [
          "あーもう!恥ずかしくて死ぬ!!",
          "だから言ったでしょ!?ネコじゃないってば!"
        ],
        ライバル: [
          "笑うな!こっちは本気なんだぞ!?",
          "俺がボケ担当じゃないって言ってるだろ!"
        ],
        師匠: [
          "ふぉっふぉっふ、若いのぅ…わしも昔はな…。",
          "今日の修行は…温泉じゃ!"
        ]
      }
    };

    function generateLine() {
      const genre = document.getElementById('genre').value;
      const character = document.getElementById('character').value;
      const options = lines[genre][character];
      const line = options[Math.floor(Math.random() * options.length)];
      document.getElementById("quote").innerText = `${character}「${line}」`;
    }
  </script>
</body>
</html>

GSAP Todoリスト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>GSAP Todoリスト</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      background: #f0f8ff;
      padding: 20px;
    }
    #todo-container {
      max-width: 500px;
      margin: auto;
    }
    input[type="text"] {
      width: 70%;
      padding: 10px;
      font-size: 16px;
    }
    button {
      padding: 10px;
      font-size: 16px;
      margin-left: 5px;
      cursor: pointer;
    }
    ul {
      list-style: none;
      padding: 0;
      margin-top: 20px;
    }
    li {
      background: #ffffff;
      margin-bottom: 10px;
      padding: 10px;
      border-radius: 8px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    }
  </style>
</head>
<body>

  <div id="todo-container">
    <h2>GSAP Todoリスト</h2>
    <input type="text" id="task-input" placeholder="タスクを入力...">
    <button onclick="addTask()">追加</button>

    <ul id="task-list"></ul>
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
  <script>
    function addTask() {
      const input = document.getElementById("task-input");
      const task = input.value.trim();
      if (task === "") return;

      const li = document.createElement("li");
      li.innerHTML = `
        <span>${task}</span>
        <button onclick="removeTask(this)">削除</button>
      `;

      document.getElementById("task-list").appendChild(li);

      // GSAP アニメーション(フェードイン)
      gsap.from(li, {opacity: 0, y: -20, duration: 0.5});

      input.value = "";
    }

    function removeTask(button) {
      const li = button.parentElement;
      gsap.to(li, {
        opacity: 0,
        y: 20,
        duration: 0.4,
        onComplete: () => li.remove()
      });
    }
  </script>

</body>
</html>

MyCSSVariables

index.html

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>CSS変数</title>
    <link rel="stylesheet" href="css/styles.css">
</head>

<body>
    <h1>タイトル</h1>
    <p>こんにちは。こんにちは。こんにちは。こんにちは。こんにちは。</p>
    <div class="btn">OK</div>
</body>

</html>

C:\MyCSSVariables\css

style.css

:root {
    /* --my-hue: 100; */
    /* --my-hue: 200; */
    --my-hue: 50;
}

body {
    background: hsl(var(--my-hue), 40%, 95%);
}

h1,
p {
    color: hsl(var(--my-hue), 35%, 55%);
}

.btn {
    color: #fff;
    width: 100px;
    padding: 8px;
    border-radius: 4px;
    text-align: center;
    /* background: hsl(var(--my-hue), 50%, 50%); */
    /* background: hsl(calc(var(--my-hue) + 180), 50%, 50%); */
    background: hsl(calc(var(--my-hue) + 60), 50%, 50%); }

Googleの人気記事を拾ってくるサイト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <title>Googleニュース 人気記事アグリゲーター</title>
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
  <style>
    body { background: #f8f9fa; padding-top: 20px; font-family: 'Arial', sans-serif; transition: background .3s, color .3s; }
    .dark-mode { background: #2c2c2c; color: #f1f1f1; }
    #controls { margin-bottom: 20px; }
    .card { border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); margin-bottom: 15px; transition: transform .2s, background .3s, color .3s; }
    .dark-mode .card { background: #3a3a3a; color: #f1f1f1; }
    .card:hover { transform: translateY(-3px); }
    .card-title { font-size: 1.25rem; margin-bottom: .5rem; }
    .meta { font-size: 0.85rem; color: #6c757d; margin-bottom: .5rem; }
    .dark-mode .meta { color: #ccc; }
    .thumbnail { width: 100%; height: auto; border-top-left-radius: 10px; border-top-right-radius: 10px; }
    #loading { display: none; font-size: 2rem; text-align: center; margin-top: 40px; }
    #error { display: none; color: red; text-align: center; margin-top: 20px; }
    @media (min-width: 768px) {
      #articles .col-md-6 { flex: 0 0 50%; max-width: 50%; }
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="d-flex justify-content-between align-items-center mb-3">
      <h1>Googleニュース 人気記事</h1>
      <button id="darkModeToggle" class="btn btn-outline-secondary">ダークモード</button>
    </div>
    <div id="controls" class="d-flex flex-wrap justify-content-between align-items-end">
      <div class="form-group mb-2 mr-2">
        <label for="categorySelect">カテゴリ:</label>
        <select id="categorySelect" class="form-control">
          <option value="WORLD">世界</option>
          <option value="BUSINESS">ビジネス</option>
          <option value="TECHNOLOGY">テクノロジー</option>
          <option value="ENTERTAINMENT">エンタメ</option>
          <option value="SPORTS">スポーツ</option>
          <option value="SCIENCE">科学</option>
          <option value="HEALTH">健康</option>
        </select>
      </div>
      <div class="form-group mb-2 mr-2 flex-grow-1">
        <label for="searchInput">キーワード:</label>
        <input id="searchInput" type="text" class="form-control" placeholder="タイトルで絞り込み">
      </div>
      <div class="form-group mb-2 mr-2">
        <label for="maxItems">表示数:</label>
        <input id="maxItems" type="number" class="form-control" value="20" min="1" max="100">
      </div>
      <button id="refreshBtn" class="btn btn-primary mb-2">更新</button>
    </div>
    <div id="loading"><i class="fas fa-spinner fa-spin"></i> 読み込み中...</div>
    <div id="error"></div>
    <div id="articles" class="row"></div>
  </div>

  <!-- FontAwesome & jQuery & Bootstrap JS -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
  <script>
    const controls = {
      category: document.getElementById('categorySelect'),
      search: document.getElementById('searchInput'),
      maxItems: document.getElementById('maxItems'),
      refresh: document.getElementById('refreshBtn'),
      darkToggle: document.getElementById('darkModeToggle')
    };
    const articlesContainer = document.getElementById('articles');
    const loading = document.getElementById('loading');
    const errorMsg = document.getElementById('error');
    const body = document.body;

    function getFeedUrl(topic) {
      return `https://news.google.com/rss/headlines/section/topic/${topic}?hl=ja&gl=JP&ceid=JP:ja`;
    }
    async function fetchArticles() {
      articlesContainer.innerHTML = '';
      errorMsg.style.display = 'none';
      loading.style.display = 'block';
      const topic = controls.category.value;
      const apiUrl = `https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(getFeedUrl(topic))}`;
      try {
        const res = await fetch(apiUrl);
        const data = await res.json();
        if (data.status !== 'ok') throw new Error('取得失敗');
        let items = data.items.map(item => ({
          title: item.title,
          link: item.link,
          date: new Date(item.pubDate),
          thumbnail: item.thumbnail || ''
        }));
        const kw = controls.search.value.trim();
        if (kw) items = items.filter(i => i.title.includes(kw));
        items = items.slice(0, parseInt(controls.maxItems.value) || items.length);
        render(items);
      } catch (e) {
        console.error(e);
        errorMsg.textContent = '記事の取得に失敗しました。';
        errorMsg.style.display = 'block';
      } finally {
        loading.style.display = 'none';
      }
    }
    function render(items) {
      if (!items.length) {
        articlesContainer.innerHTML = '<p class="text-center w-100">該当する記事がありません。</p>';
        return;
      }
      items.forEach(i => {
        const col = document.createElement('div'); col.className = 'col-12 col-md-6';
        const thumb = i.thumbnail ? `<img src="${i.thumbnail}" class="thumbnail" alt="サムネイル">` : '';
        col.innerHTML = `
          <div class="card">
            ${thumb}
            <div class="card-body">
              <h2 class="card-title"><a href="${i.link}" target="_blank">${i.title}</a></h2>
              <p class="meta">公開: ${i.date.toLocaleString()}</p>
            </div>
          </div>`;
        articlesContainer.appendChild(col);
      });
    }
    controls.refresh.addEventListener('click', fetchArticles);
    controls.darkToggle.addEventListener('click', () => {
      body.classList.toggle('dark-mode');
    });
    document.addEventListener('DOMContentLoaded', fetchArticles);
  </script>
</body>
</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>

TRPGシナリオ自動生成&共有

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>TRPGシナリオ自動生成&共有</title>
  <style>
    body {
      font-family: 'Segoe UI', sans-serif;
      background: #f4f4f4;
      padding: 20px;
      max-width: 800px;
      margin: auto;
    }
    h1, h2 {
      text-align: center;
    }
    select, button, textarea {
      width: 100%;
      padding: 10px;
      margin-top: 10px;
      font-size: 1rem;
    }
    .box {
      background: #fff;
      padding: 15px;
      border-radius: 8px;
      margin-top: 20px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    .scenario {
      background: #f9f9f9;
      border-left: 5px solid #4CAF50;
      margin-bottom: 10px;
      padding: 10px;
      border-radius: 5px;
      position: relative;
    }
    .btns {
      margin-top: 10px;
      display: flex;
      gap: 10px;
    }
    .copy, .save, .delete {
      cursor: pointer;
      border: none;
      padding: 8px 12px;
      border-radius: 4px;
    }
    .copy { background: #2196F3; color: white; }
    .save { background: #4CAF50; color: white; }
    .delete { background: #f44336; color: white; }
  </style>
</head>
<body>
  <h1>🎲 TRPGシナリオ自動生成サイト</h1>

  <div class="box">
    <label>ジャンル選択:</label>
    <select id="genre">
      <option value="fantasy">ファンタジー</option>
      <option value="horror">ホラー</option>
      <option value="scifi">SF</option>
    </select>
    <button onclick="generateScenario()">📜 シナリオを生成する</button>
  </div>

  <div id="scenarioBox" class="box"></div>

  <h2>🗃 保存済みシナリオ</h2>
  <div id="savedScenarios" class="box"></div>

  <script>
    const data = {
      fantasy: {
        title: ["封印の迷宮", "聖騎士の試練", "魔王復活の予兆", "失われた王国"],
        world: ["中世の王国", "古代の神殿", "天空都市", "エルフの森"],
        incident: ["魔法の暴走", "伝説の剣の発見", "精霊の異変", "竜の襲撃"],
        npc: ["若き魔導士", "忠義の騎士", "隠者の賢者", "裏切り者の巫女"],
        goal: ["真実を解明せよ", "封印を強化せよ", "儀式を阻止せよ", "遺跡の謎を解け"]
      },
      horror: {
        title: ["呪われた村", "深夜の廃病院", "封印されたビデオ", "血塗られた儀式"],
        world: ["雨の山奥", "閉鎖された病院", "地下の儀式場", "忘れ去られた洋館"],
        incident: ["突然の失踪事件", "見えない何かの影", "不気味な手紙", "鏡の中の誰か"],
        npc: ["精神を病んだ神父", "警察官の幽霊", "語らぬ老婆", "首のない少女"],
        goal: ["脱出せよ", "謎を暴け", "呪いを解け", "儀式を止めろ"]
      },
      scifi: {
        title: ["星間戦争の火種", "機械反乱の夜", "消えた宇宙船", "記憶を失った地球"],
        world: ["宇宙コロニー", "月面都市", "異星の遺跡", "サイバーパンク都市"],
        incident: ["AIの暴走", "重力異常", "通信断絶", "テレポート事故"],
        npc: ["サイボーグ兵士", "記憶を失った博士", "企業スパイ", "異星の子供"],
        goal: ["原因を究明せよ", "AIを停止せよ", "人類を救え", "真実を暴け"]
      }
    };

    function generateScenario() {
      const genre = document.getElementById("genre").value;
      const g = data[genre];

      const title = rand(g.title);
      const world = rand(g.world);
      const incident = rand(g.incident);
      const npc = rand(g.npc);
      const goal = rand(g.goal);

      const scenarioText = `【タイトル】${title}\n【舞台】${world}\n【事件】${incident}\n【NPC】${npc}\n【目的】${goal}`;
      const html = `
        <div class="scenario">
          <pre>${scenarioText}</pre>
          <div class="btns">
            <button class="copy" onclick="copyText(\`${scenarioText.replace(/`/g, '\\`')}\`)">📋 コピー</button>
            <button class="save" onclick="saveScenario(\`${scenarioText.replace(/`/g, '\\`')}\`)">💾 保存</button>
          </div>
        </div>
      `;
      document.getElementById("scenarioBox").innerHTML = html;
    }

    function rand(arr) {
      return arr[Math.floor(Math.random() * arr.length)];
    }

    function copyText(text) {
      navigator.clipboard.writeText(text).then(() => {
        alert("コピーしました!");
      });
    }

    function saveScenario(text) {
      let saved = JSON.parse(localStorage.getItem("trpgScenarios") || "[]");
      saved.push(text);
      localStorage.setItem("trpgScenarios", JSON.stringify(saved));
      alert("保存しました!");
      showSaved();
    }

    function deleteScenario(index) {
      let saved = JSON.parse(localStorage.getItem("trpgScenarios") || "[]");
      saved.splice(index, 1);
      localStorage.setItem("trpgScenarios", JSON.stringify(saved));
      showSaved();
    }

    function showSaved() {
      const box = document.getElementById("savedScenarios");
      const saved = JSON.parse(localStorage.getItem("trpgScenarios") || "[]");
      if (saved.length === 0) {
        box.innerHTML = "<p>保存されているシナリオはありません。</p>";
        return;
      }
      box.innerHTML = saved.map((s, i) => `
        <div class="scenario">
          <pre>${s}</pre>
          <div class="btns">
            <button class="copy" onclick="copyText(\`${s.replace(/`/g, '\\`')}\`)">📋 コピー</button>
            <button class="delete" onclick="deleteScenario(${i})">🗑 削除</button>
          </div>
        </div>
      `).join("");
    }

    // 初回ロード
    showSaved();
  </script>
</body>
</html>