Skip to content

Instantly share code, notes, and snippets.

@a70437043b
Created April 9, 2026 12:25
Show Gist options
  • Select an option

  • Save a70437043b/856d4bd34fe94412df240bdbd2b65723 to your computer and use it in GitHub Desktop.

Select an option

Save a70437043b/856d4bd34fe94412df240bdbd2b65723 to your computer and use it in GitHub Desktop.
my calendar
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
:root {
--bg: #191919; --card: #222; --text: #eee;
--c-red: #ff4d4f; --c-orange: #ffa940; --c-green: #73d13d;
--c-brown: #a0522d; --c-today: #ffd666;
--c-bday: #afeeee; --c-task: #ffd700; --c-other: #00ffff;
}
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); margin: 0; padding: 10px; display: flex; flex-direction: column; align-items: center; }
.calendar-box { width: 100%; max-width: 380px; background: var(--card); border-radius: 12px; padding: 15px; box-shadow: 0 8px 24px rgba(0,0,0,0.6); border: 1px solid #333; box-sizing: border-box; }
.nav-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.m-title { font-size: 1.15em; font-weight: 600; color: var(--c-today); }
.nav-btn { background: #333; border: 1px solid #444; color: white; padding: 4px 10px; border-radius: 6px; cursor: pointer; transition: 0.2s; }
.nav-btn:hover { background: #444; }
.grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; text-align: center; }
.w-day { font-size: 0.75em; color: #888; font-weight: bold; padding-bottom: 8px; }
.cell { aspect-ratio: 0.75; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; font-size: 0.8em; border-radius: 6px; position: relative; background: #2a2a2a; cursor: pointer; padding-top: 4px; transition: 0.2s; border: 1px solid transparent; }
.cell:hover { background: #333; }
.lunar-txt { font-size: 0.55em; color: #777; margin-top: 1px; transform: scale(0.85); white-space: nowrap; }
.is-selected { outline: 2px solid var(--c-today); background: rgba(255,214,102,0.05); }
.dots { display: flex; gap: 2px; position: absolute; bottom: 4px; flex-wrap: wrap; justify-content: center; width: 90%; height: 10px; align-items: center; }
.dot { width: 4px; height: 4px; border-radius: 50%; }
.dot-red { background: var(--c-red); } .dot-orange { background: var(--c-orange); }
.dot-green { background: var(--c-green); } .dot-brown { background: var(--c-brown); }
.dot-bday { background: var(--c-bday); } .dot-task { background: var(--c-task); } .dot-other { background: var(--c-other); }
.control-panel { width: 100%; max-width: 380px; margin-top: 12px; font-size: 0.85em; }
.display-info { min-height: 75px; text-align: center; margin-bottom: 10px; border-bottom: 1px solid #333; padding-bottom: 8px; line-height: 1.6; }
.input-box { display: flex; flex-direction: column; gap: 8px; }
.row { display: flex; gap: 6px; }
input { flex: 1; background: #111; border: 1px solid #444; color: #fff; padding: 7px; border-radius: 5px; font-size: 0.8em; outline: none; }
input:focus { border-color: var(--c-today); }
.btn { border: none; color: #000; padding: 6px 12px; border-radius: 5px; cursor: pointer; font-weight: 600; font-size: 0.75em; white-space: nowrap; }
.btn-bday { background: var(--c-bday); } .btn-task { background: var(--c-task); } .btn-other { background: var(--c-other); }
.legend { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; font-size: 0.65em; margin-top: 12px; opacity: 0.7; }
.legend span { display: flex; align-items: center; gap: 3px; }
</style>
</head>
<body>
<div class="calendar-box">
<div class="nav-header">
<button class="nav-btn" onclick="changeMonth(-1)"><</button>
<div class="m-title" id="mYear"></div>
<button class="nav-btn" onclick="changeMonth(1)">></button>
</div>
<div class="grid" id="grid"></div>
<div class="control-panel">
<div id="info" class="display-info">點擊日期或輸入:YYYY/MM/DD 事件</div>
<div class="input-box">
<div class="row"><input type="text" id="in-bday" placeholder="2026/04/20 生日姓名..."><button class="btn btn-bday" onclick="save('bday')">存生日</button></div>
<div class="row"><input type="text" id="in-task" placeholder="2026/04/20 任務事件..."><button class="btn btn-task" onclick="save('task')">存任務</button></div>
<div class="row"><input type="text" id="in-other" placeholder="2026/04/20 其他事項..."><button class="btn btn-other" onclick="save('other')">存其他</button></div>
</div>
<div class="legend">
<span><i class="dot dot-red"></i> 節日</span>
<span><i class="dot dot-orange"></i> 職業</span>
<span><i class="dot dot-green"></i> 神誕</span>
<span><i class="dot dot-brown"></i> 社會</span>
<span><i class="dot dot-bday"></i> 生日</span>
<span><i class="dot dot-task"></i> 任務</span>
<span><i class="dot dot-other"></i> 其他</span>
</div>
</div>
</div>
<script>
const RED_DAYS = {"01-01":"元旦", "02-28":"和平紀念日", "03-08":"婦女節", "04-04":"兒童節", "05-01":"勞動節", "09-03":"軍人節", "09-28":"教師節", "10-10":"國慶日", "12-25":"行憲紀念日"};
const LUNAR_RED = {"12-29":"除夕", "12-30":"除夕", "01-01":"春節", "01-02":"回娘家", "01-03":"赤狗日", "01-04":"接神日", "01-05":"開市", "01-15":"元宵節", "05-05":"端午節", "07-15":"中元節", "08-15":"中秋節"};
const ORANGE_DAYS = {"01-11":"司法節", "01-15":"藥師節", "01-19":"消防節", "01-23":"世界自由日", "02-04":"農民節", "03-01":"兵役節", "03-05":"童軍節", "03-12":"植樹節", "03-20":"郵政節", "03-21":"氣象節", "03-25":"美術節", "03-26":"廣播節", "03-29":"青年節", "04-01":"主計節", "05-04":"文藝節", "05-05":"舞蹈節", "05-12":"護師節", "06-03":"禁煙節", "06-06":"工程師節", "06-09":"鐵路節", "06-15":"警察節", "07-01":"漁民節", "07-11":"航海節", "08-14":"空軍節", "09-01":"記者節", "09-09":"體育節", "09-13":"法律日", "10-21":"華僑節", "10-25":"臺灣光復節", "10-31":"榮民節", "11-01":"商人節", "11-11":"地政節", "11-12":"醫師節", "12-12":"憲兵節", "12-27":"建築師節"};
const LUNAR_GREEN = {"01-09":"天公生", "02-02":"土地公生", "02-19":"觀音誕", "03-03":"玄天上帝誕", "03-15":"保生大帝誕", "03-23":"媽祖生", "04-08":"浴佛節", "04-12":"金門迎城隍", "04-14":"呂洞賓誕", "05-01":"新莊大眾爺誕", "05-13":"霞海城隍誕", "06-19":"觀音成道", "06-24":"關聖帝君誕", "07-07":"七娘媽生", "07-18":"王母娘娘誕", "07-30":"地藏王誕", "08-22":"廣澤尊王誕", "09-09":"中壇元帥誕", "09-19":"觀音出家日", "10-15":"下元節", "10-22":"青山王祭", "11-17":"阿彌陀佛誕", "12-08":"臘八節", "12-16":"尾牙", "12-24":"送神日"};
const BROWN_DAYS = {"02-14":"西洋情人節", "03-14":"白色情人節", "03-21":"消除種族歧視日", "04-07":"言論自由日", "04-22":"世界地球日", "04-23":"世界閱讀日", "05-17":"國際不再恐同日", "05-31":"世界無菸日", "06-08":"國家海洋日", "06-26":"國際反毒日", "08-01":"原住民族日", "08-08":"父親節", "09-21":"國家防災日", "10-24":"聯合國日", "11-17":"國際學生節", "11-20":"世界兒童日", "12-01":"世界愛滋病日", "12-03":"國際身心障礙者日", "12-10":"世界人權日", "12-31":"跨年夜"};
const SOLAR_TERMS = {"02-04":"立春", "02-19":"雨水", "03-05":"驚蟄", "03-20":"春分", "04-05":"清明", "04-20":"穀雨", "05-05":"立夏", "05-21":"小滿", "06-05":"芒種", "06-21":"夏至", "07-07":"小暑", "07-23":"大暑", "08-07":"立秋", "08-23":"處暑", "09-07":"白露", "09-23":"秋分", "10-08":"寒露", "10-23":"霜降", "11-07":"立冬", "11-22":"小雪", "12-07":"大雪", "12-21":"冬至"};
const LUNAR_INFO = [
0x0a5b0, 0x052b0, 0x0a930, 0x06aa0, 0x0ad50, 0x04b50, 0x0a4d0, 0x0a570, 0x064b0, 0x074a0, 0x06ad0
];
const LUNAR_START = [
new Date(2026, 1, 17), new Date(2027, 1, 6), new Date(2028, 0, 26), new Date(2029, 1, 13),
new Date(2030, 1, 3), new Date(2031, 0, 23), new Date(2032, 1, 11), new Date(2033, 0, 31),
new Date(2034, 1, 19), new Date(2035, 1, 8), new Date(2036, 0, 28)
];
function getLunar(y, m, d) {
if (y < 2026 || y > 2035) return "農--/--";
let date = new Date(y, m, d);
let idx = y - 2026;
let diff = Math.floor((date - LUNAR_START[idx]) / 86400000);
if (diff < 0) {
idx--;
if (idx < 0) return "農12/--";
diff = Math.floor((date - LUNAR_START[idx]) / 86400000);
}
let info = LUNAR_INFO[idx];
let leap = info & 0xf;
let lm = 1, ld = diff + 1;
for (let i = 1; i <= 12; i++) {
let days = (info & (0x10000 >> i)) ? 30 : 29;
if (ld <= days) { lm = i; break; }
ld -= days;
if (i === leap) {
let lDays = (info & 0x10000) ? 30 : 29;
if (ld <= lDays) { lm = i; break; }
ld -= lDays;
}
}
return `農${String(lm).padStart(2, '0')}/${String(ld).padStart(2, '0')}`;
}
let viewDate = new Date();
let selectedFullK = new Date().toISOString().split('T')[0];
function render() {
const y = viewDate.getFullYear(); const m = viewDate.getMonth();
document.getElementById('mYear').innerText = `${y}年 ${m+1}月`;
const g = document.getElementById('grid');
g.innerHTML = ["日","一","二","三","四","五","六"].map(d=>`<div class="w-day">${d}</div>`).join('');
const first = new Date(y, m, 1).getDay(); const days = new Date(y, m+1, 0).getDate();
for(let i=0; i<first; i++) g.innerHTML += '<div></div>';
for(let d=1; d<=days; d++) {
const k = `${String(m+1).padStart(2,'0')}-${String(d).padStart(2,'0')}`;
const fullK = `${y}-${k}`;
const lunarStr = getLunar(y, m, d);
const lk = lunarStr.replace("農", "").replace("/", "-");
let dots = "";
if(RED_DAYS[k] || LUNAR_RED[lk]) dots += '<div class="dot dot-red"></div>';
if(ORANGE_DAYS[k]) dots += '<div class="dot dot-orange"></div>';
if(LUNAR_GREEN[lk]) dots += '<div class="dot dot-green"></div>';
if(BROWN_DAYS[k]) dots += '<div class="dot dot-brown"></div>';
if(localStorage.getItem(fullK+'-bday')) dots += '<div class="dot dot-bday"></div>';
if(localStorage.getItem(fullK+'-task')) dots += '<div class="dot dot-task"></div>';
if(localStorage.getItem(fullK+'-other')) dots += '<div class="dot dot-other"></div>';
const isSel = fullK === selectedFullK ? 'is-selected' : '';
g.innerHTML += `<div class="cell ${isSel}" onclick="view('${fullK}')">${d}<div class="lunar-txt">(${lunarStr})</div><div class="dots">${dots}</div></div>`;
}
}
function changeMonth(offset) { viewDate.setMonth(viewDate.getMonth() + offset); render(); }
function view(date) {
selectedFullK = date;
const parts = date.split('-');
const k = `${parts[1]}-${parts[2]}`;
const lunarStr = getLunar(parseInt(parts[0]), parseInt(parts[1])-1, parseInt(parts[2]));
const lk = lunarStr.replace("農", "").replace("/", "-");
let txt = `<span style="font-weight:bold">【${date}】 ${lunarStr}</span><br>`;
if(SOLAR_TERMS[k]) txt += `<span style="color:var(--c-today)">✨ 節氣: ${SOLAR_TERMS[k]}</span><br>`;
if(RED_DAYS[k] || LUNAR_RED[lk]) txt += `<span style="color:var(--c-red)">🔴節慶: ${RED_DAYS[k] || LUNAR_RED[lk]}</span> `;
if(ORANGE_DAYS[k]) txt += `<span style="color:var(--c-orange)">🟠紀念: ${ORANGE_DAYS[k]}</span> `;
if(LUNAR_GREEN[lk]) txt += `<span style="color:var(--c-green)">🟢神誕: ${LUNAR_GREEN[lk]}</span> `;
if(BROWN_DAYS[k]) txt += `<span style="color:var(--c-brown)">🟤社會: ${BROWN_DAYS[k]}</span> `;
const b = localStorage.getItem(date+'-bday'); if(b) txt += `<br><span style="color:var(--c-bday)">🎂生日: ${b}</span>`;
const t = localStorage.getItem(date+'-task'); if(t) txt += `<br><span style="color:var(--c-task)">💛任務: ${t}</span>`;
const o = localStorage.getItem(date+'-other'); if(o) txt += `<br><span style="color:var(--c-other)">💎其他: ${o}</span>`;
document.getElementById('info').innerHTML = txt || "本日無特定安排";
render();
}
function save(type) {
const val = document.getElementById('in-'+type).value;
if(!val) return;
let targetKey = selectedFullK;
let content = val;
const dateMatch = val.match(/(\d{4})[/-](\d{1,2})[/-](\d{1,2})/);
if(dateMatch) {
targetKey = `${dateMatch[1]}-${dateMatch[2].padStart(2,'0')}-${dateMatch[3].padStart(2,'0')}`;
content = val.replace(dateMatch[0], '').trim();
}
localStorage.setItem(targetKey+'-'+type, content);
document.getElementById('in-'+type).value = '';
view(selectedFullK);
}
render(); view(selectedFullK);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment