Skip to content

Add monthly newsletter page#4082

Open
ewels wants to merge 64 commits into
mainfrom
claude/nf-core-newsletter-page-XyTgs
Open

Add monthly newsletter page#4082
ewels wants to merge 64 commits into
mainfrom
claude/nf-core-newsletter-page-XyTgs

Conversation

@ewels

@ewels ewels commented Mar 23, 2026

Copy link
Copy Markdown
Member

Create a newsletter page at /newsletter/[year]/[month] that aggregates
monthly community content for email distribution:

  • Events this month + upcoming events (next 2 months)
  • Blog posts this month + "in case you missed it" (past 2 months)
  • Pipeline releases with highlighted first releases
  • New pipeline repositories and approved proposals from nf-core/proposals

Uses email-friendly HTML (table-based layout, inline styles, 600px max-width)
inside a #newsletter-content div for easy extraction. Month/year selector
navigation sits outside this div.

https://claude.ai/code/session_01DHK3ARtaSYevNH85QYBDBD

@netlify /newsletter

@netlify

netlify Bot commented Mar 23, 2026

Copy link
Copy Markdown

Deploy Preview for nf-core-main-site ready!

Name Link
🔨 Latest commit 0ed89b3
🔍 Latest deploy log https://app.netlify.com/projects/nf-core-main-site/deploys/6a37f4a4625534000876d641
😎 Deploy Preview https://deploy-preview-4082--nf-core-main-site.netlify.app/newsletter
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot deleted a comment from netlify Bot Mar 23, 2026
@ewels ewels marked this pull request as draft March 23, 2026 11:45
@ewels

ewels commented Mar 23, 2026

Copy link
Copy Markdown
Member Author

@nf-core-bot fix linting

return `/newsletter/${m.year}/${String(m.month).padStart(2, "0")}`;
}

function blogUrl(postId: string): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you get that through content collections already I think

Comment thread sites/main-site/src/pages/newsletter/[year]/[month].astro Outdated
title: "Empowering bioinformatics communities with Nextflow and nf-core"
subtitle: "Next nf-core scientific article now published in Genome Biology!"
headerImage: https://images.unsplash.com/photo-1532094349884-543bc11b234d
headerImageAlt: Scientific journal article with data visualizations

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the alt text is not describing what is in the photo...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll let you know when I've reviewed my own code 😅

I haven't read it at all yet - just trying to get the end result to roughly the right place first before going back, simplifying, reviewing and getting ready for review/merge.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, sorry, I know, I will stop looking at this draft 🙂. I just got confused while looking at the deploy preview 😀

@mashehu mashehu force-pushed the claude/nf-core-newsletter-page-XyTgs branch from 8855524 to a34c3d9 Compare April 10, 2026 11:44
claude and others added 22 commits June 20, 2026 22:19
Create a newsletter page at /newsletter/[year]/[month] that aggregates
monthly community content for email distribution:

- Events this month + upcoming events (next 2 months)
- Blog posts this month + "in case you missed it" (past 2 months)
- Pipeline releases with highlighted first releases
- New pipeline repositories and approved proposals from nf-core/proposals

Uses email-friendly HTML (table-based layout, inline styles, 600px max-width)
inside a #newsletter-content div for easy extraction. Month/year selector
navigation sits outside this div.

https://claude.ai/code/session_01DHK3ARtaSYevNH85QYBDBD
- Reorder events: upcoming events (next 2 months) is now the primary
  section, events this month shown smaller as secondary
- Add nf-core/newsletter logo PNG generated from old-site base
  templates (light + dark background versions)
- Force white background on #newsletter-content div so it's always
  readable regardless of web dark mode or email client dark mode
- Logo displayed at top of newsletter using the light background variant

https://claude.ai/code/session_01DHK3ARtaSYevNH85QYBDBD
- Add @media (prefers-color-scheme: dark) overrides for Apple Mail,
  Outlook Mac, and web browsers
- Add [data-ogsc] selectors for Outlook iOS/Android apps
- Swap logo images: light bg logo by default, dark bg logo in dark mode
- Add nl-muted CSS class to secondary text elements for dark mode
  targeting with !important overrides over inline styles
- Light mode inline styles serve as fallback for Gmail and other
  clients that strip <style> blocks

https://claude.ai/code/session_01DHK3ARtaSYevNH85QYBDBD
Fetch logos from https://oldsite.nf-co.re/logo?t=newsletter
instead of generating locally with incorrect font sizes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…heme

The nf-core site uses Bootstrap's data-bs-theme attribute on <html>
toggled via ThemeSwitch.svelte, not @media (prefers-color-scheme).
The newsletter CSS was using the wrong selector, causing it to
respond to OS dark mode setting instead of the site's theme toggle.

