Something went wrong. Try again.
Central platform for European atproto.<cc> country community websites atproto.eu
community atproto
Something went wrong. Try again.
4.2 kB · 97 lines
Astro
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798---// The atproto.eu network: the sibling national sites this codebase builds (everything except the// .eu aggregate itself), each //atproto.<cc> wordmark in its own country accent. Shown only on the// aggregate (see HomePage). Each opens its national site in a new tab.import Wordmark from './Wordmark.astro';import { SCOPES, type AccentPack } from '../config/scopes';import { useTranslations, type Lang } from '../i18n/ui';import { seededShuffleInPlace } from '../lib/seeded-shuffle';
interface Props { lang: Lang;}const { lang } = Astro.props;const t = useTranslations(lang);
interface NetSite { cc: string; accent: AccentPack; // primary (slash 1 + dot) accent2: AccentPack; // secondary (slash 2) url: string; live: boolean; // false -> DNS not pointed yet: rendered as a dimmed, non-linked entry}
// Every atproto.<cc> site this codebase builds as a full scope, except the aggregate itself. Each// carries its own two-colour pair, resolved via data-accent/data-accent2 below. This is the// "Local communities" strip (countries AND cities): a city on our platform (atproto.barcelona)// belongs here more than an external country. The hard rule ("a city is never a country") is upheld// elsewhere (the FOOTER national list and the stats attribution), not by hiding the city's site.const built: NetSite[] = Object.values(SCOPES) .filter((s) => !s.aggregate) .map((s) => ({ cc: s.cc, accent: s.accent, accent2: s.accent2, url: s.site, live: true }));
// Family sites NOT built as full scopes here — friendly external communities on their own design,// or ours-but-not-yet-scoped. Given the two-colour lockup so they still read as one of us. `live`// gates a dimmed non-linked state for a domain whose DNS isn't pointed yet. Deduped against real// scopes, so an entry drops out automatically once its cc becomes a built scope.const PLANNED: NetSite[] = [ // atproto.fr — an existing, friendly French community (their own design). French flag blue + red. { cc: 'fr', accent: 'cobalt', accent2: 'red', url: 'https://atproto.fr/', live: true },];
const builtCcs = new Set(built.map((s) => s.cc));const sites: NetSite[] = [...built, ...PLANNED.filter((p) => !builtCcs.has(p.cc))];
// Arbitrary order so no country ever reads as ranked or featured. Seeded from the set of country// codes (not Math.random()) so the order is stable between builds of the same set -- otherwise the// homepage HTML would differ every build and every scope would look "changed" to the deploy step.seededShuffleInPlace(sites, (s) => s.cc);---
<nav class="section network" aria-label={t('network.heading')}> <ul class="network-list"> {sites.map((s) => ( <li data-accent={s.accent} data-accent2={s.accent2}> {s.live ? ( <a class="network-item" href={s.url} target="_blank" rel="noopener"> <Wordmark cc={s.cc} class="network-mark" /> </a> ) : ( <span class="network-item is-soon" title="Coming soon"> <Wordmark cc={s.cc} class="network-mark" /> </span> )} </li> ))} </ul></nav>
<style> /* A single quiet band of the family, framed by the section rules above and below. */ .network { padding-block: var(--space-11); } .network-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-6) var(--space-11); } .network-item { display: inline-flex; text-decoration: none; /* The wordmark's own accent (per-country via data-accent on the <li>) already carries the colour; the whole lockup lifts to inkstrong on hover/focus for the affordance. */ } .network-mark { font-size: clamp(1.5rem, 3.5vw, 2.25rem); transition: opacity 0.12s ease; } a.network-item:hover .network-mark, a.network-item:focus-visible .network-mark { opacity: 0.72; } .network-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px; } /* Not-yet-live sites (DNS unpointed): shown but visibly inert, no link. */ .is-soon { cursor: default; } .is-soon .network-mark { opacity: 0.4; }</style>