trackers: one-click spotting links: header flyout (hover/focus, delayed close, side-aware), chips on the spotting tile, mobile sub-links
All checks were successful
Deploy production / deploy (push) Successful in 1m8s

This commit is contained in:
2026-10-25 12:18:33 +00:00
parent d8490ce94f
commit 45943543c2
3 changed files with 293 additions and 73 deletions

View File

@@ -32,11 +32,51 @@ export const projects = [
}
] as const;
type Tracker = {
name: string;
subtitle: string;
href: string;
external: boolean;
/** Sub-trackers linked directly from this item (header flyout, tile chips). */
children?: readonly Tracker[];
};
// The spotting trackers: pokedexes of things I can spot in the wild. Grouped under one
// "spotting" item in the header, home page and /trackers; each keeps its own URL.
export const spottingTrackers: readonly Tracker[] = [
{
name: 'cars',
subtitle: 'a car pokedex: every brand and model, with Dutch and US data',
href: '/trackers/cars',
external: false
},
{
name: 'trains',
subtitle:
'a train pokedex: steam, diesel and electric locos, high-speed trains, multiple units, metros and trams',
href: '/trackers/trains',
external: false
},
{
name: 'planes',
subtitle: 'a plane pokedex: airliners, helicopters, bizjets, light aircraft and warbirds',
href: '/trackers/planes',
external: false
},
{
name: 'wildlife',
subtitle:
'a wild animal pokedex: everything you can spot in the Netherlands, plus famous animals worldwide',
href: '/trackers/wildlife',
external: false
}
];
// Trackers, in display order. Names are short single nouns. Most keep their existing
// /projects/... URLs (no moves, so no redirects needed). `private` trackers are shown in the
// header, home page and /trackers only to the logged-in owner (layout data `isOwner`), never
// in the sitemap; their routes 404 for everyone else.
const allTrackers = [
const allTrackers: readonly (Tracker & { private: boolean })[] = [
{
name: 'tasks',
subtitle: 'private: to-dos, goals and my TickTick history',
@@ -63,6 +103,7 @@ const allTrackers = [
subtitle: "cars, trains, planes & wildlife I've spotted",
href: '/trackers/spotting',
external: false,
children: spottingTrackers,
private: false
},
{
@@ -135,45 +176,10 @@ const allTrackers = [
external: false,
private: false
}
] as const;
type Tracker = { name: string; subtitle: string; href: string; external: boolean };
const strip = ({ name, subtitle, href, external }: Tracker): Tracker => ({
name,
subtitle,
href,
external
});
// The spotting trackers: pokedexes of things I can spot in the wild. Grouped under one
// "spotting" item in the header, home page and /trackers; each keeps its own URL.
export const spottingTrackers: readonly Tracker[] = [
{
name: 'cars',
subtitle: 'a car pokedex: every brand and model, with Dutch and US data',
href: '/trackers/cars',
external: false
},
{
name: 'trains',
subtitle:
'a train pokedex: steam, diesel and electric locos, high-speed trains, multiple units, metros and trams',
href: '/trackers/trains',
external: false
},
{
name: 'planes',
subtitle: 'a plane pokedex: airliners, helicopters, bizjets, light aircraft and warbirds',
href: '/trackers/planes',
external: false
},
{
name: 'wildlife',
subtitle:
'a wild animal pokedex: everything you can spot in the Netherlands, plus famous animals worldwide',
href: '/trackers/wildlife',
external: false
}
];
const strip = ({ name, subtitle, href, external, children }: Tracker): Tracker =>
children ? { name, subtitle, href, external, children } : { name, subtitle, href, external };
/** Public trackers (sitemap, logged-out navigation). */
export const trackers: readonly Tracker[] = allTrackers.filter((t) => !t.private).map(strip);
/** Owner-only trackers. */