Now uses :global([data-bs-theme="dark"]) for the web page, while
keeping @media (prefers-color-scheme: dark) and [data-ogsc] for
email client dark mode support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The data-bs-theme dark mode rules need !important to beat inline
styles, and child class selectors (.nl-logo-light, .nl-logo-dark,
.nl-muted) need :global() wrappers to prevent Astro from scoping
them with hash attributes.

Tested locally with dev server in both light and dark modes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Multiple releases of the same pipeline in a month (e.g. pixelator
v3.0.0 and v3.0.1) are now shown as a single row with multiple
version badges, using the most recent release date.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Split proposals into "Pipeline Proposals" (strip "New pipeline:"
  prefix) and "Other Proposals" (RFCs, SIGs, etc. keep their prefix)
- Show status badges: "Accepted" (green) for closed issues,
  "New" (blue) for issues opened that month
- Include both newly opened and closed/accepted issues for the month
- Sort accepted proposals first within each group

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…erImage

Layout changes:
- Widen email from 600px to 800px
- Primary content (upcoming events, blog posts, first releases) is full-width
- Secondary content (this month's events, older blogs, pipeline releases,
  proposals) uses 2-column table layout for visual hierarchy
- Blog posts show header images with clickable links

Blog images:
- Make headerImage and headerImageAlt required in blog content schema
- Add headerImage to the one blog post missing it (2020/data_management.md)
- Add blogImageUrl() helper for unsplash size params

Other fixes:
- Sort release tags ascending (left to right) so latest tag + date are adjacent

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reorder sections to: blog posts, first releases, pipeline releases,
upcoming events, events this month, older blog posts, pipelines &
proposals.

Add missing headerImage + headerImageAlt to:
- 2025/paper-v2.mdx
- 2026/configs-strict-syntax.mdx
- 2026/statement-on-ai.mdx

