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>

GSAP入門 Tween編

index.html

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My GSAP</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <header>
        <h1>MySite</h1>
        <nav>
            <ul>
                <li>Menu</li>
                <li>Menu</li>
                <li>Menu</li>
            </ul>
        </nav>
    </header>

    <script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
    <script src="main.js"></script>
</body>

</html>

main.js

'use strict';

{
    gsap.from('h1', {
        y: -32,
        opacity: 0,
    });

    gsap.from('li', {
        y: 32,
        opacity: 0,
        stagger: 0.3,
    });
}

style.css

@charset "utf-8";

header {
    padding: 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

h1 {
    margin: 0;
}

ul {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    gap: 32px;
}

人間の記憶を記録するサイト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Memory Recorder Pro</title>
  <!-- Bootstrap CSS & Icons -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
  <!-- Chart.js for statistics -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
  <style>
    :root{
      --bg-main:#ffffff;
      --bg-gradient:linear-gradient(135deg,#f8f9fa 0%,#e9ecef 100%);
      --text-main:#212529;
      --accent:#0d6efd;
    }
    :root.dark{
      --bg-main:#1e1e1e;
      --bg-gradient:linear-gradient(135deg,#2b2b2b 0%,#1e1e1e 100%);
      --text-main:#f8f9fa;
    }
    body{
      background:var(--bg-gradient);
      color:var(--text-main);
      min-height:100vh;
      display:flex;
      align-items:center;
      justify-content:center;
      font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;
      transition:background .3s ease,color .3s ease;
    }
    .memory-app{
      width:100%;
      max-width:920px;
      background:var(--bg-main);
      padding:2rem 2.5rem;
      border-radius:1.5rem;
      box-shadow:0 4px 20px rgba(0,0,0,.1);
      transition:background .3s ease;
    }
    .memory-card{
      border-left:4px solid var(--accent);
      padding-left:1rem;
      margin-bottom:1rem;
    }
    .tag-badge{
      background:var(--accent);
      color:#fff;
      margin-right:.25rem;
      cursor:pointer;
    }
    .btn-accent{
      background:var(--accent);
      border-color:var(--accent);
      color:#fff;
    }
  </style>
</head>
<body>
  <div class="memory-app">
    <!-- Header -->
    <div class="d-flex justify-content-between align-items-center mb-4">
      <h1 class="m-0">📝 Memory Recorder <small class="h6 fw-light">Pro</small></h1>
      <div class="d-flex align-items-center gap-3">
        <button id="statsBtn" class="btn btn-outline-secondary btn-sm"><i class="bi bi-bar-chart"></i></button>
        <div class="form-check form-switch m-0">
          <input class="form-check-input" type="checkbox" id="darkModeSwitch">
        </div>
      </div>
    </div>

    <!-- Search & Stats row -->
    <div class="row g-3 align-items-end mb-4">
      <div class="col-md-8">
        <label for="searchInput" class="form-label">検索(テキスト / タグ)</label>
        <input type="text" id="searchInput" class="form-control" placeholder="キーワードで検索...">
      </div>
      <div class="col-md-4 text-md-end">
        <p id="stats" class="mb-0 small text-muted"></p>
      </div>
    </div>

    <!-- Input Area -->
    <div class="mb-3">
      <label for="memoryText" class="form-label">新しい記憶</label>
      <textarea class="form-control" id="memoryText" rows="3" placeholder="出来事・思い出など..."></textarea>
    </div>
    <div class="mb-4 row g-2 align-items-end">
      <div class="col-md-8">
        <label for="memoryTags" class="form-label">タグ(カンマ区切り)</label>
        <input type="text" id="memoryTags" class="form-control" placeholder="例: 仕事, 家族, 趣味">
      </div>
      <div class="col-md-4 d-flex gap-2 mt-md-4">
        <button id="saveBtn" class="btn btn-primary flex-grow-1"><i class="bi bi-save"></i> 保存</button>
        <button id="voiceBtn" class="btn btn-outline-secondary" title="音声入力"><i class="bi bi-mic"></i></button>
      </div>
    </div>

    <!-- File / Clear row -->
    <div class="d-flex flex-wrap gap-2 mb-4">
      <button id="exportBtn" class="btn btn-outline-secondary"><i class="bi bi-download"></i> エクスポート</button>
      <button id="importBtn" class="btn btn-outline-secondary"><i class="bi bi-upload"></i> インポート</button>
      <button id="clearAllBtn" class="btn btn-outline-danger ms-auto"><i class="bi bi-trash"></i> 全削除</button>
      <input type="file" id="importFile" accept="application/json" class="d-none">
    </div>

    <hr>
    <h2 class="h5 mb-3">📚 保存された記憶</h2>
    <div id="memoryList"></div>
  </div>

  <!-- Statistics Modal -->
  <div class="modal fade" id="statsModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog modal-lg modal-dialog-centered">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title"><i class="bi bi-graph-up"></i> 記憶統計</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          <canvas id="statsChart" height="120"></canvas>
        </div>
      </div>
    </div>
  </div>

  <!-- Edit Modal -->
  <div class="modal fade" id="editModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title">記憶を編集</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          <div class="mb-3">
            <label for="editText" class="form-label">内容</label>
            <textarea id="editText" class="form-control" rows="4"></textarea>
          </div>
          <div class="mb-3">
            <label for="editTags" class="form-label">タグ(カンマ区切り)</label>
            <input id="editTags" class="form-control">
          </div>
        </div>
        <div class="modal-footer">
          <button class="btn btn-secondary" data-bs-dismiss="modal">キャンセル</button>
          <button id="editSaveBtn" class="btn btn-primary">保存</button>
        </div>
      </div>
    </div>
  </div>

  <!-- Bootstrap JS -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
  <script>
    // --------- Constants ---------
    const STORAGE_KEY = "memories";
    const THEME_KEY   = "prefers-dark";

    // --------- Helpers ---------
    const $ = sel => document.querySelector(sel);
    const modal = id => new bootstrap.Modal($(id));

    const memories = {
      list() { return JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]"); },
      save(arr) { localStorage.setItem(STORAGE_KEY, JSON.stringify(arr)); },
      add(obj){ const arr = this.list(); arr.push(obj); this.save(arr);} ,
      remove(id){ this.save(this.list().filter(m=>m.id!==id));},
      update(id,data){ const arr=this.list().map(m=>m.id===id?{...m,...data}:m); this.save(arr);} ,
    };

    const fmtDate = d => new Intl.DateTimeFormat("ja-JP",{dateStyle:"medium",timeStyle:"short"}).format(d);

    // --------- Rendering ---------
    function renderMemories(filter=""){
      const listEl = $("#memoryList");
      listEl.innerHTML="";
      const all = memories.list();
      const lower = filter.toLowerCase();
      const visible = all.filter(m=>{
        const tagMatch = m.tags.some(t=>t.toLowerCase().includes(lower));
        const textMatch= m.text.toLowerCase().includes(lower);
        return !lower || tagMatch || textMatch;
      }).reverse();

      // stats
      $("#stats").textContent=`合計: ${all.length} 件`;

      if(!visible.length){listEl.innerHTML='<p class="text-muted">該当する記憶がありません。</p>';return;}

      visible.forEach(m=>{
        const card=document.createElement("div");
        card.className="memory-card card";
        card.innerHTML=`<div class="card-body p-3">
          <div class="d-flex justify-content-between align-items-start flex-wrap">
            <h5 class="card-title mb-1">${fmtDate(new Date(m.date))}</h5>
            <div class="btn-group btn-group-sm">
              <button class="btn btn-link text-primary" title="編集" onclick="openEditor('${m.id}')"><i class="bi bi-pencil"></i></button>
              <button class="btn btn-link text-danger" title="削除" onclick="deleteMemory('${m.id}')"><i class="bi bi-trash"></i></button>
            </div>
          </div>
          <p class="card-text" style="white-space:pre-wrap;">${m.text}</p>
          <div class="mt-2">${m.tags.map(t=>`<span class=\"badge tag-badge\" onclick=\"filterTag('${t}')\">${t}</span>`).join(" ")}</div>
        </div>`;
        listEl.appendChild(card);
      });
    }

    // --------- CRUD ---------
    function saveMemory(){
      const text=$("#memoryText").value.trim();
      const tagRaw=$("#memoryTags").value.trim();
      if(!text)return;
      const tags=tagRaw?tagRaw.split(/\s*,\s*/).filter(Boolean):[];
      memories.add({id:crypto.randomUUID(),text,tags,date:new Date().toISOString()});
      $("#memoryText").value="";
      $("#memoryTags").value="";
      renderMemories($("#searchInput").value);
    }
    function deleteMemory(id){
      if(!confirm("削除しますか?"))return;
      memories.remove(id);
      renderMemories($("#searchInput").value);
    }

    // --------- Edit ---------
    let editingId=null;
    function openEditor(id){
      editingId=id;
      const m=memories.list().find(x=>x.id===id);
      $("#editText").value=m.text;
      $("#editTags").value=m.tags.join(", ");
      modal('#editModal').show();
    }
    $("#editSaveBtn").addEventListener("click",()=>{
      const text=$("#editText").value.trim();
      const tags=$("#editTags").value.trim().split(/\s*,\s*/).filter(Boolean);
      memories.update(editingId,{text,tags});
      modal('#editModal').hide();
      renderMemories($("#searchInput").value);
    });

    // --------- Filter helper ---------
    function filterTag(tag){
      $("#searchInput").value=tag;
      renderMemories(tag);
    }

    // --------- Export / Import ---------
    function exportMemories(){
      const blob=new Blob([JSON.stringify(memories.list(),null,2)],{type:"application/json"});
      const url=URL.createObjectURL(blob);
      const a=document.createElement("a");
      a.href=url;a.download="memories.json";a.click();URL.revokeObjectURL(url);
    }
    function importMemories(file){
      const reader=new FileReader();
      reader.onload=e=>{try{const arr=JSON.parse(e.target.result);if(Array.isArray(arr)){memories.save([...memories.list(),...arr]);renderMemories($("#searchInput").value);}else throw 0;}catch{alert("無効なファイルです");}};
      reader.readAsText(file);
    }

    // --------- Statistics ---------
    let chartInstance=null;
    function openStats(){
      const data=memories.list();
      const counts={};
      data.forEach(m=>{
        const key=m.date.slice(0,7); // YYYY-MM
        counts[key]=(counts[key]||0)+1;
      });
      const labels=Object.keys(counts).sort();
      const values=labels.map(l=>counts[l]);
      const ctx=$("#statsChart");
      if(chartInstance)chartInstance.destroy();
      chartInstance=new Chart(ctx,{type:'bar',data:{labels,datasets:[{label:'記憶数',data:values}]},options:{plugins:{legend:{display:false}}}});
      modal('#statsModal').show();
    }

    // --------- Dark Mode ---------
    function applyTheme(dark){
      document.documentElement.classList.toggle('dark',dark);
      localStorage.setItem(THEME_KEY,dark?'1':'0');
      $("#darkModeSwitch").checked=dark;
    }

    // --------- Voice Input (Experimental) ---------
    let rec=null;
    async function startVoice(){
      if(!('webkitSpeechRecognition'in window||'SpeechRecognition'in window)){alert('音声認識非対応');return;}
      const Speech=window.SpeechRecognition||window.webkitSpeechRecognition;
      rec=new Speech();
      rec.lang='ja-JP';
      rec.continuous=false;
      rec.interimResults=false;
      rec.onresult=e=>{$('#memoryText').value=e.results[0][0].transcript;};
      rec.start();
    }

    // --------- Init ---------
    document.addEventListener('DOMContentLoaded',()=>{
      // Theme
      applyTheme(localStorage.getItem(THEME_KEY)==='1');

      // Render memories
      renderMemories();

      // Listeners
      $('#saveBtn').addEventListener('click',saveMemory);
      $('#clearAllBtn').addEventListener('click',()=>{if(confirm('すべて削除しますか?')){localStorage.removeItem(STORAGE_KEY);renderMemories();}});
      $('#exportBtn').addEventListener('click',exportMemories);
      $('#importBtn').addEventListener('click',()=>$('#importFile').click());
      $('#importFile').addEventListener('change',e=>{if(e.target.files[0])importMemories(e.target.files[0]);e.target.value='';});
      $('#darkModeSwitch').addEventListener('change',e=>applyTheme(e.target.checked));
      $('#searchInput').addEventListener('input',e=>renderMemories(e.target.value));
      $('#memoryText').addEventListener('keydown',e=>{if(e.key==='Enter'&&(e.ctrlKey||e.metaKey))saveMemory();});
      $('#statsBtn').addEventListener('click',openStats);
      $('#voiceBtn').addEventListener('click',startVoice);
    });
  </script>
</body>
</html>

Eternal.html 画像投稿サイト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Eternal</title>
  <style>
    body {
      font-family: sans-serif;
      background: #f5f5f5;
      margin: 0;
      padding: 0;
    }

    header {
      background: #e60023;
      color: white;
      padding: 1em;
      text-align: center;
      font-size: 1.8em;
    }

    .controls {
      padding: 1em;
      background: #fff;
      text-align: center;
    }

    .controls input, .controls button {
      margin: 0.5em;
      padding: 0.5em;
      font-size: 1em;
    }

    .grid {
      column-count: 4;
      column-gap: 1em;
      padding: 1em;
    }

    .pin {
      background: white;
      display: inline-block;
      margin-bottom: 1em;
      width: 100%;
      box-sizing: border-box;
      border-radius: 10px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
      overflow: hidden;
      position: relative;
    }

    .pin img {
      width: 100%;
      cursor: pointer;
    }

    .description, .tags {
      padding: 0.5em;
    }

    .favorite {
      position: absolute;
      top: 8px;
      right: 8px;
      font-size: 22px;
      color: gray;
      cursor: pointer;
      user-select: none;
    }

    .favorite.active {
      color: gold;
    }

    .likes {
      font-size: 0.9em;
      text-align: right;
      padding: 0 0.5em 0.5em 0;
      color: #888;
    }

    .tag {
      display: inline-block;
      background: #eee;
      padding: 0.2em 0.5em;
      border-radius: 5px;
      margin: 0.2em;
      cursor: pointer;
    }

    .tag:hover {
      background: #ccc;
    }

    @media screen and (max-width: 1024px) {
      .grid {
        column-count: 3;
      }
    }

    @media screen and (max-width: 768px) {
      .grid {
        column-count: 2;
      }
    }

    @media screen and (max-width: 480px) {
      .grid {
        column-count: 1;
      }
    }

    #modal {
      position: fixed;
      top: 0; left: 0; right: 0; bottom: 0;
      background: rgba(0,0,0,0.7);
      display: none;
      justify-content: center;
      align-items: center;
      z-index: 1000;
    }

    #modal img {
      max-width: 90%;
      max-height: 80vh;
      border-radius: 10px;
    }
  </style>
</head>
<body>

<header>Pintorrest 完全版</header>

<div class="controls">
  <input type="file" id="imageInput" accept="image/*">
  <input type="text" id="descInput" placeholder="説明">
  <input type="text" id="tagInput" placeholder="タグ(カンマ区切り)">
  <button onclick="addPin()">投稿</button>
  <br>
  <input type="text" id="searchBox" placeholder="検索..." oninput="filterPins()">
</div>

<div class="grid" id="grid"></div>

<div id="modal" onclick="this.style.display='none'">
  <img id="modalImg" src="">
</div>

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

  function renderPins() {
    const grid = document.getElementById("grid");
    grid.innerHTML = '';
    pins.forEach((pin, index) => {
      const pinElem = document.createElement("div");
      pinElem.className = "pin";
      pinElem.innerHTML = `
        <span class="favorite ${pin.favorited ? 'active' : ''}" onclick="toggleFavorite(${index}, this)">★</span>
        <img src="${pin.image}" onclick="showModal('${pin.image}')">
        <div class="description">${pin.desc}</div>
        <div class="tags">${pin.tags.map(tag => `<span class="tag" onclick="filterByTag('${tag}')">${tag}</span>`).join(' ')}</div>
        <div class="likes">♥ ${pin.likes}</div>
      `;
      grid.appendChild(pinElem);
    });
  }

  function addPin() {
    const imageInput = document.getElementById("imageInput");
    const desc = document.getElementById("descInput").value.trim();
    const tags = document.getElementById("tagInput").value.split(',').map(t => t.trim()).filter(Boolean);
    const file = imageInput.files[0];
    if (!file || !desc) return alert("画像と説明を入力してください");

    const reader = new FileReader();
    reader.onload = function(e) {
      pins.unshift({
        image: e.target.result,
        desc: desc,
        tags: tags,
        likes: 0,
        favorited: false
      });
      savePins();
      renderPins();
      document.getElementById("descInput").value = '';
      document.getElementById("tagInput").value = '';
      document.getElementById("imageInput").value = '';
    };
    reader.readAsDataURL(file);
  }

  function showModal(src) {
    document.getElementById("modalImg").src = src;
    document.getElementById("modal").style.display = 'flex';
  }

  function toggleFavorite(index, elem) {
    pins[index].favorited = !pins[index].favorited;
    pins[index].likes += pins[index].favorited ? 1 : -1;
    savePins();
    renderPins();
  }

  function savePins() {
    localStorage.setItem("pins", JSON.stringify(pins));
  }

  function filterPins() {
    const keyword = document.getElementById("searchBox").value.toLowerCase();
    document.querySelectorAll(".pin").forEach(pin => {
      const text = pin.textContent.toLowerCase();
      pin.style.display = text.includes(keyword) ? "inline-block" : "none";
    });
  }

  function filterByTag(tag) {
    document.getElementById("searchBox").value = tag;
    filterPins();
  }

  renderPins();
</script>

</body>
</html>

RSSreader.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>超RSSニュースリーダー</title>
  <style>
    body {
      font-family: "Segoe UI", sans-serif;
      background: #f4f4f4;
      margin: 0;
      padding: 20px;
    }
    h1 {
      text-align: center;
      color: #222;
    }
    #search {
      display: block;
      margin: 10px auto;
      padding: 10px;
      width: 80%;
      font-size: 16px;
      border-radius: 8px;
      border: 1px solid #ccc;
    }
    #feeds {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 20px;
    }
    .feed-card {
      background: white;
      border-radius: 12px;
      box-shadow: 0 2px 5px rgba(0,0,0,0.1);
      width: 320px;
      padding: 12px;
      box-sizing: border-box;
    }
    .feed-card img {
      width: 100%;
      height: auto;
      border-radius: 8px;
      margin-bottom: 10px;
    }
    .feed-card h2 {
      font-size: 16px;
      margin: 0 0 5px;
    }
    .feed-card p {
      font-size: 14px;
      color: #555;
    }
    .feed-card time {
      display: block;
      font-size: 12px;
      color: #999;
      margin-top: 5px;
    }
  </style>
</head>
<body>
  <h1>超RSSニュースリーダー</h1>
  <input type="text" id="search" placeholder="記事を検索...">

  <div id="feeds"></div>

  <script>
    const rssUrls = [
      "https://news.yahoo.co.jp/rss/topics/top-picks.xml",
      "https://b.hatena.ne.jp/hotentry/it.rss",
      "https://www.nhk.or.jp/rss/news/cat0.xml",
      "http://tyosuke20xx.com/rss"
    ];
    const proxy = "https://api.allorigins.win/get?url=";
    const feedsContainer = document.getElementById("feeds");
    const searchInput = document.getElementById("search");

    let allCards = [];

    function formatDate(pubDateStr) {
      const date = new Date(pubDateStr);
      return isNaN(date) ? "" : `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}`;
    }

    function extractImageFromDescription(desc) {
      const match = desc.match(/<img.*?src="(.*?)"/);
      return match ? match[1] : null;
    }

    async function fetchAndDisplayFeeds() {
      feedsContainer.innerHTML = "🔄 更新中…";
      allCards = [];

      for (const url of rssUrls) {
        try {
          const res = await fetch(proxy + encodeURIComponent(url));
          const data = await res.json();
          const parser = new DOMParser();
          const xml = parser.parseFromString(data.contents, "text/xml");
          const items = xml.querySelectorAll("item");

          items.forEach((item, i) => {
            if (i >= 5) return;

            const title = item.querySelector("title")?.textContent || "";
            const link = item.querySelector("link")?.textContent || "#";
            const desc = item.querySelector("description")?.textContent || "";
            const pubDate = item.querySelector("pubDate")?.textContent || "";
            const dateFormatted = formatDate(pubDate);
            const img = extractImageFromDescription(desc);

            const card = document.createElement("div");
            card.className = "feed-card";
            card.innerHTML = `
              ${img ? `<img src="${img}" alt="thumbnail">` : ""}
              <h2><a href="${link}" target="_blank">${title}</a></h2>
              <p>${desc.replace(/<[^>]+>/g, '').slice(0, 100)}...</p>
              <time>${dateFormatted}</time>
            `;
            allCards.push({ title, desc, element: card });
            feedsContainer.appendChild(card);
          });
        } catch (e) {
          console.error("RSS取得失敗:", url, e);
        }
      }
    }

    searchInput.addEventListener("input", () => {
      const keyword = searchInput.value.toLowerCase();
      feedsContainer.innerHTML = "";
      allCards.forEach(({ title, desc, element }) => {
        if (title.toLowerCase().includes(keyword) || desc.toLowerCase().includes(keyword)) {
          feedsContainer.appendChild(element);
        }
      });
    });

    fetchAndDisplayFeeds();
    setInterval(fetchAndDisplayFeeds, 60000); // 60秒ごと自動更新
  </script>
</body>
</html>

Github風サイト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>GitHub風 - SampleRepo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Bootstrap & FontAwesome -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
  <style>
    body {
      background: #f6f8fa;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    }
    .repo-header { background: #fff; padding: 20px; border: 1px solid #ddd; margin-bottom: 10px; }
    .nav-tabs .nav-link.active { border-color: #ddd #ddd #fff; background: #fff; }
    .file-list li { border-bottom: 1px solid #eee; padding: 8px 0; display: flex; align-items: center; }
    .file-list i { margin-right: 10px; }
    .readme-box, .issues-box, .commits-box { background: #fff; padding: 20px; border: 1px solid #ddd; margin-top: 10px; }
    #editor { height: 400px; width: 100%; border: 1px solid #ccc; }
  </style>
</head>
<body>

  <!-- ヘッダー -->
  <nav class="navbar navbar-dark bg-dark px-3">
    <a class="navbar-brand" href="#"><i class="fab fa-github"></i> GitHub風サイト</a>
    <span class="text-white"><i class="fas fa-user-circle"></i> yuhei</span>
  </nav>

  <div class="container mt-3">
    <!-- リポジトリヘッダー -->
    <div class="repo-header">
      <h3><i class="fas fa-book"></i> yuhei / <strong>SampleRepo</strong></h3>
      <p class="text-muted">最終更新: 2025年5月26日</p>
      <button class="btn btn-sm btn-outline-secondary"><i class="fas fa-star"></i> Star</button>
      <button class="btn btn-sm btn-outline-secondary"><i class="fas fa-code-branch"></i> Fork</button>
    </div>

    <!-- タブ -->
    <ul class="nav nav-tabs" id="repoTabs">
      <li class="nav-item"><a class="nav-link active" href="#" onclick="switchTab('code')"><i class="fas fa-code"></i> Code</a></li>
      <li class="nav-item"><a class="nav-link" href="#" onclick="switchTab('issues')"><i class="fas fa-exclamation-circle"></i> Issues</a></li>
      <li class="nav-item"><a class="nav-link" href="#" onclick="switchTab('commits')"><i class="fas fa-history"></i> Commits</a></li>
    </ul>

    <!-- Codeタブ -->
    <div id="tab-code" class="tab-content">
      <ul class="file-list list-unstyled bg-white p-3 border">
        <li><i class="fas fa-folder"></i> src/</li>
        <li><i class="fas fa-file-code"></i> index.js</li>
        <li><i class="fas fa-file-alt"></i> README.md</li>
        <li><i class="fas fa-file-alt"></i> LICENSE</li>
      </ul>

      <div class="readme-box">
        <h4><i class="fas fa-book-open"></i> README.md</h4>
        <hr>
        <div id="readme-content"></div>
      </div>

      <div class="mt-4">
        <h5><i class="fas fa-code"></i> コード編集 (Monaco Editor)</h5>
        <div id="editor"></div>
        <button id="saveCode" class="btn btn-success mt-2"><i class="fas fa-save"></i> 保存</button>
        <button id="themeToggle" class="btn btn-secondary mt-2"><i class="fas fa-adjust"></i> テーマ切替</button>
      </div>
    </div>

    <!-- Issuesタブ -->
    <div id="tab-issues" class="tab-content" style="display:none;">
      <div class="issues-box">
        <h4><i class="fas fa-exclamation-circle"></i> Open Issues</h4>
        <ul class="list-group">
          <li class="list-group-item">
            <strong>#1</strong> READMEの翻訳が必要です<br>
            <small class="text-muted">posted by yuhei - 1日前</small>
          </li>
          <li class="list-group-item">
            <strong>#2</strong> index.jsにテストコードを追加してください<br>
            <small class="text-muted">posted by yuhei - 2日前</small>
          </li>
        </ul>
      </div>
    </div>

    <!-- Commitsタブ -->
    <div id="tab-commits" class="tab-content" style="display:none;">
      <div class="commits-box">
        <h4><i class="fas fa-history"></i> Commits</h4>
        <ul class="list-group">
          <li class="list-group-item">
            <strong>Initial commit</strong> - 2025-05-25<br>
            <small class="text-muted">by yuhei</small>
          </li>
          <li class="list-group-item">
            <strong>README updated</strong> - 2025-05-26<br>
            <small class="text-muted">by yuhei</small>
          </li>
        </ul>
      </div>
    </div>
  </div>

  <!-- ライブラリ -->
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.min.js"></script>

  <!-- タブ切替・Markdown表示・Monaco起動 -->
  <script>
    const markdown = `
# SampleRepo

ようこそ!これはGitHub風サイトのデモです。

## 特徴
- Monaco Editorでコード編集
- Markdown表示(README)
- ダークモード対応
- Issue・コミットのUI

## 技術
- HTML/CSS/JS
- Bootstrap5
- Monaco Editor
- Marked.js
    `;
    document.getElementById('readme-content').innerHTML = marked.parse(markdown);

    function switchTab(tab) {
      ['code', 'issues', 'commits'].forEach(id => {
        document.getElementById('tab-' + id).style.display = (id === tab) ? 'block' : 'none';
      });
      document.querySelectorAll('#repoTabs .nav-link').forEach(link => link.classList.remove('active'));
      document.querySelector(`#repoTabs .nav-link[onclick*="${tab}"]`).classList.add('active');
    }

    // Monaco起動
    require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs' }});
    require(['vs/editor/editor.main'], function () {
      window.editor = monaco.editor.create(document.getElementById('editor'), {
        value: localStorage.getItem('savedCode') || `function hello() {\n  console.log("Hello from Monaco Editor!");\n}`,
        language: 'javascript',
        theme: 'vs-light',
        automaticLayout: true
      });

      document.getElementById('saveCode').onclick = function () {
        const code = editor.getValue();
        localStorage.setItem('savedCode', code);
        alert('保存しました!');
      };

      document.getElementById('themeToggle').onclick = function () {
        const theme = monaco.editor.getTheme() === 'vs-dark' ? 'vs-light' : 'vs-dark';
        monaco.editor.setTheme(theme);
      };
    });
  </script>
</body>
</html>

NeuroLink VR

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>フルダイブVR - 仮想世界体験</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Arial', sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
        }

        .vr-container {
            width: 95vw;
            height: 95vh;
            background: #000;
            border-radius: 20px;
            position: relative;
            overflow: hidden;
            box-shadow: 0 20px 50px rgba(0,0,0,0.3);
        }

        .boot-screen {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: linear-gradient(45deg, #0f0f23, #1a1a2e);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 100;
            transition: opacity 1s ease-out;
        }

        .boot-screen.hidden {
            opacity: 0;
            pointer-events: none;
        }

        .logo {
            font-size: 3rem;
            color: #00d4ff;
            margin-bottom: 30px;
            text-shadow: 0 0 20px #00d4ff;
            animation: pulse 2s infinite;
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.7; }
        }

        .loading-bar {
            width: 300px;
            height: 4px;
            background: #333;
            border-radius: 2px;
            overflow: hidden;
            margin-bottom: 20px;
        }

        .loading-progress {
            height: 100%;
            background: linear-gradient(90deg, #00d4ff, #ff006e);
            width: 0%;
            animation: loading 3s ease-out forwards;
        }

        @keyframes loading {
            to { width: 100%; }
        }

        .boot-text {
            color: #00d4ff;
            font-size: 1.2rem;
            margin-top: 20px;
        }

        #canvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        .hud {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 10;
        }

        .status-panel {
            position: absolute;
            top: 20px;
            left: 20px;
            background: rgba(0, 212, 255, 0.1);
            border: 1px solid #00d4ff;
            border-radius: 10px;
            padding: 15px;
            color: #00d4ff;
            font-family: 'Courier New', monospace;
            backdrop-filter: blur(10px);
        }

        .controls {
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 15px;
            pointer-events: all;
        }

        .control-btn {
            background: rgba(0, 212, 255, 0.2);
            border: 2px solid #00d4ff;
            color: #00d4ff;
            padding: 12px 20px;
            border-radius: 25px;
            cursor: pointer;
            font-size: 14px;
            font-weight: bold;
            transition: all 0.3s ease;
            backdrop-filter: blur(10px);
        }

        .control-btn:hover {
            background: rgba(0, 212, 255, 0.4);
            box-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
            transform: translateY(-2px);
        }

        .neural-overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: radial-gradient(circle at center, transparent 30%, rgba(0, 212, 255, 0.05) 100%);
            pointer-events: none;
            z-index: 5;
        }

        .dive-complete {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #00d4ff;
            font-size: 2rem;
            text-align: center;
            opacity: 0;
            animation: fadeInOut 4s ease-in-out;
            z-index: 50;
            text-shadow: 0 0 20px #00d4ff;
        }

        @keyframes fadeInOut {
            0%, 100% { opacity: 0; }
            20%, 80% { opacity: 1; }
        }
    </style>
</head>
<body>
    <div class="vr-container">
        <div class="boot-screen" id="bootScreen">
            <div class="logo">NeuroLink VR</div>
            <div class="loading-bar">
                <div class="loading-progress"></div>
            </div>
            <div class="boot-text">Neural Interface Initializing...</div>
        </div>

        <canvas id="canvas"></canvas>
        
        <div class="neural-overlay"></div>
        
        <div class="hud">
            <div class="status-panel">
                <div>Neural Sync: <span id="sync">98.7%</span></div>
                <div>Reality Index: <span id="reality">VIRTUAL</span></div>
                <div>Immersion Level: <span id="immersion">MAXIMUM</span></div>
                <div>Connected Users: <span id="users">1,247</span></div>
            </div>
        </div>

        <div class="controls">
            <button class="control-btn" onclick="changeWorld('cyber')">サイバー都市</button>
            <button class="control-btn" onclick="changeWorld('forest')">森林世界</button>
            <button class="control-btn" onclick="changeWorld('space')">宇宙ステーション</button>
            <button class="control-btn" onclick="disconnect()">ログアウト</button>
        </div>

        <div class="dive-complete" id="diveComplete">
            フルダイブ完了<br>
            <small>仮想世界へようこそ</small>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        let scene, camera, renderer, world = 'cyber';
        let particles = [];
        let buildings = [];
        let stars = [];
        let trees = [];
        
        // 起動シーケンス
        setTimeout(() => {
            document.getElementById('bootScreen').classList.add('hidden');
            initVR();
            setTimeout(() => {
                document.getElementById('diveComplete').style.display = 'block';
            }, 1000);
        }, 3500);

        function initVR() {
            // Three.js セットアップ
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas'), antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setClearColor(0x000011);

            // 初期世界を構築
            buildCyberWorld();
            
            // カメラ位置
            camera.position.set(0, 5, 10);
            
            // アニメーションループ
            animate();
            
            // HUD更新
            updateHUD();
        }

        function buildCyberWorld() {
            // クリア
            clearWorld();
            scene.fog = new THREE.Fog(0x000033, 10, 100);
            
            // ネオンビル群
            for (let i = 0; i < 50; i++) {
                const height = Math.random() * 20 + 5;
                const geometry = new THREE.BoxGeometry(
                    Math.random() * 3 + 1, 
                    height, 
                    Math.random() * 3 + 1
                );
                
                const material = new THREE.MeshBasicMaterial({ 
                    color: Math.random() > 0.5 ? 0x00ffff : 0xff00ff,
                    transparent: true,
                    opacity: 0.8
                });
                
                const building = new THREE.Mesh(geometry, material);
                building.position.set(
                    (Math.random() - 0.5) * 100,
                    height / 2,
                    (Math.random() - 0.5) * 100
                );
                
                scene.add(building);
                buildings.push(building);
            }
            
            // パーティクル効果
            createParticles(0x00ffff);
            
            // 地面
            const floorGeometry = new THREE.PlaneGeometry(200, 200);
            const floorMaterial = new THREE.MeshBasicMaterial({ 
                color: 0x001122, 
                transparent: true, 
                opacity: 0.5 
            });
            const floor = new THREE.Mesh(floorGeometry, floorMaterial);
            floor.rotation.x = -Math.PI / 2;
            scene.add(floor);
        }

        function buildForestWorld() {
            clearWorld();
            scene.fog = new THREE.Fog(0x228B22, 5, 50);
            
            // 木々
            for (let i = 0; i < 100; i++) {
                // 幹
                const trunkGeometry = new THREE.CylinderGeometry(0.3, 0.5, 8);
                const trunkMaterial = new THREE.MeshBasicMaterial({ color: 0x8B4513 });
                const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
                
                // 葉
                const leavesGeometry = new THREE.SphereGeometry(3);
                const leavesMaterial = new THREE.MeshBasicMaterial({ color: 0x228B22 });
                const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial);
                leaves.position.y = 6;
                
                const tree = new THREE.Group();
                tree.add(trunk);
                tree.add(leaves);
                
                tree.position.set(
                    (Math.random() - 0.5) * 80,
                    4,
                    (Math.random() - 0.5) * 80
                );
                
                scene.add(tree);
                trees.push(tree);
            }
            
            createParticles(0x90EE90);
            
            // 地面
            const floorGeometry = new THREE.PlaneGeometry(200, 200);
            const floorMaterial = new THREE.MeshBasicMaterial({ color: 0x2F4F2F });
            const floor = new THREE.Mesh(floorGeometry, floorMaterial);
            floor.rotation.x = -Math.PI / 2;
            scene.add(floor);
        }

        function buildSpaceWorld() {
            clearWorld();
            scene.fog = null;
            renderer.setClearColor(0x000000);
            
            // 星々
            for (let i = 0; i < 1000; i++) {
                const starGeometry = new THREE.SphereGeometry(0.1);
                const starMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
                const star = new THREE.Mesh(starGeometry, starMaterial);
                
                star.position.set(
                    (Math.random() - 0.5) * 200,
                    (Math.random() - 0.5) * 200,
                    (Math.random() - 0.5) * 200
                );
                
                scene.add(star);
                stars.push(star);
            }
            
            // 宇宙ステーション
            const stationGeometry = new THREE.TorusGeometry(10, 3, 8, 20);
            const stationMaterial = new THREE.MeshBasicMaterial({ 
                color: 0x888888, 
                wireframe: true 
            });
            const station = new THREE.Mesh(stationGeometry, stationMaterial);
            station.position.set(0, 0, -20);
            scene.add(station);
            
            createParticles(0xffffff);
        }

        function createParticles(color) {
            for (let i = 0; i < 200; i++) {
                const particleGeometry = new THREE.SphereGeometry(0.05);
                const particleMaterial = new THREE.MeshBasicMaterial({ 
                    color: color,
                    transparent: true,
                    opacity: 0.6
                });
                const particle = new THREE.Mesh(particleGeometry, particleMaterial);
                
                particle.position.set(
                    (Math.random() - 0.5) * 50,
                    Math.random() * 20,
                    (Math.random() - 0.5) * 50
                );
                
                particle.velocity = {
                    x: (Math.random() - 0.5) * 0.02,
                    y: Math.random() * 0.01,
                    z: (Math.random() - 0.5) * 0.02
                };
                
                scene.add(particle);
                particles.push(particle);
            }
        }

        function clearWorld() {
            buildings.forEach(b => scene.remove(b));
            trees.forEach(t => scene.remove(t));
            stars.forEach(s => scene.remove(s));
            particles.forEach(p => scene.remove(p));
            
            buildings = [];
            trees = [];
            stars = [];
            particles = [];
            
            // シーン全体をクリア
            while(scene.children.length > 0) {
                scene.remove(scene.children[0]);
            }
        }

        function animate() {
            requestAnimationFrame(animate);
            
            // カメラの自動回転
            const time = Date.now() * 0.0005;
            camera.position.x = Math.cos(time) * 20;
            camera.position.z = Math.sin(time) * 20;
            camera.lookAt(scene.position);
            
            // パーティクルアニメーション
            particles.forEach(particle => {
                particle.position.add(particle.velocity);
                
                if (particle.position.y > 25) {
                    particle.position.y = -5;
                }
                if (particle.position.x > 25) particle.position.x = -25;
                if (particle.position.x < -25) particle.position.x = 25;
                if (particle.position.z > 25) particle.position.z = -25;
                if (particle.position.z < -25) particle.position.z = 25;
            });
            
            // ビルの光る効果
            buildings.forEach(building => {
                if (Math.random() > 0.98) {
                    building.material.opacity = Math.random() * 0.5 + 0.5;
                }
            });
            
            renderer.render(scene, camera);
        }

        function changeWorld(newWorld) {
            world = newWorld;
            
            switch(world) {
                case 'cyber':
                    buildCyberWorld();
                    break;
                case 'forest':
                    buildForestWorld();
                    break;
                case 'space':
                    buildSpaceWorld();
                    break;
            }
        }

        function updateHUD() {
            setInterval(() => {
                document.getElementById('sync').textContent = (98 + Math.random() * 2).toFixed(1) + '%';
                document.getElementById('users').textContent = (1200 + Math.floor(Math.random() * 100)).toLocaleString();
            }, 2000);
        }

        function disconnect() {
            document.querySelector('.vr-container').style.opacity = '0';
            setTimeout(() => {
                document.body.innerHTML = `
                    <div style="display: flex; justify-content: center; align-items: center; height: 100vh; color: #00d4ff; font-size: 2rem; text-align: center;">
                        <div>
                            <div>Neural Link Disconnected</div>
                            <div style="font-size: 1rem; margin-top: 20px; opacity: 0.7;">現実世界へ戻りました</div>
                        </div>
                    </div>
                `;
            }, 1000);
        }

        // リサイズ対応
        window.addEventListener('resize', () => {
            if (camera && renderer) {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
            }
        });
    </script>
</body>
</html>

求人掲示板サイト.html

<!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>

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;
}