From eedf1aec58537e048d402efb53cb22af8bc5e4e5 Mon Sep 17 00:00:00 2001 From: Gigi Date: Tue, 7 Jul 2026 10:24:11 +0200 Subject: [PATCH 01/11] feat: add developer spotlight landing page Replace the /spotlight redirect with a real landing page that showcases each spotlight using its OG preview image, newest as a featured card and the rest in a responsive grid. --- components/SpotlightCard.tsx | 59 +++++++++++++++++++++++++++++++++++ components/post/postShared.ts | 3 ++ next.config.js | 5 --- pages/spotlight/index.tsx | 59 +++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 components/SpotlightCard.tsx create mode 100644 pages/spotlight/index.tsx diff --git a/components/SpotlightCard.tsx b/components/SpotlightCard.tsx new file mode 100644 index 000000000..4045f12a6 --- /dev/null +++ b/components/SpotlightCard.tsx @@ -0,0 +1,59 @@ +import { formatDate } from 'pliny/utils/formatDate' +import { CoreContent } from 'pliny/utils/contentlayer' +import type { Blog } from 'contentlayer/generated' +import Image from '@/components/Image' +import Link from '@/components/Link' +import siteMetadata from '@/data/siteMetadata' +import { getSpotlightOgImage } from '@/components/post/postShared' + +interface Props { + post: CoreContent + featured?: boolean +} + +export default function SpotlightCard({ post, featured = false }: Props) { + const { path, date, title, summary, images } = post + const ogImage = getSpotlightOgImage(images) + + return ( + +
+ {ogImage && ( + {title} + )} +
+
+ +

+ {title} +

+ {summary && ( +

{summary}

+ )} +
+ + ) +} diff --git a/components/post/postShared.ts b/components/post/postShared.ts index 41d500c4d..3fd25ef86 100644 --- a/components/post/postShared.ts +++ b/components/post/postShared.ts @@ -22,6 +22,9 @@ export const discussUrl = () => /** Spotlight posts: images[0] = OG/social, images[1] = cover hero. */ export const getSpotlightHeroImage = (images?: string[]) => images?.[1] +/** Spotlight posts: images[0] = OG/social preview. */ +export const getSpotlightOgImage = (images?: string[]) => images?.[0] + /** Keep in sync with SectionContainer horizontal layout. */ export const postSectionClasses = 'mx-2 max-w-3xl px-4 sm:px-6 md:mx-auto lg:mx-auto xl:max-w-5xl xl:px-0' diff --git a/next.config.js b/next.config.js index 22f0b12ab..a18a23536 100644 --- a/next.config.js +++ b/next.config.js @@ -150,11 +150,6 @@ module.exports = () => { destination: '/faq/grantee', permanent: true, }, - { - source: '/spotlight', - destination: '/tags/spotlight', - permanent: true, - }, { source: '/heartbeat', destination: 'https://heartbeat.opensats.org/', diff --git a/pages/spotlight/index.tsx b/pages/spotlight/index.tsx new file mode 100644 index 000000000..0dfdeb934 --- /dev/null +++ b/pages/spotlight/index.tsx @@ -0,0 +1,59 @@ +import { PageSEO } from '@/components/SEO' +import siteMetadata from '@/data/siteMetadata' +import SpotlightCard from '@/components/SpotlightCard' +import { kebabCase } from 'pliny/utils/kebabCase' +import { sortedBlogPost, allCoreContent } from 'pliny/utils/contentlayer' +import { InferGetStaticPropsType } from 'next' +import { allBlogs } from 'contentlayer/generated' +import type { Blog } from 'contentlayer/generated' + +export const getStaticProps = async () => { + const posts = allCoreContent( + sortedBlogPost( + allBlogs.filter( + (post) => + post.draft !== true && + post.tags.map((tag) => kebabCase(tag)).includes('spotlight') + ) + ) as Blog[] + ) + return { props: { posts } } +} + +export default function Spotlight({ + posts, +}: InferGetStaticPropsType) { + const [featured, ...rest] = posts + + return ( + <> + +
+

+ Developer Spotlights +

+

+ Get to know the developers we support and the open-source projects + they pour their days into. +

+
+ + {featured && ( +
+ +
+ )} + + {rest.length > 0 && ( +
+ {rest.map((post) => ( + + ))} +
+ )} + + ) +} From cba0bc9fa9083d38e939f1f55422b60bf0c9fabd Mon Sep 17 00:00:00 2001 From: Gigi Date: Tue, 7 Jul 2026 10:28:58 +0200 Subject: [PATCH 02/11] feat: use hero image as featured spotlight card background Layer the smaller OG preview on top of the wide hero image instead of enlarging the OG card, which scaled poorly at full width. --- components/SpotlightCard.tsx | 79 ++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/components/SpotlightCard.tsx b/components/SpotlightCard.tsx index 4045f12a6..fa78c711a 100644 --- a/components/SpotlightCard.tsx +++ b/components/SpotlightCard.tsx @@ -4,7 +4,10 @@ import type { Blog } from 'contentlayer/generated' import Image from '@/components/Image' import Link from '@/components/Link' import siteMetadata from '@/data/siteMetadata' -import { getSpotlightOgImage } from '@/components/post/postShared' +import { + getSpotlightOgImage, + getSpotlightHeroImage, +} from '@/components/post/postShared' interface Props { post: CoreContent @@ -15,6 +18,66 @@ export default function SpotlightCard({ post, featured = false }: Props) { const { path, date, title, summary, images } = post const ogImage = getSpotlightOgImage(images) + if (featured) { + const heroImage = getSpotlightHeroImage(images) ?? ogImage + return ( + +
+ {heroImage && ( + + )} +
+ {ogImage && ( +
+
+ {title} +
+
+ )} +
+

+ Developer Spotlight +

+ +

+ {title} +

+ {summary && ( +

+ {summary} +

+ )} +
+
+ + ) + } + return ( )}
-
+
-

+

{title}

{summary && ( From bd20c7abf9f2d40d200551c3a1efa692a81c330b Mon Sep 17 00:00:00 2001 From: Gigi Date: Tue, 7 Jul 2026 10:29:55 +0200 Subject: [PATCH 03/11] feat: add apply for funding link to spotlight page Mirror the funds page footer link with a call to action inviting readers to apply, since spotlights profile grantees. --- pages/spotlight/index.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pages/spotlight/index.tsx b/pages/spotlight/index.tsx index 0dfdeb934..5678dda0b 100644 --- a/pages/spotlight/index.tsx +++ b/pages/spotlight/index.tsx @@ -1,5 +1,6 @@ import { PageSEO } from '@/components/SEO' import siteMetadata from '@/data/siteMetadata' +import Link from '@/components/Link' import SpotlightCard from '@/components/SpotlightCard' import { kebabCase } from 'pliny/utils/kebabCase' import { sortedBlogPost, allCoreContent } from 'pliny/utils/contentlayer' @@ -54,6 +55,16 @@ export default function Spotlight({ ))}
)} + +
+ + Could you be next? Apply for funding → + +
) } From 7ddfbb868844cf5d7490a894b94b45f625366a29 Mon Sep 17 00:00:00 2001 From: Gigi Date: Tue, 7 Jul 2026 10:31:18 +0200 Subject: [PATCH 04/11] refactor: drop og thumbnail overlay from featured spotlight card The hero image alone reads better for the featured card. --- components/SpotlightCard.tsx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/components/SpotlightCard.tsx b/components/SpotlightCard.tsx index fa78c711a..6a928a4cf 100644 --- a/components/SpotlightCard.tsx +++ b/components/SpotlightCard.tsx @@ -41,19 +41,6 @@ export default function SpotlightCard({ post, featured = false }: Props) { className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/45 to-black/10" aria-hidden /> - {ogImage && ( -
-
- {title} -
-
- )}