These were failing the build after headerImage was made required.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add optional headerImage/headerImageAlt fields to events schema
- Events with headerImage show it in the newsletter's upcoming events
- Events without headerImage fall back to the dynamic OG social card
- Add headerImage to hackathon-boston event using existing summit card

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove OG social card fallback for events without a headerImage.
Events without an explicit headerImage just show text, no image.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Local /assets/ paths don't exist as-is on the deployed site — they
get processed through Astro's build pipeline into /_astro/ URLs.
Use import.meta.glob to resolve the built URLs, matching how the
blog page handles headerImage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The newsletter is at src/pages/newsletter/[year]/[month].astro (4
levels deep from src/), not 2 like the blog page. Fix the relative
glob path and key prefix accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Glob-resolved assets return relative paths (/_astro/...) that work
on any domain. Remove the nf-co.re fallback which broke deploy
previews and hit the /assets/* → oldsite redirect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The glob returns lazy loaders that need await, which can't be done
in the template. Pre-resolve all blog and event image URLs into
Maps during frontmatter execution, then look them up synchronously
in the template.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tended

In light mode, most links are dark (#333/#555) with only a few
accent links in green. The dark mode was turning ALL links green
via the blanket `a { color: #4ade80 }` rule.

Now dark mode links default to light text (#e0e0e0) matching the
body, with green reserved for elements that use the `nl-green`
class: first-release pipeline names, proposals link, and footer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Make "Events That Happened This Month" and "In Case You Missed It"
  headings green (h2 style) so they stand out from list content
- Upcoming events: 2-column layout with title left, date + tag
  right-aligned; full-width header image above when present
- Switch to Astro <Image> component for blog and event images
  to get proper asset pipeline URLs (.netlify/images)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The relative ../../../../assets/** glob wasn't resolving correctly.
Use Vite's root-relative /src/assets/** pattern instead, which
reliably resolves regardless of file depth.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GitHub Issues have a state_reason field: "completed" or "not_planned".
Only proposals closed as "completed" should appear as "Accepted" in
the newsletter. Rejected proposals (closed as "not_planned") are
now excluded entirely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ewels and others added 22 commits June 20, 2026 22:21
…, subtitle below

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…width

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move border-bottom from table to td elements so the existing dark mode
CSS rule (#newsletter-content td border-bottom-color) applies correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Applies to pipeline releases, recent events, older blog posts,
and proposals sections.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add advisories section to newsletter (after blog posts, with severity badges)
- Add RSS feed at /newsletter/rss.xml with per-month content summaries
- Add bare HTML email version at /newsletter/[year]/[month]/email
- Add Newsletter link to Community dropdown nav after Blog
- Extract newsletter content into shared NewsletterContent.astro component
- Extract shared data-fetching helpers (getNewsletterStaticPathsData,
  getNewsletterContentData) to avoid duplication between web and email pages
- Fix pre-existing TS errors in fetchAllProposals (octokit.paginate typing,
  Proposal vs RawProposal return type)
- Add newsletter RSS <link> tag in BaseHead for newsletter pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Astro hoists getStaticPaths and may not have access to module-level
variables declared outside it during the build. Declare pipelines
inside getStaticPaths and again in the per-page section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add getAdvisoriesForPreviousMonths() helper for past 2 months
- Add olderAdvisories to newsletter content data
- Remove 'Recent events', 'Older blog posts' subheadings from ICYMI section
- Add older advisories 2-column list below older blog posts in ICYMI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add responsive 2-column layout: @media query stacks .nl-col cells
  on narrow screens (<600px) for mobile email clients
- Add role="presentation" to all layout tables for accessibility
- Add aria-label to image-only links
- Fix email page body styles (margin: 0; padding: 0)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace table-cell 2-column layout with inline-block divs that
naturally stack on narrow screens without @media queries. Each column
uses display: inline-block; width: 100%; max-width: 50% — on wide
screens two fit side-by-side, on narrow screens they wrap to full
width. @media query kept as enhancement for supporting clients.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Astro <Image> component routes through Netlify's image service
(/.netlify/images?url=...) which produces relative URLs that break
in email clients. Replace all <Image> with plain <img> tags using the
asset's .src property directly, which is already absolute thanks to
Astro's assetsPrefix config. All images now render consistently —
same approach as the logo images which already worked.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move getAdvisoryBadgeColors() calls and regex-based URL construction
out of JSX template literals into map callback variables. The complex
expressions (function().property and regex) inside template literals
inside JSX attributes confused esbuild's parser.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace table-cell 2-column with standalone div containers using
inline-block + max-width: 50%. Stacks naturally on narrow screens
without @media queries (Gmail compatible). Previous attempt using
divs inside tables caused esbuild parse errors in Astro compilation.

Also replaces <Image> with plain <img> for consistent absolute URLs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add div to dark mode border-bottom-color rules alongside td, since
the fluid hybrid 2-column sections now use div instead of td.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace aria-label with meaningful alt text on img tags (email
  clients don't support aria-label, but alt text counts as link text)
- Add explicit height attribute to event images

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reduce max-width from 50% to 49% to create visible spacing between
side-by-side columns, separating the divider lines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The @media query needs both max-width: 100% (to override inline 49%)
and display: block (to force stacking). Also use #newsletter-content
.nl-col selector in email page for specificity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace table layout with inline-block divs for blog posts. Text
block uses max-width: calc(100% - 256px) so the image naturally
wraps below on narrow screens without media queries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use inline-block divs with max-width instead of fixed width for blog
post text/image columns. Add nl-blog-text and nl-blog-img classes to
@media query for stacking on narrow screens.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The rebase auto-merge of "Reorder newsletter sections, fix missing
headerImage in 3 blog posts" left both main's and the branch's
headerImage/headerImageAlt keys in statement-on-ai.md, breaking the
YAML frontmatter parse. Keep main's canonical image + alt text and the
branch's embedHeaderImage flag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UYthwAzUGhSqZc1FQGFf4p
@ewels ewels force-pushed the claude/nf-core-newsletter-page-XyTgs branch from a34c3d9 to 1f7cb09 Compare June 20, 2026 20:25
ewels and others added 3 commits June 20, 2026 23:10
- Resolve blog/event header images with Promise.all instead of two
  sequential await loops per generated month
- Type allProposals as RawProposal[] instead of any[] in newsletter.ts
  and [month].astro
- Extract the duplicated proposal-status sort comparator into a single
  sortByStatus helper

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UYthwAzUGhSqZc1FQGFf4p
…lic/

- email.astro: use `<style is:inline>` so the responsive media query ships
  inline and unscoped. Email clients drop external stylesheets (where Astro
  bundled the component CSS), and Astro scoping stopped the inline rule from
  matching the NewsletterContent elements, so columns never stacked on mobile.
- Add nf-core/newsletter logos to public/images/logo/ for a stable asset URL
  usable from transactional emails.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jf58W9P1cwUmj2XekE6iMU
@ewels ewels marked this pull request as ready for review June 21, 2026 09:11
ewels and others added 3 commits June 21, 2026 11:28
…e, homepage CTA

- NewsletterSignup.astro: reusable email sign-up form (Bootstrap input-group);
  POSTs { email } to the newsletter service (Amazon SES double opt-in) with
  inline success/error states. Re-init on astro:after-swap; supports multiple
  instances per page.
- /newsletter/subscribe: dedicated page with the form plus "what you'll get",
  "how it works" (double opt-in) and a privacy note.
- Homepage: a "Stay in the loop" sign-up section above the contributor carousel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jf58W9P1cwUmj2XekE6iMU
Review feedback (@mashehu): the alt text described "a journal article" but the
header photo is lab glassware. Now describes the actual image (beakers, a
conical flask and a pipette).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jf58W9P1cwUmj2XekE6iMU
Adds "Unsubscribe here" to the community footer in the email, carrying the SES
{{amazonSESUnsubscribeUrl}} placeholder that SES replaces at send time. Gated
behind a new `email` prop so the public web newsletter page doesn't show the
placeholder/link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jf58W9P1cwUmj2XekE6iMU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants