# index.html
CaoBooru
# post.html
Post - CaoBooru
# style.css
:root{
--bg:#000;
--panel:#0f0f0f;
--muted:#bfc7d1;
--accent:#8fd7ff;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
background:var(--bg);
color:var(--muted);
font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
}
.topbar{
display:flex;
align-items:center;
gap:12px;
background:linear-gradient(180deg,#0b0b0b, #0f0f0f);
padding:10px 16px;
border-bottom:1px solid #222;
}
.logo{width:36px;height:36px;object-fit:cover;border-radius:4px}
.nav-links{margin-left:8px;display:flex;gap:18px}
.nav-links a{color:var(--muted);text-decoration:none;font-weight:600}
.layout{display:grid;grid-template-columns:260px 1fr;min-height:calc(100vh - 56px)}
.sidebar{background:var(--panel);padding:16px;border-right:1px solid #111}
.search-box input{width:100%;padding:8px;border-radius:6px;border:1px solid #222;background:#0b0b0b;color:var(--muted)}
.tag-list{margin-top:14px;font-size:14px;line-height:1.6}
.tag-list span{display:inline-block;padding:6px 8px;margin:4px 6px 0 0;background:#071526;color:var(--accent);border-radius:6px;cursor:pointer}
.gallery{padding:18px;display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}
.thumb-link{display:block;border-radius:6px;overflow:hidden;background:#070707;border:1px solid #111}
.thumb-img{width:100%;height:220px;object-fit:cover;display:block}
.post-view{padding:20px}
.post-inner{max-width:1100px;margin:0 auto}
.post-image{width:100%;height:auto;border-radius:6px;display:block}
.post-tags{margin-top:10px}
.post-tags span{display:inline-block;margin-right:8px;padding:6px 8px;background:#071526;color:var(--accent);border-radius:6px}
/* mobile */
@media (max-width:900px){
.layout{grid-template-columns:1fr}
.sidebar{order:2}
}
# posts.json
{
"posts": [
{
"id": "p001",
"file": "img/sample1.png",
"tags": ["general","character","copyright"]
},
{
"id": "p002",
"file": "img/sample2.png",
"tags": ["general","mothgirl","copyright"]
}
]
}
# gallery.js
// Minimal booru-like static frontend for Neocities
let posts = [];
let allTags = [];
async function loadPosts(){
try{
const res = await fetch('posts.json',{cache:'no-store'});
const data = await res.json();
posts = data.posts || [];
buildTagIndex();
renderTags();
renderGallery(posts);
initSearchAutocomplete();
// if URL has ?tag=foo prefilter
const q = new URLSearchParams(location.search).get('tag');
if(q) filterByTag(q);
}catch(e){
console.error('Failed to load posts.json',e);
}
}
function buildTagIndex(){
const set = new Set();
posts.forEach(p=>p.tags.forEach(t=>set.add(t)));
allTags = Array.from(set).sort((a,b)=>a.localeCompare(b));
}
function renderTags(){
const el = document.getElementById('tagList');
if(!el) return;
el.innerHTML = allTags.map(t=>`${t}`).join('');
}
function renderGallery(list){
const gal = document.getElementById('gallery');
if(!gal) return;
gal.innerHTML = '';
list.forEach(p=>{
const a = document.createElement('a');
a.className = 'thumb-link';
a.href = `post.html?id=${encodeURIComponent(p.id)}`;
const img = document.createElement('img');
img.className='thumb-img';
img.src = p.file;
img.alt = p.tags.join(', ');
a.appendChild(img);
gal.appendChild(a);
});
}
function filterByTag(tag){
const filtered = posts.filter(p=>p.tags.includes(tag));
renderGallery(filtered);
const input = document.getElementById('searchInput');
if(input) input.value = tag;
}
// simple search by substring of tags
function initSearchAutocomplete(){
const input = document.getElementById('searchInput');
if(!input) return;
let currentList = [];
const ac = document.createElement('div');
ac.className='ac-box';
ac.style.position='absolute';
ac.style.background='#0b0b0b';
ac.style.border='1px solid #222';
ac.style.padding='6px';
ac.style.display='none';
ac.style.zIndex='2000';
input.parentNode.appendChild(ac);
input.addEventListener('input',e=>{
const q = e.target.value.trim().toLowerCase();
if(!q){ ac.style.display='none'; renderGallery(posts); return; }
const matches = allTags.filter(t=>t.includes(q));
currentList = matches.slice(0,12);
ac.innerHTML = currentList.map(t=>`${t}
`).join('');
ac.style.display = currentList.length? 'block':'none';
positionAc(input,ac);
// live filter
const filtered = posts.filter(p=>p.tags.some(t=>t.includes(q)));
renderGallery(filtered);
});
ac.addEventListener('click', e=>{
const t = e.target.closest('.ac-item'); if(!t) return;
const tag = t.getAttribute('data-tag');
filterByTag(tag);
ac.style.display='none';
});
document.addEventListener('click',e=>{ if(!ac.contains(e.target) && e.target!==input) ac.style.display='none'; });
}
function positionAc(input,ac){
const r = input.getBoundingClientRect();
ac.style.left = (r.left + window.scrollX) + 'px';
ac.style.top = (r.bottom + window.scrollY + 6) + 'px';
ac.style.minWidth = r.width + 'px';
}
loadPosts();
# post.js
let postsData = [];
async function loadPost(){
try{
const res = await fetch('posts.json',{cache:'no-store'});
const data = await res.json();
postsData = data.posts || [];
renderTagsSidebar();
const id = new URLSearchParams(location.search).get('id');
const post = postsData.find(p=>p.id===id);
if(!post){ document.getElementById('postView').innerHTML = 'Post not found.
'; return; }
document.getElementById('postImage').src = post.file;
document.getElementById('postImage').alt = post.tags.join(', ');
document.getElementById('postTags').innerHTML = post.tags.map(t=>`${t}`).join('');
}catch(e){ console.error(e); }
}
function renderTagsSidebar(){
const set = new Set();
postsData.forEach(p=>p.tags.forEach(t=>set.add(t)));
const arr = Array.from(set).sort((a,b)=>a.localeCompare(b));
const el = document.getElementById('tagList');
if(!el) return;
el.innerHTML = arr.map(t=>`${t}`).join('');
}
loadPost();