Developer Spotlight From 86c6b594d85185abf443f6122869057f23a53c4c Mon Sep 17 00:00:00 2001 From: Gigi Date: Tue, 7 Jul 2026 10:32:18 +0200 Subject: [PATCH 05/11] feat: add donate link to spotlight page footer Pair the apply call to action with a donate link so readers can support the developers the spotlights profile. --- pages/spotlight/index.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pages/spotlight/index.tsx b/pages/spotlight/index.tsx index 5678dda0b..c73382cc0 100644 --- a/pages/spotlight/index.tsx +++ b/pages/spotlight/index.tsx @@ -56,7 +56,14 @@ export default function Spotlight({

)} -
+
+ + Help fund developers like these → + Date: Tue, 7 Jul 2026 11:01:06 +0200 Subject: [PATCH 06/11] style: stack spotlight footer links vertically Place the donate link above the apply link, both right-aligned. --- pages/spotlight/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/spotlight/index.tsx b/pages/spotlight/index.tsx index c73382cc0..580bc0af0 100644 --- a/pages/spotlight/index.tsx +++ b/pages/spotlight/index.tsx @@ -56,7 +56,7 @@ export default function Spotlight({
)} -
+
Date: Tue, 7 Jul 2026 11:01:32 +0200 Subject: [PATCH 07/11] style: order apply link above donate on spotlight page --- pages/spotlight/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pages/spotlight/index.tsx b/pages/spotlight/index.tsx index 580bc0af0..979a29a8f 100644 --- a/pages/spotlight/index.tsx +++ b/pages/spotlight/index.tsx @@ -58,18 +58,18 @@ export default function Spotlight({
- Help fund developers like these → + Could you be next? Apply for funding → - Could you be next? Apply for funding → + Help fund developers like these →
From afd80a0b833d302da05b0c13496ea0c28244aaf6 Mon Sep 17 00:00:00 2001 From: Gigi Date: Tue, 7 Jul 2026 11:04:49 +0200 Subject: [PATCH 08/11] feat: use compact spotlight card on mobile for featured post Show the OG-image card at the same size as the rest on mobile, and only switch to the hero background layout from md up. Extract shared compact and hero card variants to keep it DRY. --- components/SpotlightCard.tsx | 120 +++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 49 deletions(-) diff --git a/components/SpotlightCard.tsx b/components/SpotlightCard.tsx index 6a928a4cf..733d5fdce 100644 --- a/components/SpotlightCard.tsx +++ b/components/SpotlightCard.tsx @@ -14,62 +14,18 @@ interface Props { featured?: boolean } -export default function SpotlightCard({ post, featured = false }: Props) { +const cardClasses = + 'group block overflow-hidden rounded-lg border-2 border-gray-200 border-opacity-60 transition-colors hover:border-primary-500 dark:border-gray-700 dark:hover:border-primary-400' + +function CompactCard({ post }: { post: CoreContent }) { const { path, date, title, summary, images } = post const ogImage = getSpotlightOgImage(images) - if (featured) { - const heroImage = getSpotlightHeroImage(images) ?? ogImage - return ( - -
- {heroImage && ( - - )} -
-
-

- Developer Spotlight -

- -

- {title} -

- {summary && ( -

- {summary} -

- )} -
-
- - ) - } - return (
{ogImage && ( @@ -99,3 +55,69 @@ export default function SpotlightCard({ post, featured = false }: Props) { ) } + +function HeroCard({ post }: { post: CoreContent }) { + const { path, date, title, summary, images } = post + const heroImage = getSpotlightHeroImage(images) ?? getSpotlightOgImage(images) + + return ( + +
+ {heroImage && ( + + )} +
+
+

+ Developer Spotlight +

+ +

+ {title} +

+ {summary && ( +

+ {summary} +

+ )} +
+
+ + ) +} + +export default function SpotlightCard({ post, featured = false }: Props) { + if (!featured) { + return + } + + return ( + <> +
+ +
+
+ +
+ + ) +} From 80f4f93156058f269513ac326e01b315f2685026 Mon Sep 17 00:00:00 2001 From: Gigi Date: Tue, 7 Jul 2026 11:13:28 +0200 Subject: [PATCH 09/11] feat: add dedicated og image and structured data to spotlight page Generate a spotlight page OG card and pass the slug to PageSEO, and emit CollectionPage/ItemList JSON-LD so the page is treated as a collection. --- pages/spotlight/index.tsx | 32 +++++++++++++++++++++++++++++++- scripts/generate-page-og.mjs | 6 ++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pages/spotlight/index.tsx b/pages/spotlight/index.tsx index 979a29a8f..111a6d45f 100644 --- a/pages/spotlight/index.tsx +++ b/pages/spotlight/index.tsx @@ -1,3 +1,4 @@ +import Head from 'next/head' import { PageSEO } from '@/components/SEO' import siteMetadata from '@/data/siteMetadata' import Link from '@/components/Link' @@ -8,6 +9,9 @@ import { InferGetStaticPropsType } from 'next' import { allBlogs } from 'contentlayer/generated' import type { Blog } from 'contentlayer/generated' +const PAGE_DESCRIPTION = + 'Meet the developers OpenSats supports and the open-source work they are building.' + export const getStaticProps = async () => { const posts = allCoreContent( sortedBlogPost( @@ -26,12 +30,38 @@ export default function Spotlight({ }: InferGetStaticPropsType) { const [featured, ...rest] = posts + const structuredData = { + '@context': 'https://schema.org', + '@type': 'CollectionPage', + name: 'Developer Spotlights', + description: PAGE_DESCRIPTION, + url: `${siteMetadata.siteUrl}/spotlight`, + mainEntity: { + '@type': 'ItemList', + itemListElement: posts.map((post, index) => ({ + '@type': 'ListItem', + position: index + 1, + url: `${siteMetadata.siteUrl}/${post.path}`, + name: post.title, + })), + }, + } + return ( <> + +