Blot Theme Date Sorting Fix
Summary
Fixed the Blot blog theme to sort and display posts by the Date: front matter field instead of file modification time, ensuring manually-dated posts (like the EBB design documents) appear in their correct chronological position.
Initial Website Push:
- Renamed log files from
emoji-ball-battles-*.mdtoebb-*.mdand pushed to Blot
Diagnosing the Sorting Problem:
- User noticed posts weren't sorting by their
Date:front matter field (e.g., EBB posts dated Jan 19 were appearing at Jan 20 position) - Examined the theme files:
pagination.html,navigation-js.js, andscript.js - The sidebar JavaScript was sorting by
data-dateattribute (file creation timestamp), not the front matter Date
Finding the Right Blot Template Variable:
- Initial attempt using
{{date}}worked but output formatted strings like "January 19, 2026" — same-day posts couldn't be differentiated by time - Attempted
{{#formatDate}}YYYY-MM-DD HH:mm{{/formatDate}}but this broke the template entirely - Used Blot's
?debug=truequery parameter to inspect available template variables - Discovered
{{dateStamp}}provides the raw Unix timestamp of the front matter Date field
Three File Changes:
- pagination.html: Changed
data-frontmatter-date="{{date}}"todata-frontmatter-date="{{dateStamp}}"to output raw timestamp - navigation-js.js: Added
getDateValue()helper insortTree()to prefer frontmatter date over created date, with regex check for numeric timestamp strings - script.js: Added same
getDateValue()helper forbuildHomeLists()sorting, plus changedbuildList()to display frontmatter date:formatDate(li.getAttribute('data-frontmatter-date') || li.getAttribute('data-date'))
Debugging Along the Way:
- Had to clear localStorage (
localStorage.clear()) after each change since sidebar is cached - Encountered
TypeError: Cannot read properties of undefined (reading 'localeCompare')— fixed by adding regex check/^\d+$/.test(frontmatterDate)to detect numeric timestamp strings and skip Date parsing
Could you commit and push the website for me? I changed a few files around.
Bash (cd E:/a327ex/anchor.blot.im && git status)
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: logs/emoji-ball-battles-implementation-plan.md
deleted: logs/emoji-ball-battles-temporary-end-design.md
modified: pages/home.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
logs/ebb-implementation-plan.md
logs/ebb-temporary-end-design.md
no changes added to commit (use "git add" and/or "git commit -a")
Bash (cd E:/a327ex/anchor.blot.im && git add -A && git commit --allow-empty-message -m...)
[master f5b04cd]
3 files changed, 8 insertions(+), 3 deletions(-)
rename logs/{emoji-ball-battles-implementation-plan.md => ebb-implementation-plan.md} (97%)
rename logs/{emoji-ball-battles-temporary-end-design.md => ebb-temporary-end-design.md} (98%)
To https://blot.im/clients/git/end/anchor.git
9997908..f5b04cd master -> master
Done. Pushed the renamed log files and home.md changes to Blot.
Please do it again, I've made more changes.
Bash (cd E:/a327ex/anchor.blot.im && git add -A && git commit --allow-empty-message -m...)
[master e31e66d]
3 files changed, 6 insertions(+)
To https://blot.im/clients/git/end/anchor.git
f5b04cd..e31e66d master -> master
Done.
Blot is not reading the Date: parameters properly when ordering the list on my website. I believe it's because we created a custom ordering mechanism to order posts by when they were posted/last edited instead of alphabetically which was the original for this theme. If you look at the home.md file you can see that it lists both notes and logs using specific lists. I can paste you the .js .css .html files for the blot theme and can you help me additionally have the theme read the Date: attribute at the top of the post (if it exists) so that it's also taken into account for the sorting?
Read (E:/a327ex/anchor.blot.im/pages/home.md)
1→Title:
2→Menu: no
3→Link: /
4→
5→This site serves as a devlog for my work during 2026. In 2025, I worked on [It Follows](https://a327ex.com/posts/it_follows) to hone my skills as a writer.
6→In 2026, I'm working on games by using the new AI models (i.e. Opus 4.5) to help with my engine's rewrite and with designing and coding the games themselves.
7→
8→To the left are sections that contain various documents:
9→
10→* context: context documents used in every AI interaction so it codes like me
11→* files: files for posts in the logs or notes section
12→* logs: daily logs of my interactions with AI (auto-generated)
13→* notes: notes about various topics, dev related or not
14→
15→This website currently tracks two projects:
16→
17→* [Anchor](https://github.com/a327ex/Anchor)
18→* [Emoji Ball Battles (EBB)](https://github.com/a327ex/emoji-ball-battles)
19→
20→The engine repository is always public. Any game repositories are private until the game is released.
21→
22→#####
23→
24→<div id ="notes-list" class="archives-grid"></div>
25→
26→<div id="logs-list" class="archives-grid"></div>
27→
28→###
29→
30→[a327ex](https://a327ex.com) · [GitHub](https://github.com/a327ex) · [Steam](https://store.steampowered.com/developer/a327ex) · [X](https://x.com/a327ex)
... [1 more lines]
I see the structure - you have notes-list and logs-list divs that get populated by JavaScript. Go ahead and paste the JS, CSS, and HTML files for your Blot theme and I'll help you add Date: front matter support to the sorting logic.
I believe these are the relevant files, ask more if needed.
<!DOCTYPE html> {{> head}}
{{> navigation}}{{> footer}} Archives
{{> search-form}}{{#allEntries}}{{title}} {{#formatDate}}MMM D, YYYY{{/formatDate}}{{/allEntries}}labelForLi(li) { if (li.classList.contains("folder")) { return ( li.querySelector(":scope > .folder-label")?.textContent?.trim() || "" ); } const a = li.querySelector(":scope > a"); return a?.textContent?.trim() || li.getAttribute("data-filename") || ""; }
sortTree(ul) { const children = Array.from(ul.children).filter((n) => n.tagName === "LI"); const folders = children.filter((li) => li.classList.contains("folder")); const files = children.filter((li) => !li.classList.contains("folder"));
const cmpAlpha = (a, b) => this.labelForLi(a).localeCompare(this.labelForLi(b), undefined, { sensitivity: "base", });
// Sort by date descending (newest first) const cmpDate = (a, b) => { const dateA = a.getAttribute("data-date") || "0"; const dateB = b.getAttribute("data-date") || "0"; return dateB.localeCompare(dateA); };
folders.sort(cmpAlpha); files.sort(cmpDate);
[...folders, ...files].forEach((li) => ul.appendChild(li));
folders.forEach((li) => { const sub = li.querySelector(":scope > ul.submenu"); if (sub) this.sortTree(sub); }); }
// ------- toggle ------- setFolder(li, open) { li.setAttribute("aria-expanded", open ? "true" : "false"); const submenu = li.querySelector(":scope > ul.submenu"); if (submenu) submenu.hidden = !open; } toggleFolder(li) { const open = li.getAttribute("aria-expanded") === "true"; this.setFolder(li, !open); this._saveCache(); }
// ------- default expand ------- expandToActiveIfAny() { const active = this.root.querySelector("a.active"); if (!active) return; let ul = active.closest("ul"); while (ul && ul !== this.root) { const folder = ul.parentElement?.closest("li.folder"); if (!folder) break; this.setFolder(folder, true); ul = folder.parentElement?.closest("ul"); } }
// ------- events ------- _bindEvents() { this.root.addEventListener("click", (e) => { const btn = e.target.closest("button.folder-label"); if (!btn || !this.root.contains(btn)) return; const folder = btn.closest("li.folder"); if (folder) this.toggleFolder(folder); });
this.root.addEventListener("keydown", (e) => { const btn = e.target.closest("button.folder-label"); if (!btn) return; if (e.key === "Enter" || e.key === " ") { e.preventDefault(); const folder = btn.closest("li.folder"); if (folder) this.toggleFolder(folder); } });}
// ------- init ------- async init() { if (!this.root) return;
const cached = this._loadCache(); if (cached) { this.root.innerHTML = cached; this._bindEvents(); this.root.classList.add("initialized"); return; } await this.loadAllPages(); this.build(); this.expandToActiveIfAny(); this._bindEvents(); this.root.classList.add("initialized"); this._saveCache();}
static saveCache() { try { localStorage.setItem( "sidebarState:" + document.querySelector('meta[name="blot-cache-id"]')?.content, document.querySelector(".sidebar").innerHTML ); } catch {} } }
// boot (async function boot() { const start = async () => { await new SidebarNavigation().init(); }; if (document.querySelector(".sidebar")) start(); else { const mo = new MutationObserver(() => { if (document.querySelector(".sidebar")) { start(); mo.disconnect(); } }); mo.observe(document.documentElement, { childList: true, subtree: true }); } })();
// make the class available globally if (typeof window !== "undefined") window.SidebarNavigation = SidebarNavigation;
Sorry I pressed ESC by mistake. I don't know if everything that's needed is defined in those two.
No problem. I can see there's already a cmpDate function that sorts by data-date attribute, but I need to see how that attribute gets populated. Can you show me:
- The
loadAllPages()method - this likely fetches the entries - The
build()method - this likely creates the list items and setsdata-date
Those will show me where the date data comes from and where we'd need to read the front matter Date: field.
// Plugin JavaScript for analytics embed code {{{appJS}}}
{{> heading.js}} {{> pre-copy.js}}
class PageTransitioner { constructor(linkSelector, contentSelector) { this.linkSelector = linkSelector; this.contentSelector = contentSelector; this.pageCache = new Map(); this.currentXHR = null;
this.init();}
init() { function isModified(e) { // Cmd (mac), Ctrl, Shift, Alt, or non-left mouse button return e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0; }
function isInternal(link) { if (!link || !link.href) return false; try { const linkURL = new URL(link.href, window.location.href); return linkURL.origin === window.location.origin; } catch { return false; } } function isSameDocumentHash(link) { if (!link || !link.href) return false; const linkURL = new URL(link.href, window.location.href); return ( linkURL.origin === window.location.origin && linkURL.pathname === window.location.pathname && linkURL.hash.length > 1 // has a fragment ); } // Hover prefetch: skip hashes document.addEventListener("mouseover", (e) => { const link = e.target.closest(this.linkSelector); if (isInternal(link) && !isSameDocumentHash(link)) this.prefetch(link.href); }); // Click nav: skip hashes document.addEventListener("click", (e) => { const link = e.target.closest(this.linkSelector); if (!link) return; // Let browser handle new-tab/window behavior and same-document hashes if ( isModified(e) || link.target === "_blank" || link.hasAttribute("download") ) return; // Let the browser handle same-page anchors (footnotes/backrefs) if (isSameDocumentHash(link)) return; if (isInternal(link)) { e.preventDefault(); link.blur(); this.navigate(link.href); } }); // Handle browser back/forward window.addEventListener("popstate", (e) => { if (e.state?.url) { this.navigate(e.state.url, false); } });}
async prefetch(url) { if (this.pageCache.has(url)) return;
try { const response = await fetch(url + "?partial=true"); const text = await response.text(); this.pageCache.set(url, text); } catch (err) { console.warn("Prefetch failed:", err); }}
async navigate(url, pushState = true) { if (this.currentXHR) { this.currentXHR.abort(); }
const content = document.querySelector(this.contentSelector); if (!content) return; // clear the existing content content.innerHTML = ''; content.classList.add("loading"); // close the mobile nav when a link is clicked document.querySelector('#toggle-left').checked = false; try { let html; if (this.pageCache.has(url)) { html = this.pageCache.get(url); } else { const controller = new AbortController(); this.currentXHR = controller; const response = await fetch(url + "?partial=true", { signal: controller.signal, }); html = await response.text(); this.pageCache.set(url, html); } const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); const newContent = doc.querySelector(this.contentSelector); if (newContent) { content.innerHTML = newContent.innerHTML; document.title = doc.title; if (pushState) { history.pushState({ url }, "", url); } // Re-run scripts content.querySelectorAll("script").forEach((oldScript) => { const newScript = document.createElement("script"); Array.from(oldScript.attributes).forEach((attr) => { newScript.setAttribute(attr.name, attr.value); }); newScript.textContent = oldScript.textContent; oldScript.parentNode.replaceChild(newScript, oldScript); }); // remove class 'active' from all links document.querySelectorAll(".sidebar a").forEach((link) => { link.classList.remove("active"); }); // add class 'active' to the current link document.querySelectorAll(".sidebar a").forEach((link) => { if (link.href === url) { link.classList.add("active"); SidebarNavigation.saveCache(); } }); window.scrollTo(0, 0); renderHeadingAnchors(); preCopy(); buildHomeLists(); } } catch (err) { if (err.name === "AbortError") return; console.error("Navigation failed:", err); } finally { content.classList.remove("loading"); this.currentXHR = null; }} }
function buildHomeLists() { const notesContainer = document.getElementById('notes-list'); const logsContainer = document.getElementById('logs-list');
if (!notesContainer && !logsContainer) return;
const sidebar = document.querySelector('.sidebar'); if (!sidebar) return;
// Wait for sidebar to be initialized const observer = new MutationObserver(() => { if (sidebar.classList.contains('initialized')) { observer.disconnect(); populateLists(); } });
if (sidebar.classList.contains('initialized')) { populateLists(); } else { observer.observe(sidebar, { attributes: true }); }
function populateLists() { const items = Array.from(sidebar.querySelectorAll('li[data-path]'));
const notes = items.filter(li => { const path = li.getAttribute('data-path') || ''; return path.startsWith('/notes/') && !path.startsWith('/notes/') || path.match(/^\/notes\/[^/]+$/); }).filter(li => { // Only top-level notes, not files in subfolders const path = li.getAttribute('data-path') || ''; const parts = path.replace('/notes/', '').split('/'); return parts.length === 1; }); const logs = items.filter(li => { const path = li.getAttribute('data-path') || ''; return path.startsWith('/logs/'); }).filter(li => { const path = li.getAttribute('data-path') || ''; const parts = path.replace('/logs/', '').split('/'); return parts.length === 1; }); // Sort by date descending const sortByDate = (a, b) => { const dateA = a.getAttribute('data-date') || '0'; const dateB = b.getAttribute('data-date') || '0'; return dateB.localeCompare(dateA); }; notes.sort(sortByDate); logs.sort(sortByDate); if (notesContainer) { notesContainer.innerHTML = buildList(notes); } if (logsContainer) { logsContainer.innerHTML = buildList(logs); }}
function buildList(items) { if (items.length === 0) { return '
No posts yet.
'; }return items.map(li => { const link = li.querySelector('a'); const href = link?.getAttribute('href') || '#'; const title = link?.textContent || 'Untitled'; const date = formatDate(li.getAttribute('data-date')); return ` <div class="archive-item"> <a href="${href}" class="archive-title">${title}</a> <span class="archive-date">${date}</span> </div> `; }).join('');}
function formatDate(timestamp) { if (!timestamp) return ''; const date = new Date(parseInt(timestamp, 10)); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } }
// Initial load if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', buildHomeLists); } else { buildHomeLists(); }
// Initialize with your selectors new PageTransitioner("a", "main");
Are they defined here? This is script.js
I can see buildHomeLists() uses data-date from the sidebar items, but I need to see where those data-date attributes are set. That would be in the loadAllPages() and build() methods of the SidebarNavigation class. Can you show me the full SidebarNavigation class, particularly those two methods?
I'm not sure I know where those are. I pasted you the full navigation-js.js file, that's the only file related to navigation, I believe.
The snippet you pasted earlier was cut off - it showed sortTree, setFolder, toggleFolder, expandToActiveIfAny, _bindEvents, and init methods, but not loadAllPages() or build(). These are called in init():
await this.loadAllPages();
this.build();
Could you paste the full navigation-js.js file? Those methods should be in there and they're what I need to see - that's where the data-date attribute gets set on the list items.
// Map folder [Eg] to 'Eg' const STRIP_TAG_TOKENS = true;
class SidebarNavigation { constructor() { this.root = document.querySelector(".sidebar"); if (!this.root) return; this.cacheKey = "sidebarState:" + document.querySelector('meta[name="blot-cache-id"]')?.content; this.maxPages = 100; }
// ------- cache ------- loadCache() { try { return localStorage.getItem(this.cacheKey); } catch { return null; } } saveCache() { try { localStorage.setItem(this.cacheKey, this.root.innerHTML); } catch {} } _clearCache() { try { localStorage.removeItem(this.cacheKey); } catch {} }
// ------- pagination ------- async loadAllPages() { if (!this.root) return; const parseHTML = (html) => { const t = document.createElement("template"); t.innerHTML = html; return t.content; };
let guard = 0; while (true) { if (++guard > this.maxPages) break; const nextEl = this.root.querySelector(":scope span[data-next]"); if (!nextEl) break; const token = nextEl.getAttribute("data-next"); nextEl.remove(); try { const res = await fetch(`/pagination/${encodeURIComponent(token)}`, { credentials: "same-origin", }); if (!res.ok) continue; const html = await res.text(); const frag = parseHTML(html); for (const node of Array.from(frag.childNodes)) { if (node.nodeType === Node.TEXT_NODE && !node.textContent.trim()) continue; this.root.appendChild(node); } } catch {} } this.items = Array.from(this.root.querySelectorAll(":scope > li"));}
// ------- build ------- build() { if (!this.root || this.root.dataset.treeBuilt === "1") return; this.root.dataset.treeBuilt = "1";
// separate menu items from normal posts const allLis = Array.from(this.root.querySelectorAll(":scope > li")); const menuItems = allLis.filter((li) => li.hasAttribute("data-menu")); const postItems = allLis.filter((li) => !li.hasAttribute("data-menu")); // collect hrefs from menu items (normalize to handle trailing slash variations) const normalizeHref = (href) => { if (!href) return ""; // remove trailing slash for comparison (except for root "/") return href === "/" ? "/" : href.replace(/\/$/, ""); }; const getHref = (li) => { const a = li.querySelector(":scope > a"); return a ? (a.getAttribute("href") || a.href) : null; }; const menuHrefs = new Set(); menuItems.forEach((li) => { const href = getHref(li); if (href) menuHrefs.add(normalizeHref(href)); }); // filter out post items that match menu hrefs, removing them from DOM const filteredPostItems = postItems.filter((li) => { const href = getHref(li); if (!href) return true; const shouldKeep = !menuHrefs.has(normalizeHref(href)); if (!shouldKeep) li.remove(); return shouldKeep; }); const byPath = new Map(); byPath.set("", { el: this.root, submenu: this.root }); const segTitle = (s) => { let out = s.replace(/[-_]/g, " "); if (typeof STRIP_TAG_TOKENS !== "undefined" && STRIP_TAG_TOKENS) { out = out.replace(/\[([^\]]+)\]/g, "$1"); // remove surrounding [ ] } return out; }; const ensureFolder = (folderPath) => { if (byPath.has(folderPath)) return byPath.get(folderPath); const seg = folderPath.split("/").filter(Boolean).at(-1) || ""; const li = document.createElement("li"); li.className = "folder has-submenu"; li.dataset.folder = folderPath; li.setAttribute("aria-expanded", "false"); const label = document.createElement("button"); label.type = "button"; label.className = "folder-label"; label.textContent = segTitle(seg); li.appendChild(label); const submenu = document.createElement("ul"); submenu.className = "submenu"; submenu.hidden = true; li.appendChild(submenu); const node = { el: li, submenu }; byPath.set(folderPath, node); return node; }; // build nested tree for post items only filteredPostItems.forEach((li) => { const path = li.getAttribute("data-path") || ""; const parts = path.split("/").filter(Boolean); const folderParts = parts.slice(0, -1); let parentPath = ""; let parentNode = byPath.get(""); for (const seg of folderParts) { const nextPath = parentPath + "/" + seg; const node = ensureFolder(nextPath); if (!node.el.isConnected) parentNode.submenu.appendChild(node.el); parentNode = node; parentPath = nextPath; } parentNode.submenu.appendChild(li); }); this.sortTree(this.root); // append menu items flat, in original order if (menuItems.length) { menuItems[0].classList.add("menu-separator"); menuItems.forEach((li) => this.root.appendChild(li)); }}
Now I can see the issue. The data-date attribute is expected to already be on the <li> elements when they're fetched from Blot's pagination endpoint. The JavaScript doesn't set it - it just reads it.
Let me check where Blot generates those <li> elements. Can you show me the HTML template that renders the sidebar items? It's likely a partial like sidebar.html or similar, or it might be in a entry.html partial. This template would have something like {{#entries}} or {{#allEntries}} and create the <li data-path="..." data-date="..."> elements.
[Pasted HTML content]
<!DOCTYPE html>
<html lang="{{lang}}">
{{> head}}
<body>
{{> navigation}}
<main>
<div class="entry">
{{^entries.length}}
<p>Nothing here.</p>
{{/entries.length}}
{{#entries}}
{{#first}}
{{{html}}}
{{#backlinks.length}}
<br>
<hr>
<h4>Links to this page</h4>
<p>
{{#backlinks}}
<a href="{{{url}}}">{{title}}</a><br>
{{/backlinks}}
</p>
{{/backlinks.length}}
{{/first}}
{{/entries}}
</div>
</main>
{{> footer}}
</body>
</html>
<!DOCTYPE html>
<html lang="{{lang}}">
{{> head}}
<body>
{{> navigation}}
<main>
<div class="entry">
{{{entry.html}}}
{{#entry.backlinks.length}}
<br>
<hr>
<h4>Links to this page</h4>
<p>
{{#entry.backlinks}}
<a href="{{{url}}}">{{title}}</a><br>
{{/entry.backlinks}}
</p>
{{/entry.backlinks.length}}
</div>
</main>
{{> footer}}
</body>
</html>
{{^query.partial}}
<!-- Hidden checkbox to toggle navigation -->
<input type="checkbox" id="nav-toggle" />
<label for="nav-toggle" class="popup-cover"></label>
<input type="checkbox" id="toggle-left" class="toggle-checkbox" />
<div class="control-bar">
<label for="toggle-left" class="control-label">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
class="svg-icon lucide-menu"
>
<line x1="4" y1="12" x2="20" y2="12"></line>
<line x1="4" y1="6" x2="20" y2="6"></line>
<line x1="4" y1="18" x2="20" y2="18"></line></svg>
</label>
<a href="/">{{title}}</a>
</div>
<div class="column-container">
<div class="column">
<div class="navigation">
<a href="/">
<h2>
{{#avatar}}
<img width="16" height="16" src="{{{avatar}}}" class="avatar" title="{{title}}" />
{{/avatar}}
{{^avatar}}
<span style="display: inline-block;width:8px;"></span>
{{/avatar}}
{{title}}
</h2>
</a>
<ul class="sidebar">
{{#menu}}
<li data-menu="true"><a class="{{active}}" href="{{{url}}}">{{label}}</a></li>
{{/menu}}
{{> pagination}}
</ul>
</div>
</div>
{{/query.partial}}
entries.html, entry.html, navigation.html
I see the sidebar uses {{> pagination}} to render the entries. Can you show me the pagination.html partial? That's where the <li> elements with data-path and data-date attributes should be generated.
{{#posts}}
{{title}} {{#pagination.next}} {{/pagination.next}} {{/posts}}
Found it. The data-date="{{created}}" is using the file's creation/modification timestamp. To support the Date: front matter field, we need to:
- Update pagination.html to include the front matter date
- Update the JavaScript to prefer the front matter date over the created timestamp
Here's my proposed approach:
pagination.html - add a second attribute for the front matter date:
html
{{#posts}}
<li data-path="{{{path}}}" data-date="{{created}}" data-frontmatter-date="{{date}}"><a class="{{active}}" href="{{{url}}}">{{title}}</a></li>
{{#pagination.next}}
<span data-next="{{.}}"></span>
{{/pagination.next}}
{{/posts}}
JavaScript - modify the sort comparisons to prefer data-frontmatter-date when it exists:
javascript
const getDateValue = (el) => {
const frontmatterDate = el.getAttribute('data-frontmatter-date');
if (frontmatterDate && frontmatterDate !== '') {
// Parse the frontmatter date and convert to timestamp for comparison
const parsed = new Date(frontmatterDate).getTime();
if (!isNaN(parsed)) return String(parsed);
}
return el.getAttribute('data-date') || '0';
};
Then update both sortTree and buildHomeLists to use this helper.
Does this approach look right? Also - what format is your Date: field in the front matter? (e.g., 2026-01-15, Jan 15, 2026, etc.)
2026-01-20 07:30, 2026-01-20 18:30
That format will parse fine with new Date(). Here are the changes:
pagination.html:
html
{{#posts}}
<li data-path="{{{path}}}" data-date="{{created}}" data-frontmatter-date="{{date}}"><a class="{{active}}" href="{{{url}}}">{{title}}</a></li>
{{#pagination.next}}
<span data-next="{{.}}"></span>
{{/pagination.next}}
{{/posts}}
navigation-js.js - update sortTree:
```javascript
sortTree(ul) {
const children = Array.from(ul.children).filter((n) => n.tagName === "LI");
const folders = children.filter((li) => li.classList.contains("folder"));
const files = children.filter((li) => !li.classList.contains("folder"));
const cmpAlpha = (a, b) => this.labelForLi(a).localeCompare(this.labelForLi(b), undefined, { sensitivity: "base", });
const getDateValue = (el) => { const frontmatterDate = el.getAttribute('data-frontmatter-date'); if (frontmatterDate && frontmatterDate !== '') { const parsed = new Date(frontmatterDate).getTime(); if (!isNaN(parsed)) return String(parsed); } return el.getAttribute('data-date') || '0'; };
// Sort by date descending (newest first) const cmpDate = (a, b) => { const dateA = getDateValue(a); const dateB = getDateValue(b); return dateB.localeCompare(dateA); };
folders.sort(cmpAlpha); files.sort(cmpDate);
[...folders, ...files].forEach((li) => ul.appendChild(li));
folders.forEach((li) => { const sub = li.querySelector(":scope > ul.submenu"); if (sub) this.sortTree(sub); }); } ```
script.js - update buildHomeLists sort function:
```javascript
// Sort by date descending (prefer frontmatter date over created)
const getDateValue = (el) => {
const frontmatterDate = el.getAttribute('data-frontmatter-date');
if (frontmatterDate && frontmatterDate !== '') {
const parsed = new Date(frontmatterDate).getTime();
if (!isNaN(parsed)) return String(parsed);
}
return el.getAttribute('data-date') || '0';
};
const sortByDate = (a, b) => { const dateA = getDateValue(a); const dateB = getDateValue(b); return dateB.localeCompare(dateA); }; ```
Does this look right? If so, do you want me to write these changes to files, or will you apply them manually? (I'd need the file paths to your Blot theme if you want me to edit them directly.)
I've applied them manually but nothing seems to have changed.
A few things to check:
Clear the sidebar cache - The JavaScript caches the sidebar in localStorage. Open browser DevTools (F12), go to Console, and run:
javascript localStorage.clear();Then refresh the page.Check if
{{date}}is outputting anything - Inspect one of the<li>elements in the sidebar with DevTools. Doesdata-frontmatter-datehave a value, or is it empty?
If data-frontmatter-date is empty, then Blot might use a different variable name for the front matter Date field. It could be {{metadata.date}} or something else. Can you check what the rendered HTML shows for that attribute?
Can I paste you the front page html?
Yes, please paste it.
[Pasted HTML content]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> - Anchor</title>
<meta name="blot-cache-id" content="1768911816561">
<meta name="description" content=" - Anchor">
<meta property="og:title" content=" - Anchor">
<meta property="og:description" content=" - Anchor">
<meta property="og:type" content="website">
<meta property="og:url" content="https://a327ex.com/">
<link rel="alternate" type="application/rss+xml" title="Anchor" href="https://anchor.blot.im/feed.rss">
<link rel="stylesheet" href="/style.css?cache=1768911816561&extension=.css">
<script type="module" src="/navigation-js.js"></script>
</head>
<body>
<!-- Hidden checkbox to toggle navigation -->
<input type="checkbox" id="nav-toggle" />
<label for="nav-toggle" class="popup-cover"></label>
<input type="checkbox" id="toggle-left" class="toggle-checkbox" />
<div class="control-bar">
<label for="toggle-left" class="control-label">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
stroke-linejoin="round"
class="svg-icon lucide-menu"
>
<line x1="4" y1="12" x2="20" y2="12"></line>
<line x1="4" y1="6" x2="20" y2="6"></line>
<line x1="4" y1="18" x2="20" y2="18"></line></svg>
</label>
<a href="/">Anchor</a>
</div>
<div class="column-container">
<div class="column">
<div class="navigation">
<a href="/">
<h2>
<span style="display: inline-block;width:8px;"></span>
Anchor
</h2>
</a>
<ul class="sidebar">
<li data-menu="true"><a class="" href="/home">home</a></li>
<li data-menu="true"><a class="" href="/archives">archives</a></li>
<li data-menu="true"><a class="" href="/search">search</a></li>
<li data-menu="true"><a class="" href="/feed.rss">feed</a></li>
<li data-path="/context/ANCHOR_CONTEXT_BRIEF.md" data-date="1767914492618" data-frontmatter-date="January 8, 2026"><a class="" href="/anchor-context-brief">Anchor Context Brief</a></li>
<li data-path="/context/CLAUDE-anchor.md" data-date="1768861982684" data-frontmatter-date="January 19, 2026"><a class="" href="/claude-md-2">CLAUDE.md</a></li>
<li data-path="/context/CLAUDE-emoji-ball-battles.md" data-date="1768875186491" data-frontmatter-date="January 19, 2026"><a class="" href="/claude-md-3">CLAUDE.md</a></li>
<li data-path="/context/ENGINE_API.md" data-date="1768079842794" data-frontmatter-date="January 10, 2026"><a class="" href="/engine-api-reference">Engine API Reference</a></li>
<li data-path="/context/ENGINE_API_QUICK.md" data-date="1768079844692" data-frontmatter-date="January 10, 2026"><a class="" href="/engine-api-quick-reference">Engine API Quick Reference</a></li>
<li data-path="/context/ENGINE_WANTS.md" data-date="1768875186826" data-frontmatter-date="January 19, 2026"><a class="" href="/engine-wants">Engine Wants</a></li>
<li data-path="/context/FRAMEWORK_API.md" data-date="1768695245547" data-frontmatter-date="January 17, 2026"><a class="" href="/framework-api">Framework API</a></li>
<li data-path="/context/FRAMEWORK_API_QUICK.md" data-date="1768695248007" data-frontmatter-date="January 17, 2026"><a class="" href="/framework-api-quick-reference">Framework API Quick Reference</a></li>
<li data-path="/files/@grok explode his balls/1.png" data-date="1768175172835" data-frontmatter-date="January 11, 2026"><a class="" href="/1">1</a></li>
<li data-path="/files/@grok explode his balls/2.png" data-date="1768175173075" data-frontmatter-date="January 11, 2026"><a class="" href="/2">2</a></li>
<li data-path="/files/@grok explode his balls/3.png" data-date="1768175173324" data-frontmatter-date="January 11, 2026"><a class="" href="/3">3</a></li>
<li data-path="/files/Opus 4.5/ANCHOR.md" data-date="1766993522006" data-frontmatter-date="December 29, 2025"><a class="" href="/anchor-engine-specification">Anchor Engine Specification</a></li>
<li data-path="/files/Opus 4.5/CLAUDE.md" data-date="1766993525102" data-frontmatter-date="December 29, 2025"><a class="" href="/claude-md">CLAUDE.md</a></li>
<li data-path="/files/Opus 4.5/IMPLEMENTATION_PLAN.md" data-date="1766993527376" data-frontmatter-date="December 29, 2025"><a class="" href="/anchor-implementation-plan">Anchor Implementation Plan</a></li>
<li data-path="/logs/2026-01-01_07-37.md" data-date="1767264039103" data-frontmatter-date="January 1, 2026"><a class="" href="/omarchy-and-anchor-setup">Omarchy and Anchor Setup</a></li>
<li data-path="/logs/2026-01-01_19-37.md" data-date="1767307351301" data-frontmatter-date="January 1, 2026"><a class="" href="/cachyos-and-neovim-setup">CachyOS and NeoVim Setup</a></li>
<li data-path="/logs/2026-01-02_10-00.md" data-date="1767378979188" data-frontmatter-date="January 2, 2026"><a class="" href="/workflow-updates-and-blog-post">Workflow Updates and Blog Post</a></li>
<li data-path="/logs/2026-01-02_18-59.md" data-date="1767401811459" data-frontmatter-date="January 2, 2026"><a class="" href="/roguelite-design-analysis-2">Roguelite Design Analysis</a></li>
<li data-path="/logs/2026-01-03_12-30.md" data-date="1767458329212" data-frontmatter-date="January 3, 2026"><a class="" href="/emoji-ball-battles-design">Emoji Ball Battles Design</a></li>
<li data-path="/logs/2026-01-03_15-30.md" data-date="1767472457894" data-frontmatter-date="January 3, 2026"><a class="" href="/anchor-interview">Anchor Interview</a></li>
<li data-path="/logs/2026-01-03_18-20.md" data-date="1767475607544" data-frontmatter-date="January 3, 2026"><a class="" href="/log-generator-script-fix">Log Generator Script Fix</a></li>
<li data-path="/logs/2026-01-04_12-15.md" data-date="1767540258019" data-frontmatter-date="January 4, 2026"><a class="" href="/windows-setup">Windows Setup</a></li>
<li data-path="/logs/anchor-phase-1.md" data-date="1767647878953" data-frontmatter-date="January 5, 2026"><a class="" href="/anchor-phase-1">Anchor Phase 1</a></li>
<li data-path="/logs/anchor-phase-10-part-1.md" data-date="1768254080642" data-frontmatter-date="January 12, 2026"><a class="" href="/anchor-phase-10-part-1">Anchor Phase 10 Part 1</a></li>
<li data-path="/logs/anchor-phase-10-part-10.md" data-date="1768608132752" data-frontmatter-date="January 16, 2026"><a class="" href="/anchor-phase-10-part-10">Anchor Phase 10 Part 10</a></li>
<li data-path="/logs/anchor-phase-10-part-11.md" data-date="1768683402002" data-frontmatter-date="January 17, 2026"><a class="" href="/anchor-phase-10-part-11">Anchor Phase 10 Part 11</a></li>
<li data-path="/logs/anchor-phase-10-part-2.md" data-date="1768309916473" data-frontmatter-date="January 13, 2026"><a class="" href="/anchor-phase-10-part-2">Anchor Phase 10 Part 2</a></li>
<li data-path="/logs/anchor-phase-10-part-3.md" data-date="1768337652601" data-frontmatter-date="January 13, 2026"><a class="" href="/anchor-phase-10-part-3">Anchor Phase 10 Part 3</a></li>
<li data-path="/logs/anchor-phase-10-part-4.md" data-date="1768379575808" data-frontmatter-date="January 14, 2026"><a class="" href="/anchor-phase-10-part-4">Anchor Phase 10 Part 4</a></li>
<li data-path="/logs/anchor-phase-10-part-5.md" data-date="1768434374700" data-frontmatter-date="January 14, 2026"><a class="" href="/anchor-phase-10-part-5">Anchor Phase 10 Part 5</a></li>
<li data-path="/logs/anchor-phase-10-part-6.md" data-date="1768444006234" data-frontmatter-date="January 14, 2026"><a class="" href="/anchor-phase-10-part-6">Anchor Phase 10 Part 6</a></li>
<li data-path="/logs/anchor-phase-10-part-7.md" data-date="1768520805313" data-frontmatter-date="January 15, 2026"><a class="" href="/anchor-phase-10-part-7">Anchor Phase 10 Part 7</a></li>
<li data-path="/logs/anchor-phase-10-part-8.md" data-date="1768529066608" data-frontmatter-date="January 15, 2026"><a class="" href="/anchor-phase-10-part-8">Anchor Phase 10 Part 8</a></li>
<li data-path="/logs/anchor-phase-10-part-9.md" data-date="1768586074944" data-frontmatter-date="January 16, 2026"><a class="" href="/anchor-phase-10-part-9">Anchor Phase 10 Part 9</a></li>
<li data-path="/logs/anchor-phase-2.md" data-date="1767717377503" data-frontmatter-date="January 6, 2026"><a class="" href="/anchor-phase-2">Anchor Phase 2</a></li>
<li data-path="/logs/anchor-phase-3-part-1.md" data-date="1767731194557" data-frontmatter-date="January 6, 2026"><a class="" href="/anchor-phase-3-part-1">Anchor Phase 3 Part 1</a></li>
<li data-path="/logs/anchor-phase-3-part-2.md" data-date="1767749326014" data-frontmatter-date="January 6, 2026"><a class="" href="/anchor-phase-3-part-2">Anchor Phase 3 Part 2</a></li>
<li data-path="/logs/anchor-phase-3-part-3.md" data-date="1767785993406" data-frontmatter-date="January 7, 2026"><a class="" href="/anchor-phase-3-part-3">Anchor Phase 3 Part 3</a></li>
<li data-path="/logs/anchor-phase-3-part-4.md" data-date="1767830109252" data-frontmatter-date="January 7, 2026"><a class="" href="/anchor-phase-3-part-4">Anchor Phase 3 Part 4</a></li>
<li data-path="/logs/anchor-phase-4.md" data-date="1767882190662" data-frontmatter-date="January 8, 2026"><a class="" href="/anchor-phase-4">Anchor Phase 4</a></li>
<li data-path="/logs/anchor-phase-5.md" data-date="1767914502990" data-frontmatter-date="January 8, 2026"><a class="" href="/anchor-phase-5">Anchor Phase 5</a></li>
<li data-path="/logs/anchor-phase-6.md" data-date="1767950011375" data-frontmatter-date="January 9, 2026"><a class="" href="/anchor-phase-6">Anchor Phase 6</a></li>
<li data-path="/logs/anchor-phase-7.md" data-date="1767964843916" data-frontmatter-date="January 9, 2026"><a class="" href="/anchor-phase-7">Anchor Phase 7</a></li>
<li data-path="/logs/anchor-phase-8.md" data-date="1768001737835" data-frontmatter-date="January 9, 2026"><a class="" href="/anchor-phase-8">Anchor Phase 8</a></li>
<li data-path="/logs/anchor-phase-9.md" data-date="1768013594167" data-frontmatter-date="January 9, 2026"><a class="" href="/anchor-phase-9">Anchor Phase 9</a></li>
<li data-path="/logs/anchor-review-1.md" data-date="1767863179468" data-frontmatter-date="January 8, 2026"><a class="" href="/anchor-review-1">Anchor Review 1</a></li>
<li data-path="/logs/anchor-review-2.md" data-date="1768079848342" data-frontmatter-date="January 10, 2026"><a class="" href="/anchor-review-2">Anchor Review 2</a></li>
<li data-path="/logs/anchor-review-3.md" data-date="1768695255424" data-frontmatter-date="January 17, 2026"><a class="" href="/anchor-review-3">Anchor Review 3</a></li>
<li data-path="/logs/anchor-runtime-configuration.md" data-date="1768904385462" data-frontmatter-date="January 20, 2026"><a class="" href="/anchor-runtime-configuration">Anchor Runtime Configuration</a></li>
<li data-path="/logs/audible-book-recommendations-2.md" data-date="1768128357416" data-frontmatter-date="January 11, 2026"><a class="" href="/audible-book-recommendations-2">Audible Book Recommendations 2</a></li>
<li data-path="/logs/audible-book-recommendations.md" data-date="1767583818712" data-frontmatter-date="January 5, 2026"><a class="" href="/audible-book-recommendations">Audible Book Recommendations</a></li>
<li data-path="/logs/ebb-implementation-plan.md" data-date="1768907118158" data-frontmatter-date="January 19, 2026"><a class="" href="/ebb-implementation-plan">EBB Implementation Plan</a></li>
<li data-path="/logs/ebb-temporary-end-design.md" data-date="1768907125570" data-frontmatter-date="January 19, 2026"><a class="" href="/ebb-temporary-end-design">EBB Temporary End Design</a></li>
<li data-path="/logs/steam-game-recommendations.md" data-date="1768084543982" data-frontmatter-date="January 10, 2026"><a class="" href="/steam-game-recommendations">Steam Game Recommendations</a></li>
<li data-path="/notes/@grok explode his balls.txt" data-date="1768175173578" data-frontmatter-date="January 11, 2026"><a class="" href="/grok-explode-his-balls">@grok explode his balls</a></li>
<li data-path="/notes/Anchor Engine Overview.md" data-date="1768695259601" data-frontmatter-date="January 17, 2026"><a class="" href="/anchor-engine-overview">Anchor Engine Overview</a></li>
<li data-path="/notes/Claude Code Silently Deletes Local Sessions After 30 Days.md" data-date="1768373349226" data-frontmatter-date="January 14, 2026"><a class="" href="/claude-code-silently-deletes-local-sessions-after-30-days">Claude Code Silently Deletes Local Sessions After 30 Days</a></li>
<li data-path="/notes/Opus 4.5.txt" data-date="1766974414428" data-frontmatter-date="December 28, 2025"><a class="" href="/opus-4-5">Opus 4.5</a></li>
<li data-path="/notes/Roguelite Design Analysis.md" data-date="1767397557409" data-frontmatter-date="January 2, 2026"><a class="" href="/roguelite-design-analysis">Roguelite Design Analysis</a></li>
<li data-path="/notes/Verifiers and Readers.txt" data-date="1767367206752" data-frontmatter-date="January 2, 2026"><a class="" href="/verifiers-and-readers">Verifiers and Readers</a></li>
<li data-path="/notes/Writing and Gamedev.txt" data-date="1768786880771" data-frontmatter-date="January 18, 2026"><a class="" href="/writing-and-gamedev">Writing and Gamedev</a></li>
</ul>
</div>
</div>
<main>
<div class="entry">
<p>This site serves as a devlog for my work during 2026. In 2025, I worked on <a href="https://a327ex.com/posts/it_follows">It Follows</a> to hone my skills as a writer. In 2026, I’m working on games by using the new <span class="small-caps">AI</span> models (i.e. Opus 4.5) to help with my engine’s rewrite and with designing and coding the games themselves.</p>
<p>To the left are sections that contain various documents:</p>
<ul>
<li>context: context documents used in every <span class="small-caps">AI</span> interaction so it codes like me</li>
<li>files: files for posts in the logs or notes section</li>
<li>logs: daily logs of my interactions with <span class="small-caps">AI</span> (auto-generated)</li>
<li>notes: notes about various topics, dev related or not</li>
</ul>
<p>This website currently tracks two projects:</p>
<ul>
<li><a href="https://github.com/a327ex/Anchor">Anchor</a><br>
</li>
<li><a href="https://github.com/a327ex/emoji-ball-battles">Emoji Ball Battles (<span class="small-caps">EBB</span>)</a></li>
</ul>
<p>The engine repository is always public. Any game repositories are private until the game is released.</p>
<h5 id="section"></h5>
<div id="notes-list" class="archives-grid">
</div>
<div id="logs-list" class="archives-grid">
</div>
<h3 id="section-1"></h3>
<p><a href="https://a327ex.com">a327ex</a> · <a href="https://github.com/a327ex">GitHub</a> · <a href="https://store.steampowered.com/developer/a327ex">Steam</a> · <a href="https://x.com/a327ex">X</a></p>
</div>
</main>
</div>
</div>
<script src="/script.js?cache=1768911816561&extension=.js" type="text/javascript"></script>
</body>
</html>
I see the problem. The data-frontmatter-date is being formatted as human-readable text like "January 8, 2026" instead of preserving the time component. So two posts from the same day with different times (like 2026-01-20 07:30 and 2026-01-20 18:30) both become "January 20, 2026" and sort identically.
Looking at your archives.html, Blot has a formatDate helper. Try updating pagination.html to output a sortable format with time:
{{#posts}}
<li data-path="{{{path}}}" data-date="{{created}}" data-frontmatter-date="{{#formatDate}}YYYY-MM-DD HH:mm{{/formatDate}}"><a class="{{active}}" href="{{{url}}}">{{title}}</a></li>
{{#pagination.next}}
<span data-next="{{.}}"></span>
{{/pagination.next}}
{{/posts}}
This should output "2026-01-20 07:30" format which preserves the time and sorts correctly.
After making the change, clear localStorage again:
javascript
localStorage.clear();
But both EBB posts had dates at 2026-01-19, and they're rendering at their last edited date instead (01-20)
Ah, so the data-frontmatter-date="January 19, 2026" is correct in the HTML, but the list is still sorting by data-date (the file modification timestamp from 01-20)?
That means the JavaScript change isn't taking effect. A few things to check:
Hard refresh - Ctrl+Shift+R (or Cmd+Shift+R on Mac) to bypass browser cache
Check for JS errors - Open DevTools (F12) → Console tab. Any errors?
Verify the JS is updated - In DevTools → Sources tab, find your script.js and navigation-js.js files and check if the
getDateValuefunction is actually thereBlot's CDN cache - Blot might be caching your JS files. Try adding a query param to bust the cache, or wait a few minutes and try again
Can you open the browser console and run this to test if the parsing works?
javascript
document.querySelectorAll('li[data-frontmatter-date]').forEach(li => {
const fm = li.getAttribute('data-frontmatter-date');
const parsed = new Date(fm).getTime();
console.log(fm, '->', parsed, isNaN(parsed) ? 'FAILED' : 'OK');
});
document.querySelectorAll('li[data-frontmatter-date]').forEach(li => { const fm = li.getAttribute('data-frontmatter-date'); const parsed = new Date(fm).getTime(); console.log(fm, '->', parsed, isNaN(parsed) ? 'FAILED' : 'OK'); }); 3VM620:4 January 19, 2026 -> 1768791600000 OK 2VM620:4 January 17, 2026 -> 1768618800000 OK 2VM620:4 January 10, 2026 -> 1768014000000 OK VM620:4 January 8, 2026 -> 1767841200000 OK 3VM620:4 January 11, 2026 -> 1768100400000 OK 3VM620:4 December 29, 2025 -> 1766977200000 OK 2VM620:4 January 19, 2026 -> 1768791600000 OK VM620:4 January 20, 2026 -> 1768878000000 OK 2VM620:4 January 17, 2026 -> 1768618800000 OK 2VM620:4 January 16, 2026 -> 1768532400000 OK 2VM620:4 January 15, 2026 -> 1768446000000 OK 3VM620:4 January 14, 2026 -> 1768359600000 OK 2VM620:4 January 13, 2026 -> 1768273200000 OK VM620:4 January 12, 2026 -> 1768186800000 OK VM620:4 January 11, 2026 -> 1768100400000 OK 2VM620:4 January 10, 2026 -> 1768014000000 OK 4VM620:4 January 9, 2026 -> 1767927600000 OK 3VM620:4 January 8, 2026 -> 1767841200000 OK 2VM620:4 January 7, 2026 -> 1767754800000 OK 3VM620:4 January 6, 2026 -> 1767668400000 OK 2VM620:4 January 5, 2026 -> 1767582000000 OK VM620:4 January 4, 2026 -> 1767495600000 OK 3VM620:4 January 3, 2026 -> 1767409200000 OK 2VM620:4 January 2, 2026 -> 1767322800000 OK 2VM620:4 January 1, 2026 -> 1767236400000 OK VM620:4 January 18, 2026 -> 1768705200000 OK VM620:4 January 17, 2026 -> 1768618800000 OK VM620:4 January 14, 2026 -> 1768359600000 OK VM620:4 January 11, 2026 -> 1768100400000 OK 2VM620:4 January 2, 2026 -> 1767322800000 OK VM620:4 December 28, 2025 -> 1766890800000 OK undefined
The date parsing works fine. Two issues:
Time is being lost - Notice "January 19, 2026" appears twice with the same timestamp (1768791600000). Without the time component, posts from the same day sort identically. Did you update pagination.html to use
{{#formatDate}}YYYY-MM-DD HH:mm{{/formatDate}}?JS might not be deployed - Can you check if your JavaScript changes are actually live? In DevTools → Sources tab (or Network tab), find
script.jsand search forgetDateValue. Is that function there?
Also, you can test the sorting logic directly in the console:
javascript
const items = Array.from(document.querySelectorAll('.sidebar li[data-frontmatter-date]'));
items.slice(0, 5).forEach(li => {
const fm = li.getAttribute('data-frontmatter-date');
const created = li.getAttribute('data-date');
const title = li.querySelector('a')?.textContent;
console.log(title, '| frontmatter:', fm, '| created:', new Date(parseInt(created)).toISOString().slice(0,10));
});
This will show you both dates for the first 5 items so you can see which one is being used for sorting.
I'll change pagination.html, but the new JS functions are present in the sources. Let me update pagination.html first.
Sounds good. After updating pagination.html, remember to clear localStorage again:
localStorage.clear();
Then hard refresh (Ctrl+Shift+R).
Changing pagination.html like that makes no posts show at all on the website anymore.
The {{#formatDate}} helper might not work in that context. Let's revert and try a different approach.
First, revert pagination.html back to:
html
{{#posts}}
<li data-path="{{{path}}}" data-date="{{created}}" data-frontmatter-date="{{date}}"><a class="{{active}}" href="{{{url}}}">{{title}}</a></li>
{{#pagination.next}}
<span data-next="{{.}}"></span>
{{/pagination.next}}
{{/posts}}
Then try {{dateStamp}} instead of {{date}} - Blot might have a raw timestamp version:
html
{{#posts}}
<li data-path="{{{path}}}" data-date="{{created}}" data-frontmatter-date="{{dateStamp}}"><a class="{{active}}" href="{{{url}}}">{{title}}</a></li>
{{#pagination.next}}
<span data-next="{{.}}"></span>
{{/pagination.next}}
{{/posts}}
If that doesn't work either, can you check what variables Blot exposes? Their docs might list available entry properties, or you could try outputting {{.}} or checking the Blot template documentation.
Can you try googling for it?
It's an open source project I believe.
WebSearch (Blot blog github template variables entry date timestamp)
Web search results for query: "Blot blog github template variables entry date timestamp"
Links: [{"title":"blot/TODO at master · davidmerfield/blot","url":"https://github.com/davidmerfield/blot/blob/master/TODO"},{"title":"blot/app/templates at master · davidmerfield/blot","url":"https://github.com/davidmerfield/Blot/tree/master/app/templates"},{"title":"Questions - Blot","url":"https://blot.im/questions"},{"title":"GitHub - fmeus/Blot-themes: Blot themes","url":"https://github.com/fmeus/Blot-themes"},{"title":"GitHub - davidmerfield/blot: Turns a folder into a website","url":"https://github.com/davidmerfield/Blot"},{"title":"Reference - Blot","url":"https://blot.im/templates/developers/reference"},{"title":"How to add timestamp in GitHub action as a global variable for use by all jobs? · community · Discussion #88772","url":"https://github.com/orgs/community/discussions/88772"},{"title":"GitHub - chadgmoore/whereTheLightGathers-blot: A theme for the Blot blogging platform","url":"https://github.com/chadgmoore/whereTheLightGathers-blot"},{"title":"azure-docs/articles/azure-resource-manager/templates/template-functions-date.md at main · MicrosoftDocs/azure-docs","url":"https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/azure-resource-manager/templates/template-functions-date.md"},{"title":"Blot.Im Tutorials - Neural Market Trends","url":"https://neuralmarkettrends.com/blot-tutorials/"}]
Based on my search, I found detailed information about Blot blog template variables related to entry dates and timestamps.
## Blot Blog Template Variables for Entry Date/Timestamp
According to the official Blot documentation, here are the relevant template variables:
### Timestamp Variables
The blog post's publication date is available as a unix timestamp.(no content)The file's creation date is available as a Unix timestamp.(no content)The file's last-modified date is also available as a Unix timestamp.
### Date Formatting Functions
Blot provides several formatting functions to display dates:
`{{#formatUpdated}}` is used to format the entry's publication date as you like, using any of the supported date tokens. It can also be used to format the entry's updated date.
Example usage:
```handlebars
{{#formatUpdated}} MMMM DDDo, YYYY {{/formatUpdated}}
```
`{{#formatCreated}}` is used to format the entry's created date as you like, using any of the supported date tokens.
Example usage:
```handlebars
{{#formatCreated}} MMMM DDDo, YYYY {{/formatCreated}}
... [14 more lines]
WebFetch (https://blot.im/templates/developers/reference)
Request failed with status code 403
WebFetch (https://github.com/davidmerfield/Blot/blob/master/app/templates)
# Finding Template Variable Documentation
Based on the GitHub repository content provided, I can see this is the Blot blogging platform's template system. However, **the specific documentation showing available entry/post variables (like `dateStamp`, `created`, `updated`, `timestamp`) is not present in the displayed page content.**
## What's Available
The readme indicates that:
- Templates use Mustache syntax to render variables
- Views contain "variables from your blog"
- The system fetches information based on route and template tags
- Configuration files can declare template-specific locals
## Where to Look
To find the actual variable definitions, you would need to:
1. **Check the source files** in `/app/templates/source/` directory (referenced but not shown)
2. **Review model definitions** in `/app/models/` (visible in directory tree)
3. **Examine the view rendering logic** mentioned as needing refactoring in the notes
4. **Consult template examples** in the `/folders/` subdirectory
The readme notes that the project is undergoing refactoring to improve how variables are exposed to templates, suggesting this documentation may be evolving. You'd benefit from exploring the actual template files and the data-fetching code directly.
WebSearch (site:github.com/davidmerfield/blot entry dateStamp timestamp model)
Web search results for query: "site:github.com/davidmerfield/blot entry dateStamp timestamp model"
No links found.
The search didn't return any results for that specific query. This could be because:
1. The `site:` operator with such a specific repository path may be too restrictive
2. The specific combination of terms (entry, dateStamp, timestamp, model) may not appear together in indexed pages from that repository
Would you like me to try a broader search? For example, I could:
- Search without the `site:` restriction: `davidmerfield blot entry dateStamp timestamp`
- Search for just the repository and one or two key terms: `davidmerfield blot dateStamp model`
- Search GitHub directly for the repository content
Let me know how you'd like to proceed.
REMINDER: You MUST include the sources above in your response to the user using markdown hyperlinks.
WebFetch (https://github.com/davidmerfield/Blot/blob/master/app/models/entry/build/index.js)
Request failed with status code 404
WebFetch (https://raw.githubusercontent.com/davidmerfield/Blot/master/app/build/prepare/dateStamp.js)
Request failed with status code 404
WebSearch (Blot.im template dateStamp entry variables documentation)
Web search results for query: "Blot.im template dateStamp entry variables documentation"
Links: [{"title":"Templates - Blot","url":"https://blot.im/templates"},{"title":"Blot Variables","url":"https://ryanmack.me/blot-variables"},{"title":"Documentation - Blot","url":"https://blot.im/how"},{"title":"Reference - Blot","url":"https://blot.im/templates/developers/reference"},{"title":"blot/app/templates at master · davidmerfield/blot","url":"https://github.com/davidmerfield/Blot/tree/master/app/templates"},{"title":"Blot.Im Tutorials - Neural Market Trends","url":"https://neuralmarkettrends.com/blot-tutorials/"},{"title":"Get started - Blot","url":"https://blot.im/templates/developers/get-started"},{"title":"Metadata - Blot","url":"https://blot.im/how/metadata"},{"title":"Mustache - Blot","url":"https://blot.im/developers/tutorials/mustache"},{"title":"Tutorials - Blot","url":"https://blot.im/templates/developers/tutorials"}]
Based on the search results, here's what I found about Blot.im template entry variables:
## Blot.im Template Variables Documentation
Not all of the variables are in the documentation quite yet, but there are several resources available:
### Official Reference
The official Blot developer reference at [blot.im/templates/developers/reference](https://blot.im/templates/developers/reference) documents entry variables including:
- The file's creation date, as a Unix timestamp
- The file's last-modified date, as a Unix timestamp
- The text of the first paragraph of the entry
- The index of the entry in the list of all of your entries. For example, the first entry published has an `{{index}}` of 1 and the 10th entry published has an `{{index}}` of 10.
### Date-Related Variables
From the unofficial variables list, date-related variables include `dateFormat`, `dateDisplay`, `timeZone`, and `hideDates`.
### Entry Variables
Entry variables include: `id`, `url`, `permalink`, `title`, `titleTag` (the HTML tag containing the title text), `body` (html excluding the HTML tag for its title), `summary` (plain text summary of article), and `teaser` (stuff before read more links).
### Debugging Tip
You can retrieve the object used to render your site's template if you append the query string `?debug=true` to any page on your site. This would show you all available variables including `dateStamp` if it exists.
The search results don't specifically document a `dateStamp` variable, but using the `?debug=true` parameter on your Blot site would reveal all available entry variables in the template context.
... [1 more lines]
WebFetch (https://ryanmack.me/blot-variables)
Request failed with status code 402
Good news - you can see all available variables by adding ?debug=true to any page on your site. Try visiting:
https://a327ex.com/logs/ebb-implementation-plan?debug=true
This will show you the complete object used to render the template, including all date-related properties. Look for anything like dateStamp, timestamp, date, created, updated, etc.
Once you find the raw timestamp variable name, we can use that in pagination.html.
Sources: - Blot Templates Documentation - Blot Developer Reference
It's a huge file. Can you see it here?
{ "partials": { "pluginHTML": "", "title": "{{{entry.title}}} - {{{title}}}", "description": "{{{entry.title}}} - {{{title}}}", "head": "{{^query.partial}}\n\u003Chead\u003E\n \u003Cmeta charset=\"utf-8\"\u003E\n \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\u003E\n \u003Ctitle\u003E{{\u003E title}}\u003C/title\u003E\n\t\t\u003Cmeta name=\"blot-cache-id\" content=\"{{cacheID}}\"\u003E\n\t\t\u003Cmeta name=\"description\" content=\"{{\u003E description}}\"\u003E\n \u003Cmeta property=\"og:title\" content=\"{{\u003E title}}\"\u003E\n \u003Cmeta property=\"og:description\" content=\"{{\u003E description}}\"\u003E\n \u003Cmeta property=\"og:type\" content=\"website\"\u003E\n {{#entry}}\n {{#thumbnail.large}}\n \u003Cmeta property=\"og:image\" content=\"{{{url}}}\"\u003E\n \u003Cmeta name=\"twitter:card\" content=\"summary_large_image\"\u003E\n {{/thumbnail.large}}\n \u003Cmeta property=\"og:url\" content=\"{{{absoluteURL}}}\"\u003E\n {{/entry}}\n \u003Clink rel=\"alternate\" type=\"application/rss+xml\" title=\"{{title}}\" href=\"{{{blogURL}}}{{{feedURL}}}\"\u003E\n {{#avatar}}\u003Clink rel=\"icon\" href=\"{{{avatar}}}\" type=\"image/x-icon\"\u003E{{/avatar}}\n \u003Clink rel=\"stylesheet\" href=\"{{{cssURL}}}\"\u003E\n \u003Cscript type=\"module\" src=\"/navigation-js.js\"\u003E\u003C/script\u003E\n\u003C/head\u003E\n{{/query.partial}}\n{{#query.partial}}\n\u003Chead\u003E\n \u003Ctitle\u003E{{\u003E title}}\u003C/title\u003E\n\u003C/head\u003E \n{{/query.partial}}", "navigation": "{{^query.partial}}\n\n\u003C!-- Hidden checkbox to toggle navigation --\u003E\n\u003Cinput type=\"checkbox\" id=\"nav-toggle\" /\u003E\n\n\u003Clabel for=\"nav-toggle\" class=\"popup-cover\"\u003E\u003C/label\u003E\n\n\u003Cinput type=\"checkbox\" id=\"toggle-left\" class=\"toggle-checkbox\" /\u003E\n\n\u003Cdiv class=\"control-bar\"\u003E\n \u003Clabel for=\"toggle-left\" class=\"control-label\"\u003E\n \u003Csvg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"svg-icon lucide-menu\"\n \u003E\n \u003Cline x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"\u003E\u003C/line\u003E\n \u003Cline x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"\u003E\u003C/line\u003E\n \u003Cline x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"\u003E\u003C/line\u003E\u003C/svg\u003E\n \u003C/label\u003E\n \u003Ca href=\"/\"\u003E{{title}}\u003C/a\u003E\n\u003C/div\u003E\n\n\u003Cdiv class=\"column-container\"\u003E\n \u003Cdiv class=\"column\"\u003E\n \u003Cdiv class=\"navigation\"\u003E\n \u003Ca href=\"/\"\u003E\n \u003Ch2\u003E\n {{#avatar}}\n \u003Cimg width=\"16\" height=\"16\" src=\"{{{avatar}}}\" class=\"avatar\" title=\"{{title}}\" /\u003E\n {{/avatar}}\n {{^avatar}}\n \u003Cspan style=\"display: inline-block;width:8px;\"\u003E\u003C/span\u003E\n {{/avatar}}\n \n {{title}}\n \u003C/h2\u003E\n \u003C/a\u003E\n \u003Cul class=\"sidebar\"\u003E\n {{#menu}}\n \u003Cli data-menu=\"true\"\u003E\u003Ca class=\"{{active}}\" href=\"{{{url}}}\"\u003E{{label}}\u003C/a\u003E\u003C/li\u003E\n {{/menu}} \n {{\u003E pagination}}\n \u003C/ul\u003E\n \u003C/div\u003E\n \u003C/div\u003E\n{{/query.partial}}\n\n", "pagination": "{{#posts}}\n\u003Cli data-path=\"{{{path}}}\" data-date=\"{{created}}\" data-frontmatter-date=\"{{date}}\"\u003E\u003Ca class=\"{{active}}\" href=\"{{{url}}}\"\u003E{{title}}\u003C/a\u003E\u003C/li\u003E\n{{#pagination.next}}\n\u003Cspan data-next=\"{{.}}\"\u003E\u003C/span\u003E\n{{/pagination.next}}\n{{/posts}}\n", "footer": "{{^query.partial}}\r\n\u003C/div\u003E\r\n\u003C/div\u003E\r\n\u003Cscript src=\"{{{scriptURL}}}\" type=\"text/javascript\"\u003E\u003C/script\u003E\r\n{{/query.partial}}" }, "entry": { "html": "\n\u003Ch1 id=\"ebb-temporary-end-design\"\u003E\u003Cspan class=\"small-caps\"\u003EEBB\u003C/span\u003E Temporary End Design\u003C/h1\u003E\n\u003Ch2 id=\"summary\"\u003ESummary\u003C/h2\u003E\n\u003Cp\u003EThis session focused on pre-implementation design work for Emoji Ball Battles, applying the\u003Cspan class=\"push-double\"\u003E\u003C/span\u003E \u003Cspan class=\"pull-double\"\u003E“\u003C/span\u003Ebeginning, end, constraint” framework from the user’s recent blog post to evaluate and strengthen the game design before coding begins.\u003C/p\u003E\n\u003Cp\u003E\u003Cstrong\u003EApplying the Beginning/End/Constraint Framework:\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EThe user’s blog post explains that creative work needs three elements: a beginning, an end, and a constraint\u003C/li\u003E\n\u003Cli\u003EApplied this framework to Emoji Ball Battles: the constraint (meta loop/draft system) was already strong, but the\u003Cspan class=\"push-double\"\u003E\u003C/span\u003E \u003Cspan class=\"pull-double\"\u003E“\u003C/span\u003Eend” (what a broken late-game build feels like) was weak\u003C/li\u003E\n\u003Cli\u003EThis identified the key design gap: no articulated vision of what powerful builds should feel like\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u003Cstrong\u003EEarclacks Research and Attribute Analysis:\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EResearched the Earclacks Weapon Ball roster by searching wikis and documenting 19 weapon balls and 11 block breaker balls\u003C/li\u003E\n\u003Cli\u003ECreated \u003Ccode\u003Ereference/earclacks-attribute-analysis.md\u003C/code\u003E with comprehensive breakdown of scaling triggers (on hit, on parry, on wall collision, timed intervals), scaling targets (damage, speed, rotation, weapon size/length), and scaling patterns (linear, percentage, multiplicative, Fibonacci)\u003C/li\u003E\n\u003Cli\u003EExtrapolated additional mechanics not in Earclacks: on damage taken, on kill, HP/armor/knockback scaling, status effects (slow, root, freeze)\u003C/li\u003E\n\u003Cli\u003EIdentified 16 power fantasy archetypes that define\u003Cspan class=\"push-double\"\u003E\u003C/span\u003E \u003Cspan class=\"pull-double\"\u003E“\u003C/span\u003Ebroken” end-game builds (Blender, Artillery, Wall, Berserker, etc.)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u003Cstrong\u003EDefense System Design:\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EUser specified no % damage reduction — only flat armor\u003C/li\u003E\n\u003Cli\u003EAdded shield system inspired by Path of Exile/Nova Drift: regenerating shield with regen rate, regen cooldown, on shield break/full triggers\u003C/li\u003E\n\u003Cli\u003EResource systems: \u003Cspan class=\"small-caps\"\u003EHP\u003C/span\u003E, armor, shield (class-dependent), charge stacks (ability-dependent), heat, combo meter — no mana, abilities use cooldowns/triggers\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u003Cstrong\u003E108 Emoji Item System Design:\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003ECreated \u003Ccode\u003Ereference/emoji-ball-battles-items.md\u003C/code\u003E with 18 ball classes (face emojis), 18 weapons, 72 passive items\u003C/li\u003E\n\u003Cli\u003EBall classes have strong Earclacks-style identities — one core scaling mechanic each (e.g., \u003Cspan class=\"small-caps\"\u003E😈\u003C/span\u003E Imp increases rotation speed per hit, \u003Cspan class=\"small-caps\"\u003E😡\u003C/span\u003E Rage increases damage based on missing \u003Cspan class=\"small-caps\"\u003EHP\u003C/span\u003E)\u003C/li\u003E\n\u003Cli\u003EUser requested all non-face emojis be changed to faces: \u003Cspan class=\"small-caps\"\u003E🔥→😤\u003C/span\u003E, \u003Cspan class=\"small-caps\"\u003E💀→🤯\u003C/span\u003E, \u003Cspan class=\"small-caps\"\u003E🎲→🤡\u003C/span\u003E, \u003Cspan class=\"small-caps\"\u003E🎭→🥸\u003C/span\u003E, etc.\u003C/li\u003E\n\u003Cli\u003EResolved duplicate emoji conflicts by picking alternative emojis for items vs weapons\u003C/li\u003E\n\u003Cli\u003EPreserved Super Auto Pets design language where sensible (🍎=damage, 🧄=armor, 🍯=summon on death)\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u003Cstrong\u003EImplementation Scope Clarified:\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EBeginning (now): Physics battle — two balls in walled arena with class + weapon + items, fight until one \u003Cspan class=\"small-caps\"\u003EHP\u003C/span\u003E hits 0\u003C/li\u003E\n\u003Cli\u003EEnd (soon after): Test\u003Cspan class=\"push-double\"\u003E\u003C/span\u003E \u003Cspan class=\"pull-double\"\u003E“\u003C/span\u003Ebroken” builds to validate power fantasies\u003C/li\u003E\n\u003Cli\u003EMiddle (later): Draft, evolution, meta loop, enemy generation\u003C/li\u003E\n\u003Cli\u003EArena: just walls, fixed size\u003C/li\u003E\n\u003Cli\u003EBall draft: 3-4 stages with one pick each, then 1-2 accompany you on the run\u003C/li\u003E\n\u003C/ul\u003E\n\u003Cp\u003E\u003Cstrong\u003ERepository Organization:\u003C/strong\u003E\u003C/p\u003E\n\u003Cul\u003E\n\u003Cli\u003EOrganized Anchor’s \u003Ccode\u003Ereference/\u00
The file is truncated. Can you search in that JSON for dateStamp or scroll to find the date-related properties on the entry object? Look for fields like:
- dateStamp
- date
- created
- updated
- timestamp
Or in your browser's DevTools, you can paste this in the console after loading that debug page:
fetch(window.location.href).then(r => r.json()).then(data => {
const entry = data.entry;
console.log('Date fields:', {
date: entry.date,
dateStamp: entry.dateStamp,
created: entry.created,
updated: entry.updated,
timestamp: entry.timestamp
});
});
"pagination": "{{#posts}}\n\u003Cli data-path=\"{{{path}}}\" data-date=\"{{created}}\" data-frontmatter-date=\"{{date}}\"\u003E\u003Ca class=\"{{active}}\" href=\"{{{url}}}\"\u003E{{title}}\u003C/a\u003E\u003C/li\u003E\n{{#pagination.next}}\n\u003Cspan data-next=\"{{.}}\"\u003E\u003C/span\u003E\n{{/pagination.next}}\n{{/posts}}\n",
That's the template definition, not the entry data. I need to see the entry object's properties. Can you search the JSON for "dateStamp" or find where the entry object lists its fields?
Or try this in the console:
fetch(window.location.href).then(r => r.json()).then(data => console.log(Object.keys(data.entry).filter(k => k.toLowerCase().includes('date') || k.toLowerCase().includes('stamp') || k.toLowerCase().includes('created') || k.toLowerCase().includes('updated'))));
This will list just the date-related property names from the entry object.
fetch(window.location.href).then(r => r.json()).then(data => { const entry = data.entry; console.log('Date fields:', { date: entry.date, dateStamp: entry.dateStamp, created: entry.created, updated: entry.updated, timestamp: entry.timestamp }); }); Promise {
} VM916:3 Date fields: {date: 'January 19, 2026', dateStamp: 1768820400000, created: 1768907125570, updated: 1768910530966, timestamp: undefined}
dateStamp is exactly what we need - it's the raw timestamp (1768820400000) of the front matter Date field.
Update pagination.html to:
{{#posts}}
<li data-path="{{{path}}}" data-date="{{created}}" data-frontmatter-date="{{dateStamp}}"><a class="{{active}}" href="{{{url}}}">{{title}}</a></li>
{{#pagination.next}}
<span data-next="{{.}}"></span>
{{/pagination.next}}
{{/posts}}
Then clear localStorage and hard refresh:
localStorage.clear();
The JavaScript getDateValue function will still work - it checks if the value is non-empty and parseable. Since dateStamp is already a timestamp, new Date(1768820400000).getTime() will work fine (it'll just return the same number).
There's another issue now when loading the website: navigation-js.js:203 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'localeCompare') at cmpDate (navigation-js.js:203:18) at Array.sort (
) at SidebarNavigation.sortTree (navigation-js.js:207:9) at SidebarNavigation.build (navigation-js.js:162:10) at SidebarNavigation.init (navigation-js.js:276:10) at async start (navigation-js.js:297:5)
The issue is that getAttribute returns a string, and new Date("1768820400000") (string) doesn't parse as a timestamp - JavaScript tries to interpret it as a date string format and fails.
Update the getDateValue function in navigation-js.js to handle numeric timestamp strings:
const getDateValue = (el) => {
const frontmatterDate = el.getAttribute('data-frontmatter-date');
if (frontmatterDate && frontmatterDate !== '') {
// If it's a numeric timestamp string, use it directly
if (/^\d+$/.test(frontmatterDate)) {
return frontmatterDate;
}
// Otherwise try parsing as a date string
const parsed = new Date(frontmatterDate).getTime();
if (!isNaN(parsed)) return String(parsed);
}
return el.getAttribute('data-date') || '0';
};
And the same fix in script.js for buildHomeLists:
const getDateValue = (el) => {
const frontmatterDate = el.getAttribute('data-frontmatter-date');
if (frontmatterDate && frontmatterDate !== '') {
if (/^\d+$/.test(frontmatterDate)) {
return frontmatterDate;
}
const parsed = new Date(frontmatterDate).getTime();
if (!isNaN(parsed)) return String(parsed);
}
return el.getAttribute('data-date') || '0';
};
Now the site is back to working but the dates are still wrong.
Let's debug. Run this in the console to see what date values are being used:
document.querySelectorAll('.sidebar li[data-path*="/logs/"]').forEach(li => {
const title = li.querySelector('a')?.textContent;
const fm = li.getAttribute('data-frontmatter-date');
const created = li.getAttribute('data-date');
console.log(title, '| frontmatter:', fm, '| created:', created);
});
This will show if data-frontmatter-date now has the timestamp values from {{dateStamp}}. If the EBB posts show different values for frontmatter vs created, the sorting should work. If they're the same or frontmatter is empty, that's the problem.
document.querySelectorAll('.sidebar li[data-path*="/logs/"]').forEach(li => { const title = li.querySelector('a')?.textContent; const fm = li.getAttribute('data-frontmatter-date'); const created = li.getAttribute('data-date'); console.log(title, '| frontmatter:', fm, '| created:', created); }); VM1424:5 Anchor Runtime Configuration | frontmatter: 1768905000000 | created: 1768904385462 VM1424:5 EBB Implementation Plan | frontmatter: 1768856400000 | created: 1768907118158 VM1424:5 EBB Temporary End Design | frontmatter: 1768820400000 | created: 1768907125570 VM1424:5 Anchor Review 3 | frontmatter: 1768695255424 | created: 1768695255424 VM1424:5 Anchor Phase 10 Part 11 | frontmatter: 1768683402002 | created: 1768683402002 VM1424:5 Anchor Phase 10 Part 10 | frontmatter: 1768608132752 | created: 1768608132752 VM1424:5 Anchor Phase 10 Part 9 | frontmatter: 1768586074944 | created: 1768586074944 VM1424:5 Anchor Phase 10 Part 8 | frontmatter: 1768529066608 | created: 1768529066608 VM1424:5 Anchor Phase 10 Part 7 | frontmatter: 1768520805313 | created: 1768520805313 VM1424:5 Anchor Phase 10 Part 6 | frontmatter: 1768444006234 | created: 1768444006234 VM1424:5 Anchor Phase 10 Part 5 | frontmatter: 1768434374700 | created: 1768434374700 VM1424:5 Anchor Phase 10 Part 4 | frontmatter: 1768379575808 | created: 1768379575808 VM1424:5 Anchor Phase 10 Part 3 | frontmatter: 1768337652601 | created: 1768337652601 VM1424:5 Anchor Phase 10 Part 2 | frontmatter: 1768309916473 | created: 1768309916473 VM1424:5 Anchor Phase 10 Part 1 | frontmatter: 1768254080642 | created: 1768254080642 VM1424:5 Audible Book Recommendations 2 | frontmatter: 1768128357416 | created: 1768128357416 VM1424:5 Steam Game Recommendations | frontmatter: 1768084543982 | created: 1768084543982 VM1424:5 Anchor Review 2 | frontmatter: 1768079848342 | created: 1768079848342 VM1424:5 Anchor Phase 9 | frontmatter: 1768013594167 | created: 1768013594167 VM1424:5 Anchor Phase 8 | frontmatter: 1768001737835 | created: 1768001737835 VM1424:5 Anchor Phase 7 | frontmatter: 1767964843916 | created: 1767964843916 VM1424:5 Anchor Phase 6 | frontmatter: 1767950011375 | created: 1767950011375 VM1424:5 Anchor Phase 5 | frontmatter: 1767914502990 | created: 1767914502990 VM1424:5 Anchor Phase 4 | frontmatter: 1767882190662 | created: 1767882190662 VM1424:5 Anchor Review 1 | frontmatter: 1767863179468 | created: 1767863179468 VM1424:5 Anchor Phase 3 Part 4 | frontmatter: 1767830109252 | created: 1767830109252 VM1424:5 Anchor Phase 3 Part 3 | frontmatter: 1767785993406 | created: 1767785993406 VM1424:5 Anchor Phase 3 Part 2 | frontmatter: 1767749326014 | created: 1767749326014 VM1424:5 Anchor Phase 3 Part 1 | frontmatter: 1767731194557 | created: 1767731194557 VM1424:5 Anchor Phase 2 | frontmatter: 1767717377503 | created: 1767717377503 VM1424:5 Anchor Phase 1 | frontmatter: 1767647878953 | created: 1767647878953 VM1424:5 Audible Book Recommendations | frontmatter: 1767583818712 | created: 1767583818712 VM1424:5 Windows Setup | frontmatter: 1767539700000 | created: 1767540258019 VM1424:5 Log Generator Script Fix | frontmatter: 1767475200000 | created: 1767475607544 VM1424:5 Anchor Interview | frontmatter: 1767465000000 | created: 1767472457894 VM1424:5 Emoji Ball Battles Design | frontmatter: 1767454200000 | created: 1767458329212 VM1424:5 Roguelite Design Analysis | frontmatter: 1767391140000 | created: 1767401811459 VM1424:5 Workflow Updates and Blog Post | frontmatter: 1767358800000 | created: 1767378979188 VM1424:5 CachyOS and NeoVim Setup | frontmatter: 1767307020000 | created: 1767307351301 VM1424:5 Omarchy and Anchor Setup | frontmatter: 1767263820000 | created: 1767264039103
The sorting is actually correct now by frontmatter date - look at the order:
1. Anchor Runtime Configuration: 1768905000000
2. EBB Implementation Plan: 1768856400000
3. EBB Temporary End Design: 1768820400000
That's descending by frontmatter date.
But I bet the displayed date in the list is still wrong. The buildList function in script.js formats the date using data-date (created), not data-frontmatter-date.
Find this line in the buildList function:
javascript
const date = formatDate(li.getAttribute('data-date'));
Change it to:
javascript
const date = formatDate(li.getAttribute('data-frontmatter-date') || li.getAttribute('data-date'));
This will display the frontmatter date when available, falling back to created date otherwise.
Yes!!! It now works, thank you.
Glad it's working. To summarize what we changed:
- pagination.html - Used
{{dateStamp}}instead of{{date}}to get the raw timestamp - navigation-js.js - Added
getDateValuefunction to prefer frontmatter date for sorting - script.js - Added
getDateValuefor sorting, and updatedbuildListto display the frontmatter date
Now posts sort and display by your Date: front matter field when present.
What would you call this session?
"Blot Theme Date Sorting Fix" or just "Blot Date Sorting"
Thank you, good bye.
Goodbye!