an app to share curated trails
sidetrail.app
1import type { TrailCardData } from "../data/queries";
2import { TrailCard } from "./TrailCard";
3import { HomeEmptyState } from "./HomeEmptyState";
4import "./HomeTrailsList.css";
5
6type Props = {
7 trails: TrailCardData[];
8};
9
10// Reorder items for CSS columns masonry: [1,2,3,4,5,6] -> [1,3,5,2,4,6]
11// CSS columns fills top-to-bottom, so left column gets first half of DOM.
12// We put odd-ranked items first (left col), then even-ranked (right col).
13// Visual order reads left-to-right: 1,2 / 3,4 / 5,6
14// Tab order follows DOM: 1 -> 3 -> 5 -> 2 -> 4 -> 6 (column-wise)
15// On mobile, CSS order property restores original ranking.
16function interleaveForMasonry<T>(items: T[]): { item: T; originalIndex: number }[] {
17 const result: { item: T; originalIndex: number }[] = [];
18 for (let i = 0; i < items.length; i += 2) {
19 result.push({ item: items[i], originalIndex: i });
20 }
21 for (let i = 1; i < items.length; i += 2) {
22 result.push({ item: items[i], originalIndex: i });
23 }
24 return result;
25}
26
27export function HomeTrailsList({ trails }: Props) {
28 if (trails.length === 0) {
29 return (
30 <HomeEmptyState message="no trails are trending right now" buttonText="start a new trail" />
31 );
32 }
33
34 const reordered = interleaveForMasonry(trails);
35
36 return (
37 <div className="HomeTrailsList-grid">
38 {reordered.map(({ item: trail, originalIndex }) => (
39 <div key={trail.uri} style={{ "--original-index": originalIndex } as React.CSSProperties}>
40 <TrailCard
41 uri={trail.uri}
42 rkey={trail.rkey}
43 creatorHandle={trail.creatorHandle}
44 title={trail.title}
45 description={trail.description}
46 accentColor={trail.accentColor}
47 backgroundColor={trail.backgroundColor}
48 creator={trail.creator}
49 stopsCount={trail.stopsCount}
50 />
51 </div>
52 ))}
53 </div>
54 );
55}