diff --git a/CLAUDE.md b/CLAUDE.md index 17f288f..78e55c2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,6 +15,10 @@ Every merge into `main` is a release. Before committing any change to `mdcms.py` In practice: check out `development`, do the work, push to `development`, PR `development` → `main` when ready to release. +## Unreleased changelog + +`docs/unreleased.md` is a living document that tracks every fix or feature on `development` that has not yet been merged to `main`. Keep it current: whenever a change lands on `development`, add or update an entry in `unreleased.md` in the same commit (or a follow-up commit to `development`). When a batch of changes is merged to `main` and released, clear the entries that were released and leave the file in place for the next round of work. + ## What this project is MD-CMS is a markdown-based static site system with two distinct parts: @@ -166,6 +170,7 @@ For nested navigation, set `parent: ` and `parent-sort` on - Variant files: `..md` — the suffix is only treated as a category if the code is declared in config - `categories-sectionnames: per-category` requires each section in `nav.yml` to have a `categorynames` block with an entry per category code - RTL is set per category via `direction: rtl` +- Line height is set per category via `line-height: 2.8` (useful for scripts like Nastaliq that need extra vertical space). Restores to theme default when switching to a category without this key. ## Dynamic post tags (mdcms code blocks) diff --git a/app/index.html b/app/index.html index b9b030a..d811dc9 100644 --- a/app/index.html +++ b/app/index.html @@ -76,7 +76,7 @@ if ('serviceWorker' in navigator) { --link-visited-colour: #7C3AED; --link-decoration: underline; --link-hover-decoration: underline; - --link-hover-weight: 700; + --link-hover-weight: normal; --link-visited-decoration: underline; --search-bg: #FFFFFF; --search-border: #E2E8F0; @@ -113,7 +113,7 @@ if ('serviceWorker' in navigator) { --link-visited-colour: #A78BFA; --link-decoration: underline; --link-hover-decoration: underline; - --link-hover-weight: 700; + --link-hover-weight: normal; --link-visited-decoration: underline; --search-bg: #1E293B; --search-border: #334155; @@ -171,7 +171,7 @@ body { flex-direction: column; overflow-y: auto; z-index: 100; - transition: background-color 0.2s ease, transform 0.3s ease; + transition: background-color 0.2s ease, transform 0.3s ease, visibility 0s ease 0.3s; } .layout-sidebar.nav-left .sidebar { left: 0; } @@ -187,12 +187,13 @@ body { .sidebar::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 2px; } .sidebar::-webkit-scrollbar-track { background: var(--scrollbar-track); } +.layout-sidebar .mobile-header { display: none; } + /* ═══════════════════════════════════════════ SIDEBAR CONTENT ═══════════════════════════════════════════ */ .sidebar-header { padding: 1.5rem 1.25rem 1rem; - border-bottom: 1px solid var(--divider); flex-shrink: 0; } @@ -337,7 +338,6 @@ body { /* Sidebar footer */ .sidebar-footer { padding: 0.75rem 1.25rem; - border-top: 1px solid var(--divider); flex-shrink: 0; } @@ -446,7 +446,7 @@ body { border: none; cursor: pointer; padding: 0.4rem; - color: var(--font-colour); + color: var(--nav-link-colour, var(--font-colour)); z-index: 150; } .hamburger svg { width: 22px; height: 22px; } @@ -625,17 +625,17 @@ body { padding: 0.55rem 0.85rem; cursor: pointer; font-size: 0.88rem; - color: var(--font-colour); + color: var(--nav-link-colour, var(--font-colour)); border-bottom: 1px solid var(--divider); transition: background 0.1s; } .category-option:last-child { border-bottom: none; } .category-option:hover { background: var(--nav-hover-bg); } -.category-option.active { background: var(--nav-active-bg); color: var(--accent); font-weight: 600; } +.category-option.active { background: var(--nav-active-bg); color: var(--nav-link-active-colour, var(--accent)); font-weight: 600; } .category-option .secondary { display: block; font-size: 0.75rem; - color: var(--font-colour-muted); + color: var(--nav-section-heading-colour, var(--font-colour-muted)); margin-top: 0.15rem; } @@ -738,12 +738,13 @@ body { .layout-sidebar .sidebar { transform: translateX(-100%); + visibility: hidden; width: 80vw; max-width: 320px; box-shadow: var(--shadow-md); } .layout-sidebar.nav-right .sidebar { transform: translateX(100%); } - .layout-sidebar .sidebar.open { transform: translateX(0); } + .layout-sidebar .sidebar.open { transform: translateX(0); visibility: visible; transition: background-color 0.2s ease, transform 0.3s ease; } .layout-sidebar .main-area { margin-left: 0 !important; margin-right: 0 !important; } .main-content { padding: 1rem 1.25rem 3rem; } @@ -957,6 +958,7 @@ body { let activeCategory = null; // current code let sectionnamesMode = 'same'; // 'same' | 'per-category' let loadedFonts = new Set(); // track which font files have been loaded + let defaultLineHeight = null; // theme/CSS default, captured after theme is applied // ─── Icons ──────────────────────────────────────────────── const STANDARD_ICONS = ['dark_mode','light_mode','menu','search','arrow_right','arrow_drop_down','mobile_arrow_down','language','info','warning','error','success','exclamation','dangerous','report','history','text_compare','keyboard_arrow_right','keyboard_arrow_down','keyboard_double_arrow_right','keyboard_double_arrow_down','expand_content','collapse_content','add','minimize']; @@ -1091,9 +1093,16 @@ body { async function maybeLoadCategoryFont(code) { const cat = categoriesByCode[code]; - if (!cat || !cat.font || loadedFonts.has(cat.font)) return; - showFontLoadingBanner(); + if (!cat || !cat.font) { + document.body.style.fontFamily = ''; + return; + } const family = 'mdcms-cat-' + code; + if (loadedFonts.has(cat.font)) { + document.body.style.fontFamily = `"${family}", ${getComputedStyle(document.documentElement).getPropertyValue('--font-body').trim()}`; + return; + } + showFontLoadingBanner(); const css = `@font-face { font-family: "${family}"; src: url("assets/fonts/${cat.font}"); }`; const style = document.createElement('style'); style.textContent = css; @@ -1103,8 +1112,7 @@ body { await face.load(); document.fonts.add(face); loadedFonts.add(cat.font); - // Apply font to body for this session - document.body.style.fontFamily = `"${family}", ${getComputedStyle(document.body).fontFamily}`; + document.body.style.fontFamily = `"${family}", ${getComputedStyle(document.documentElement).getPropertyValue('--font-body').trim()}`; } catch (e) { console.warn('Font load failed:', e); } @@ -2018,7 +2026,6 @@ function fmtDatetime(dtStr) { const layout = el('div', { className: `layout-sidebar nav-${navPos}` }); const mobileHeader = el('div', { className: 'mobile-header' }); - mobileHeader.style.display = 'none'; const hamburger = el('button', { className: 'hamburger', 'aria-label': 'Open menu' }); hamburger.appendChild(iconEl('menu')); const mobileName = el('span', { className: 'sidebar-sitename', textContent: config.sitename || 'MD-CMS' }); @@ -2028,10 +2035,6 @@ function fmtDatetime(dtStr) { } mobileHeader.appendChild(mobileName); - const mobileStyle = document.createElement('style'); - mobileStyle.textContent = `@media (max-width: 768px) { .layout-sidebar .mobile-header { display: flex !important; } }`; - document.head.appendChild(mobileStyle); - const overlay = el('div', { className: 'mobile-overlay' }); overlay.addEventListener('click', () => closeMobileMenu()); @@ -2298,6 +2301,9 @@ function fmtDatetime(dtStr) { document.querySelectorAll('.category-bar').forEach(b => b.setAttribute('dir', direction)); document.querySelectorAll('.md-content, .title-bar').forEach(el => el.setAttribute('dir', direction)); document.querySelectorAll('.sidebar').forEach(el => el.setAttribute('dir', direction)); + // Apply per-category line-height override, or restore theme default + const lh = cat && cat['line-height']; + document.documentElement.style.setProperty('--line-height-body', lh ? String(lh) : (defaultLineHeight || '1.7')); } function doSearch(query, resultsEl) { @@ -2770,6 +2776,7 @@ function fmtDatetime(dtStr) { if (config.theme) applyThemeYml(themeConfig); else applyConfigTheme(); + defaultLineHeight = getComputedStyle(document.documentElement).getPropertyValue('--line-height-body').trim() || '1.7'; applyTheme(getInitialTheme()); // nav.yml — phase 2 expects `sections:` + `pages:` blocks; phase 1 flat