Common Worship • Daily Office • Lectionary

Church of England Lectionary Almanac

A daily companion for the Common Worship Almanac and Lectionary, arranged for quiet reading and practical worship planning.

Open the Almanac data

Choose the supplied lectionary data.xlsx spreadsheet to browse every dated row locally. Its wording, readings, prayers, observances, and source notes remain the source of truth.

Common Worship • Daily Office • Lectionary

Church of England Lectionary Almanac

A daily companion for the Common Worship Almanac and Lectionary, arranged for quiet reading and practical worship planning.

Open the Almanac data

Choose the supplied lectionary data.xlsx spreadsheet to browse every dated row locally. Its wording, readings, prayers, observances, and source notes remain the source of truth.

function readFile(file) { const reader=new FileReader(); reader.onload=e=>{ try { let raw; if(file.name.toLowerCase().endsWith(".json")) raw=JSON.parse(e.target.result); else { const wb=XLSX.read(e.target.result,{type:"array",cellDates:true}); raw=XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]],{defval:""}); } if(!Array.isArray(raw)||!raw.length) throw new Error(); importRows(raw); } catch { $("import-message").textContent="The selected file could not be read. Please choose the supplied spreadsheet or an export containing the required columns."; $("import-message").classList.remove("hidden"); } }; file.name.toLowerCase().endsWith(".json") ? reader.readAsText(file) : reader.readAsArrayBuffer(file); } function currentRow(){ return state.rows.find(r=>r.date===state.selected); } function render() { $("date-picker").value=state.selected || ""; renderCalendar(); renderDay(); } function renderCalendar() { const y=state.month.getFullYear(), m=state.month.getMonth(); $("month-name").textContent=new Intl.DateTimeFormat("en-GB",{month:"long",year:"numeric"}).format(state.month); const first=(new Date(y,m,1).getDay()+6)%7, count=new Date(y,m+1,0).getDate(), grid=$("calendar-grid"); grid.innerHTML=""; for(let i=0;i<=count;d++){ const key=`${y}-${String(m+1).padStart(2,"0")}-${String(d).padStart(2,"0")}`, row=state.rows.find(r=>r.date===key), btn=document.createElement("button"); btn.type="button"; btn.className="calendar-day relative rounded text-sm font-semibold "+(!row?"is-empty":"")+(key===state.selected?" is-selected":""); btn.textContent=d; btn.disabled=!row; if(row){ const bar=document.createElement("span"); bar.className="absolute inset-x-0 bottom-0 h-1 season-"+getSeason(row.subject+" "+row.description); bar.style.background=getSeason(row.subject+" "+row.description)==="purple"?"#613c73":getSeason(row.subject+" "+row.description)==="red"?"#a73c38":getSeason(row.subject+" "+row.description)==="white"?"#b78a27":"#426642"; btn.appendChild(bar); if(has(row,"saint")||has(row,"commemoration")||has(row,"ember")) btn.title=row.subject; btn.onclick=()=>{state.selected=key;state.month=new Date(key+"T12:00:00");render();}; } grid.appendChild(btn); } } function renderDay() { const row=currentRow(), card=$("daily-card"), empty=$("no-entry"); if(!row){card.classList.add("hidden");empty.classList.remove("hidden");return;} card.classList.remove("hidden"); empty.classList.add("hidden"); $("day-date").textContent=new Intl.DateTimeFormat("en-GB",{weekday:"long",day:"numeric",month:"long",year:"numeric"}).format(new Date(row.date+"T12:00:00")); $("day-subject").textContent=row.subject || "Daily lectionary"; const combined=row.subject+" "+row.description, season=getSeason(combined); $("season-strip").style.background=season==="purple"?"#613c73":season==="red"?"#a73c38":season==="white"?"#b78a27":"#426642"; const tags=[]; if(row.allDay)tags.push("All day"); if(has(row,"saint")||has(row,"commemoration"))tags.push("Commemoration"); if(has(row,"bcp"))tags.push("BCP"); if(has(row,"rogation"))tags.push("Rogation"); if(has(row,"ember"))tags.push("Ember Day"); $("day-tags").innerHTML=tags.map(t=>`${t}`).join(""); const cycle=combined.match(/Anglican Cycle of Prayer[\s:]*([\s\S]*?)(?=\n[A-Z][^\n]*:|$)/i); $("cycle-entry").textContent=cycle ? "Anglican Cycle of Prayer: "+cycle[1].trim() : ""; $("cycle-entry").classList.toggle("hidden",!cycle); const sections=parseSections(row.description).filter(s=>{ const f=state.filters; const text=(s.title+" "+s.content).toLowerCase(); return !f.size || [...f].some(x=>x==="sunday"?new Date(row.date+"T12:00").getDay()===0:x==="saint"?/saint|commemoration/.test(combined.toLowerCase()):text.includes(x==="cycle"?"anglican cycle":x)); }); $("section-list").innerHTML=""; sections.forEach((s,i)=>{ const wrap=document.createElement("section"); wrap.className="accordion rounded border border-[#ded2bb]"; const button=document.createElement("button"); button.className="flex w-full items-center justify-between gap-4 px-4 py-4 text-left"; button.setAttribute("aria-expanded",i===0?"true":"false"); button.innerHTML=`${s.title}`; const body=document.createElement("div"); body.className="accordion-body prose-source border-t border-[#eadfca] px-4 py-4 liturgical text-[15px] leading-7"; s.content.split(/\n\s*\n/).forEach(p=>{const el=document.createElement("p");el.textContent=p;body.appendChild(el);}); if(i===0)wrap.classList.add("open"); button.onclick=()=>{const is=wrap.classList.toggle("open");button.setAttribute("aria-expanded",String(is));}; wrap.append(button,body); $("section-list").appendChild(wrap); }); if(!sections.length) $("section-list").innerHTML='

No entry matches the selected filters for this date.

'; lucide.createIcons(); } $("file-input").addEventListener("change",e=>e.target.files[0]&&readFile(e.target.files[0])); $("prev-day").onclick=()=>move(-1); $("next-day").onclick=()=>move(1); function move(n){const i=state.rows.findIndex(r=>r.date===state.selected);if(state.rows[i+n]){state.selected=state.rows[i+n].date;state.month=new Date(state.selected+"T12:00:00");render();}} $("date-picker").onchange=e=>{state.selected=e.target.value;state.month=new Date(e.target.value+"T12:00:00");render();}; $("prev-month").onclick=()=>{state.month.setMonth(state.month.getMonth()-1);renderCalendar();}; $("next-month").onclick=()=>{state.month.setMonth(state.month.getMonth()+1);renderCalendar();}; $("filters").onclick=e=>{const b=e.target.closest("button");if(!b)return;const f=b.dataset.filter;state.filters.has(f)?state.filters.delete(f):state.filters.add(f);b.classList.toggle("active");renderDay();}; $("search").oninput=e=>{const q=e.target.value.toLowerCase().trim(), box=$("search-results");if(!q){box.classList.add("hidden");return;}const matches=state.rows.filter(r=>(r.date+" "+r.subject).toLowerCase().includes(q)).slice(0,20);box.innerHTML=matches.map(r=>``).join("")||'

No matching dated entry.

';box.classList.remove("hidden");}; $("search-results").onclick=e=>{const b=e.target.closest("[data-date]");if(b){state.selected=b.dataset.date;state.month=new Date(state.selected+"T12:00:00");$("search-results").classList.add("hidden");$("search").value="";render();}}; $("about-toggle").onclick=()=>{$("about-content").classList.toggle("hidden");$("about-toggle").setAttribute("aria-expanded",String(!$("about-content").classList.contains("hidden")));}; loadDefaultSpreadsheet(); lucide.createIcons();