Files
CashuMints.space/web/src/components/Footer.astro
T
michilisandCursor c74c7fc187 Ship LNURL mint pages, indexing UI, and reviews rewrite.
Add lnurl list/detail routes, OG fixtures, i18n strings, and the write/
index client flows so the site surfaces the new mint type end to end.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 03:44:43 +02:00

128 lines
5.1 KiB
Plaintext

---
/**
* The site footer: one component, every page, three zones.
*
* Zone one is four columns — who this is, then the two link groups people actually
* navigate to, then the language control. Zone two is a thin bar with the copyright
* and the credit, and nothing else: the moment anything else moves into that row it
* stops being a signature and starts being a fourth navigation surface.
*
* What used to be here was four flat rows, the last of which was ~20 raw language
* links wrapped over two lines. That wall is now the same switcher the header carries
* (see LanguageSwitcher.astro, `placement="footer"`), so there is one language control
* on the site in two places rather than two controls that happen to agree.
*
* Everything below is static per locale and prerenders. No hex, no npub, no counts,
* nothing fetched: the footer is the one region of every page that must be identical
* on the home page and on a mint page that failed to load.
*/
import LanguageSwitcher from './LanguageSwitcher.astro';
import Moai from './Moai.astro';
import { localePath, useI18n } from '../i18n';
import { pageLocale } from '../i18n/paths';
const year = new Date().getFullYear();
const GITHUB = 'https://github.com/Azzamo-net/cashumints.space';
const { locale } = pageLocale(Astro);
const t = useI18n(locale);
/*
* Route slugs stay English in every language and `localePath` adds the prefix, exactly
* as the header does. See src/i18n/routing.ts for why that trade was made.
*/
const explore = [
{ href: localePath('/mints', locale), label: t('footer.allMints') },
{ href: localePath('/fedimints', locale), label: t('footer.allFedimints') },
{ href: localePath('/lnurl-mints', locale), label: t('footer.allLnurlMints') },
{ href: localePath('/reviews', locale), label: t('footer.allReviews') },
{ href: localePath('/wallets', locale), label: t('footer.wallets') },
];
/*
* GitHub sits in this column rather than beside the credit line because it is a place
* to go, not a signature. It is the only external link in the columns, so it carries
* `rel="noopener"` and nothing else needs to.
*/
const site = [
{ href: localePath('/about', locale), label: t('footer.about'), external: false },
{ href: GITHUB, label: 'GitHub', external: true },
{ href: localePath('/terms', locale), label: t('footer.terms'), external: false },
{ href: localePath('/privacy', locale), label: t('footer.privacy'), external: false },
{ href: localePath('/disclaimer', locale), label: t('footer.disclaimer'), external: false },
];
/*
* One sentence with a link in the middle of it, so the whole thing is one string a
* translator can reorder. The only interpolated value is markup this file wrote, and
* the label inside it is a name, so there is nothing here that could come from a mint,
* a relay or a reader.
*/
const madeBy = t('footer.madeBy', {
azzamo: '<a href="https://azzamo.net" rel="noopener">Azzamo</a>',
});
---
<footer class="site-footer" role="contentinfo">
<div class="foot-cols">
{/*
The brand column. The custodial warning lives here, under the description,
because it is the most honest sentence on the site and it was previously an
orphan floating at the bottom right of the last row. It is set in `--muted`
rather than `--faint`: quiet, but a step louder than the link columns, which is
the only typographic claim this footer makes about relative importance.
*/}
<div class="foot-brand">
<a class="foot-mark" href={localePath('/', locale)}>
<Moai size={20} />
<span>Cashumints.space</span>
</a>
<p class="foot-desc">{t('footer.tagline')}</p>
<p class="foot-warn">{t('footer.warning')}</p>
</div>
{/*
One nav landmark over both link columns, not one per column: a screen reader
listing landmarks should find "Footer", not "Footer" twice. The language control
carries its own nav, because it is a different kind of thing — it does not take
you to another page, it takes you to this page again.
*/}
<nav class="foot-nav" aria-label={t('footer.nav')}>
<div class="foot-col">
<h2 class="foot-head">{t('footer.col.explore')}</h2>
<ul>
{explore.map((link) => (
<li><a href={link.href}>{link.label}</a></li>
))}
</ul>
</div>
<div class="foot-col">
<h2 class="foot-head">{t('footer.col.site')}</h2>
<ul>
{site.map((link) => (
<li>
<a href={link.href} rel={link.external ? 'noopener' : undefined}>{link.label}</a>
</li>
))}
</ul>
</div>
</nav>
<div class="foot-col foot-lang">
<h2 class="foot-head">{t('lang.label')}</h2>
<LanguageSwitcher placement="footer" />
</div>
</div>
{/*
The bottom bar. Two items, and the discipline is that it stays two items: the
copyright and who made it. Everything else that was ever tempted into this row now
has a column above it.
*/}
<div class="foot-bar">
<span>{t('footer.copyright', { year: String(year) })}</span>
<span class="foot-credit" set:html={madeBy} />
</div>
</footer>