Compare commits

...

5 Commits

Author SHA1 Message Date
Anas Rashid
b7facedf95 Merge branch 'feature/sticky-header-footer' into main: sticky header, a footer that uses its width (#93) 2026-10-09 23:52:24 +02:00
Anas Rashid
65762f286f Footer: the made-in line starts at the logo's visible edge
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:52:15 +02:00
Anas Rashid
7077524dcb Footer: the made-in line on its own row below the logo and tagline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:51:06 +02:00
Anas Rashid
93ccabdf29 Footer: no red دیوان heading (the logo says it; its tall letters collided with the tagline); the tagline beside the logo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:50:35 +02:00
Anas Rashid
575ef530a7 Sticky header and a footer that uses its width
- The header stays at the top for readers and admins alike; its height never changes while scrolling (no jumps), it gains a translucent blurred background and a hairline shadow once scrolled. --header-h keeps anchors (#c6) and the editor's sticky toolbar and code panel clear of it. The header logo is a little smaller (48–60px)
- Footer: the brand (logo, name, tagline, the flag line) beside the links, the site's five links down one column with sources and code stacked beside them; licence, contact and attribution in a thin bottom bar. On phones the brand on top and the links in two columns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:33:06 +02:00
2 changed files with 60 additions and 16 deletions

View File

@ -130,7 +130,20 @@ import { brand } from '../lib/brand';
</aside> </aside>
<footer class="site-footer"> <footer class="site-footer">
<div class="wrap"> <div class="wrap">
<a class="footer-logo" href="/" title="سرورق"><img src="/logo/turanj.svg" width="56" height="56" alt={brand.name} /></a> <div class="footer-brand">
<a class="footer-logo" href="/" title="سرورق"><img src="/logo/turanj.svg" width="64" height="64" alt={brand.name} /></a>
<div>
<p class="footer-tagline">{brand.tagline}</p>
</div>
<p class="made-in">
<svg class="flag" viewBox="0 0 30 20" width="27" height="18" role="img" aria-label="پاکستان کا پرچم">
<rect width="30" height="20" fill="#fff" /><rect x="7.5" width="22.5" height="20" fill="#01411c" />
<circle cx="19.2" cy="10" r="6" fill="#fff" /><circle cx="20.6" cy="8.7" r="5.4" fill="#01411c" />
<polygon fill="#fff" points="23.65,5.99 23.12,7.39 24.25,8.39 22.75,8.32 22.15,9.69 21.75,8.25 20.26,8.11 21.51,7.28 21.19,5.82 22.36,6.75" />
</svg>
{brand.madeBy}
</p>
</div>
<nav class="footer-links" aria-label="سائٹ کے بارے میں"> <nav class="footer-links" aria-label="سائٹ کے بارے میں">
<div><h2>{brand.name}</h2><ul> <div><h2>{brand.name}</h2><ul>
<li><a href="/about"><Icon name="info" size={18} /><span>{brand.name} کے بارے میں<small>مقصد اور طریقۂ کار</small></span></a></li> <li><a href="/about"><Icon name="info" size={18} /><span>{brand.name} کے بارے میں<small>مقصد اور طریقۂ کار</small></span></a></li>
@ -148,13 +161,10 @@ import { brand } from '../lib/brand';
<li><a href="https://github.com/anas-rashid/divan" rel="noopener"><Icon name="code" size={18} /><span>کوڈ<small>سائٹ کا سورس</small></span><Icon name="external" size={13} class="ext" /></a></li> <li><a href="https://github.com/anas-rashid/divan" rel="noopener"><Icon name="code" size={18} /><span>کوڈ<small>سائٹ کا سورس</small></span><Icon name="external" size={13} class="ext" /></a></li>
</ul></div> </ul></div>
</nav> </nav>
<p class="made-in"> <p class="footer-bar">
<svg class="flag" viewBox="0 0 30 20" width="27" height="18" role="img" aria-label="پاکستان کا پرچم"> <span>متن: عوامی ملکیت · کوڈ: <span lang="en" dir="ltr">GNU GPL v3</span></span>
<rect width="30" height="20" fill="#fff" /><rect x="7.5" width="22.5" height="20" fill="#01411c" /> <a href={brand.contact.url} rel="noopener">{brand.contact.text}</a>
<circle cx="19.2" cy="10" r="6" fill="#fff" /><circle cx="20.6" cy="8.7" r="5.4" fill="#01411c" /> <a href={brand.basedOn.url} rel="noopener" lang="en" dir="ltr">{brand.basedOn.text}</a>
<polygon fill="#fff" points="23.65,5.99 23.12,7.39 24.25,8.39 22.75,8.32 22.15,9.69 21.75,8.25 20.26,8.11 21.51,7.28 21.19,5.82 22.36,6.75" />
</svg>
{brand.madeBy}
</p> </p>
</div> </div>
</footer> </footer>
@ -551,6 +561,15 @@ import { brand } from '../lib/brand';
</form> </form>
</dialog> </dialog>
<div class="toasts" aria-live="polite"></div> <div class="toasts" aria-live="polite"></div>
<script is:inline>
// the sticky header: its height for anchors and toolbars (--header-h), and a shadow once the page is scrolled
(() => {
const root = document.documentElement, header = document.querySelector('.site-header');
new ResizeObserver(() => root.style.setProperty('--header-h', header.offsetHeight + 'px')).observe(header);
const mark = () => (scrollY > 8 ? (root.dataset.scrolled = '') : delete root.dataset.scrolled);
addEventListener('scroll', mark, { passive: true }); mark();
})();
</script>
<script is:inline> <script is:inline>
// reading mode: only the text, the breadcrumbs and the logo; remembered across pages until switched off // reading mode: only the text, the breadcrumbs and the logo; remembered across pages until switched off
document.addEventListener('click', (e) => { document.addEventListener('click', (e) => {

View File

@ -89,7 +89,7 @@ body { margin: 0; background: var(--surface-page); color: var(--text); font-fami
.site-header .wrap { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-top: 12px; } .site-header .wrap { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-top: 12px; }
.wordmark { display: flex; flex: none; } .wordmark { display: flex; flex: none; }
/* the logo: the shamsa with دیوان inside, sized with the viewport */ /* the logo: the shamsa with دیوان inside, sized with the viewport */
.wordmark .logo { width: clamp(52px, 13vw, 72px); height: auto; filter: drop-shadow(0 1px 3px rgb(0 0 0 / .15)); overflow: visible; } .wordmark .logo { width: clamp(48px, 12vw, 60px); height: auto; filter: drop-shadow(0 1px 3px rgb(0 0 0 / .15)); overflow: visible; }
/* on hover the two layers of rays turn opposite ways; the blue disc and the word stay still */ /* on hover the two layers of rays turn opposite ways; the blue disc and the word stay still */
.logo .rays-long, .logo .rays-short { transform-box: fill-box; transform-origin: center; } .logo .rays-long, .logo .rays-short { transform-box: fill-box; transform-origin: center; }
:is(.wordmark:hover, .wordmark:focus-visible, .masthead-logo:hover) .rays-long { animation: rays 9s linear infinite; } :is(.wordmark:hover, .wordmark:focus-visible, .masthead-logo:hover) .rays-long { animation: rays 9s linear infinite; }
@ -205,14 +205,8 @@ mark { background: var(--accent-soft); color: inherit; border-radius: 3px; paddi
.pager { display: flex; gap: 12px; justify-content: center; margin-top: 20px; } .pager { display: flex; gap: 12px; justify-content: center; margin-top: 20px; }
/* made by a Pakistani for Pakistan: the flag beside the line (an SVG: the emoji is "PK" on Windows) */ /* made by a Pakistani for Pakistan: the flag beside the line (an SVG: the emoji is "PK" on Windows) */
.made-in { margin: 10px 0 0; display: flex; align-items: center; justify-content: center; gap: 10px;
font-family: 'Noto Nastaliq Urdu', serif; font-size: 1.05rem; line-height: 2; color: var(--text); } /* a tagline */
.made-in .flag { flex: none; border-radius: 2px; box-shadow: 0 0 0 1px rgb(0 0 0 / .15); }
.site-footer { border-top: 1.5px solid var(--border); padding: 20px 0 16px; margin-top: 16px; font-size: var(--fs-sm); color: var(--text-muted); background: var(--surface-raised); } .site-footer { border-top: 1.5px solid var(--border); padding: 20px 0 16px; margin-top: 16px; font-size: var(--fs-sm); color: var(--text-muted); background: var(--surface-raised); }
/* footer: three labelled groups of links, each with an icon and a one-line note; external links marked */ /* footer: three labelled groups of links, each with an icon and a one-line note; external links marked */
.footer-logo { display: block; width: fit-content; margin: 0 auto 14px; }
.footer-logo img { display: block; width: 56px; height: 56px; }
.footer-links { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px 24px; max-width: 760px; margin: 0 auto; }
.footer-links h2 { text-align: start; font-size: var(--fs-xs); font-weight: 700; color: var(--text-muted); letter-spacing: .02em; margin: 0 0 4px; } .footer-links h2 { text-align: start; font-size: var(--fs-xs); font-weight: 700; color: var(--text-muted); letter-spacing: .02em; margin: 0 0 4px; }
.footer-links h2::after { display: none; } .footer-links h2::after { display: none; }
.footer-links ul { list-style: none; margin: 0; padding: 0; } .footer-links ul { list-style: none; margin: 0; padding: 0; }
@ -222,7 +216,6 @@ mark { background: var(--accent-soft); color: inherit; border-radius: 3px; paddi
.footer-links a span { display: flex; flex-direction: column; line-height: 1.5; } .footer-links a span { display: flex; flex-direction: column; line-height: 1.5; }
.footer-links small { color: var(--text-muted); font-size: var(--fs-xs); } .footer-links small { color: var(--text-muted); font-size: var(--fs-xs); }
.footer-links .ext { align-self: flex-start; margin-top: 6px; margin-inline-start: -4px; opacity: .5; } .footer-links .ext { align-self: flex-start; margin-top: 6px; margin-inline-start: -4px; opacity: .5; }
.made-in { border-top: 1px dashed var(--line); padding-top: 12px; margin-top: 16px !important; }
h1 + .muted { text-align: center; margin-top: 0; } h1 + .muted { text-align: center; margin-top: 0; }
@ -837,3 +830,35 @@ p, li { text-wrap: pretty; }
.error-page { text-align: center; padding: 32px 0 48px; } .error-page { text-align: center; padding: 32px 0 48px; }
.error-code { font: 700 4.5rem/1 'Noto Naskh Arabic', serif; color: var(--accent, var(--brand)); opacity: .35; margin: 0 0 8px; } .error-code { font: 700 4.5rem/1 'Noto Naskh Arabic', serif; color: var(--accent, var(--brand)); opacity: .35; margin: 0 0 8px; }
.error-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 18px; } .error-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 18px; }
/* ── the header stays at the top for everyone. Its height never changes while scrolling (that would make the page jump):
once scrolled it only gains a translucent, blurred background and a hairline shadow. --header-h (set by Base) lets
anchors and sticky toolbars clear it ── */
.site-header { position: sticky; top: 0; z-index: 15; background: var(--surface-page); transition: box-shadow .2s, background .2s; }
.site-header .wrap { padding-bottom: 10px; }
:root[data-scrolled] .site-header { background: color-mix(in srgb, var(--surface-page) 86%, transparent); backdrop-filter: saturate(1.3) blur(10px);
box-shadow: 0 1px 0 var(--line), 0 8px 20px rgb(0 0 0 / .06); }
:root { scroll-padding-top: calc(var(--header-h, 80px) + 12px); } /* #c5 and other anchors land below the header */
.toolbar { top: var(--header-h, 0px); }
.live-code { top: calc(var(--header-h, 0px) + 8px); }
/* ── footer: the brand beside three groups of links across the page's width; licence and attribution in a thin bar ── */
.site-footer .wrap { display: grid; grid-template-columns: minmax(300px, 1.2fr) 2fr; gap: 18px 40px; align-items: start; }
.footer-brand { display: flex; flex-wrap: wrap; align-items: center; gap: 0 14px; }
.footer-brand .made-in { flex-basis: 100%; margin-top: 14px; padding-inline-start: 5px; } /* its own line below the logo, starting at the
logo's visible edge (the medallion's solid lobes sit about 5px inside the image) */
.footer-logo { flex: none; display: block; }
.footer-logo img { display: block; width: 64px; height: 64px; }
.footer-tagline { margin: 4px 0 0; font: 700 1.15rem/2.2 'Noto Nastaliq Urdu', serif; color: var(--action); } /* the logo says دیوان; the tagline beside it */
.made-in { margin: 8px 0 0; display: flex; align-items: center; gap: 8px; font-family: 'Noto Nastaliq Urdu', serif; font-size: .9rem; line-height: 2; color: var(--text); }
.made-in .flag { flex: none; border-radius: 2px; box-shadow: 0 0 0 1px rgb(0 0 0 / .15); }
.footer-links { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 24px; }
.footer-links > div:first-child { grid-row: span 2; } /* the site's five links down one column, sources and code stacked beside them */
.footer-bar { grid-column: 1 / -1; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 18px; margin: 0; padding-top: 10px;
border-top: 1px dashed var(--line); font-size: var(--fs-xs); color: var(--text-muted); }
.footer-bar a { color: var(--text-muted); }
.footer-bar a:hover { color: var(--active); }
@media (max-width: 760px) {
.site-footer .wrap { grid-template-columns: 1fr; }
.footer-bar { justify-content: center; text-align: center; }
}