Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f53912d
Update site for 2026, fix responsive layout, improve README
ashleywolf Mar 15, 2026
f2c37bf
Fix sticky nav, text clipping, and card width issues (#87)
ashleywolf Mar 15, 2026
b2237b5
Support TBD/All Day event times and multi-day events
ashleywolf Mar 15, 2026
cb57566
Archive 2025 events, add ICS support, accessibility fixes, update iss…
ashleywolf Mar 15, 2026
1870517
Fix Windows build, footer icon wrapping on mobile
ashleywolf Mar 15, 2026
b203079
Update partner pack page with 2026 call for partners
ashleywolf Mar 15, 2026
0aac0aa
Remove Security Challenge from navigation for 2026
ashleywolf Mar 15, 2026
473148f
Clear library resources for 2026
ashleywolf Mar 15, 2026
e53d5d3
Revert "Clear library resources for 2026"
ashleywolf Mar 15, 2026
5c6f252
Fix empty states, theme placeholder, remove council for 2026
ashleywolf Mar 15, 2026
cbb6476
Add Ships page for maintainer-relevant GitHub feature releases
ashleywolf Mar 15, 2026
a9e4d68
Add tests for ships data, event parsing, TBD/all-day times
ashleywolf Mar 15, 2026
96f6413
Add intro text to Ships page
ashleywolf Mar 15, 2026
6718d17
Fix ships page responsive edge cases
ashleywolf Mar 15, 2026
bfd536e
Clean up: remove yarn.lock, fix warnings, update configs and archives
ashleywolf Mar 15, 2026
4ca641a
Fix event parsing, ships component, and dependency updates
ashleywolf Mar 19, 2026
b0f4284
Fix build: handle missing content/events dir, pin serve dependency
ashleywolf Mar 19, 2026
8bd8d47
Fix remaining 2025 references in commons.json
ashleywolf Mar 21, 2026
a0f815f
Update content/partner-pack/offers/jsconf.md
ashleywolf Mar 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/ISSUE_TEMPLATE/add-to-calendar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,19 @@ body:
type: input
attributes:
label: Start time in UTC
description: 'Use HH:MM format (e.g., 14:00). Enter "TBD" if time is not yet confirmed, or "all-day" for all-day events.'
placeholder: '14:00'
validations:
required: true
required: false

- id: UTCEndTime
type: input
attributes:
label: End time in UTC
description: 'Use HH:MM format (e.g., 15:00). Leave blank if start time is TBD or all-day.'
placeholder: '15:00'
validations:
required: true
required: false

- id: type
type: dropdown
Expand All @@ -61,6 +63,7 @@ body:
- talk
- meetup
- fundraising
- workshop
- misc
validations:
required: true
Expand Down
10 changes: 6 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Need to run the website locally for testing? → [Jump to Development Guidelines
### Adding a New Event

1. Navigate to the `content/events/` folder
2. Create a new markdown file with a descriptive name (e.g., `2025-05-20-your-event-name.md`)
2. Create a new markdown file with a descriptive name (e.g., `2026-05-20-your-event-name.md`)
3. Use the following template:

```markdown
Expand All @@ -51,6 +51,7 @@ title: 'Your Event Title'
metaTitle: 'Your Event Title'
metaDesc: 'A brief description of your event'
date: 'MM/DD'
endDate: 'MM/DD'
UTCStartTime: 'HH:MM'
UTCEndTime: 'HH:MM'
type: 'meetup'
Expand All @@ -64,7 +65,7 @@ linkUrl: 'https://link-to-event.com'
Detailed description of your event goes here. You can use markdown formatting.
```

4. All frontmatter fields (between `---`) are mandatory
> **Note:** `UTCStartTime` and `UTCEndTime` are optional. If omitted, the event will display "TBD" for the time. You can also set them to `'TBD'` explicitly or use `'all-day'` for events that span an entire day. Use `endDate` for multi-day events.
5. Submit a PR with your changes

### Adding a New Resource
Expand Down Expand Up @@ -175,8 +176,9 @@ For significant changes like:
- `metaTitle`: Title for SEO meta tags
- `metaDesc`: Description for SEO meta tags
- `date`: Event date in `MM/DD` format
- `UTCStartTime`: Start time in UTC, in `HH:MM` format
- `UTCEndTime`: End time in UTC, in `HH:MM` format
- `endDate`: _(optional)_ End date in `MM/DD` format, for multi-day events (e.g., a conference running May 16-18 would use `date: '05/16'` and `endDate: '05/18'`). Multi-day events display a date range and remain listed as upcoming until the end date passes.
- `UTCStartTime`: _(optional)_ Start time in UTC, in `HH:MM` format. Omit or set to `'TBD'` if the time is not yet confirmed. Set to `'all-day'` for events without a specific start time.
- `UTCEndTime`: _(optional)_ End time in UTC, in `HH:MM` format. Same rules as `UTCStartTime`.
- `type`: One of: `podcast`, `stream`, `talk`, `meetup`, `fundraising`, `conference`, `misc`
- `language`: Primary language of the event
- `location`: `Virtual` or physical location
Expand Down
4 changes: 2 additions & 2 deletions Participation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
...a month for open source maintainers to gather, share, and be celebrated.
Open source runs the world, but who runs open source? Open source maintainers are behind the software we use everyday, but they don't always have the community or support they need. That's why we're celebrating open source maintainers all month long.

The 2025 Maintainer Month is, of course, in **May**! You're invited to participate in whatever ways you'd like, and list what you're doing on the public [Maintainer Month site](https://maintainermonth.github.com/).
The 2026 Maintainer Month is, of course, in **May**! You're invited to participate in whatever ways you'd like, and list what you're doing on the public [Maintainer Month site](https://maintainermonth.github.com/).

## Themes for 2025
## Themes for 2026

**Theme for Maintainers**

Expand Down
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Repository for the official GitHub Maintainer Month website. You can access the

**Add your event!** See the [contributing guide](CONTRIBUTING.md) for details on how.

**<p align="center"> ❇️ May 2025 ❇️ </p>**
**<p align="center"> ❇️ May 2026 ❇️ </p>**

## Table of Contents

Expand All @@ -20,32 +20,30 @@ Repository for the official GitHub Maintainer Month website. You can access the

## Getting Started

### Installation
Requires [Node.js](https://nodejs.org/) v20 or later.

Run the following command before any other to install all the project's dependencies.
### Installation

```
npm install
```

### Usage

To start application in development mode at [http://localhost:3000](http://localhost:3000) run the following command.
Start the development server at [http://localhost:3000](http://localhost:3000):

```
npm start
npm run dev
```

### Build

To generate the application build run the following command
Generate a production build (static export to the `out` folder):

```
npm run build
```

This will create an `out` folder in the repository root with the static files.

## Contributing

See the [contributing guide](CONTRIBUTING.md) for more details.
Expand All @@ -60,5 +58,5 @@ Maintainer Month was created, and is maintained, by GitHub, starting in 2021 as

## License

Copyright © 2025 [GitHub](https://github.com/github).<br />
Copyright © 2026 [GitHub](https://github.com/github).<br />
This project is [MIT](LICENSE) licensed.
88 changes: 88 additions & 0 deletions api/calendar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { createEvents } from 'ics'

const EVENT_YEAR = 2026

const isTBDValue = (value) =>
!value || (typeof value === 'string' && value.toUpperCase() === 'TBD')

const isAllDayValue = (value) =>
typeof value === 'string' &&
value.toLowerCase().replace(/[\s\-_]/g, '') === 'allday'

function parseDateParts(dateStr) {
const [month, day] = dateStr.split('/').map(Number)
return { month, day }
}

function toICSEvent(event) {
const { month: startMonth, day: startDay } = parseDateParts(event.date)

const hasTime =
!isTBDValue(event.UTCStartTime) && !isAllDayValue(event.UTCStartTime)

const icsEvent = {
title: event.title,
description: event.metaDesc || '',
location: event.location || '',
url: event.linkUrl || '',
calName: 'Maintainer Month 2026',
}

if (hasTime) {
const [startHour, startMinute] = event.UTCStartTime.split(':').map(Number)
icsEvent.start = [EVENT_YEAR, startMonth, startDay, startHour, startMinute]
icsEvent.startInputType = 'utc'

if (!isTBDValue(event.UTCEndTime) && !isAllDayValue(event.UTCEndTime)) {
const [endHour, endMinute] = event.UTCEndTime.split(':').map(Number)

if (event.endDate && event.endDate !== event.date) {
const { month: endMonth, day: endDay } = parseDateParts(event.endDate)
icsEvent.end = [EVENT_YEAR, endMonth, endDay, endHour, endMinute]
} else {
icsEvent.end = [EVENT_YEAR, startMonth, startDay, endHour, endMinute]
}
icsEvent.startOutputType = 'utc'
icsEvent.endInputType = 'utc'
icsEvent.endOutputType = 'utc'
} else {
icsEvent.duration = { hours: 1 }
}
} else {
// All-day or TBD: treat as all-day event
icsEvent.start = [EVENT_YEAR, startMonth, startDay]
icsEvent.startInputType = 'utc'

if (event.endDate && event.endDate !== event.date) {
const { month: endMonth, day: endDay } = parseDateParts(event.endDate)
// ICS all-day end date is exclusive, so add one day
const endDate = new Date(Date.UTC(EVENT_YEAR, endMonth - 1, endDay + 1))
icsEvent.end = [
endDate.getUTCFullYear(),
endDate.getUTCMonth() + 1,
endDate.getUTCDate(),
]
} else {
// Use Date math to handle month boundary rollover
const endDate = new Date(Date.UTC(EVENT_YEAR, startMonth - 1, startDay + 1))
icsEvent.end = [
endDate.getUTCFullYear(),
endDate.getUTCMonth() + 1,
endDate.getUTCDate(),
]
}
}

return icsEvent
}

export function generateICS(events) {
const icsEvents = events.map(toICSEvent)
const { error, value } = createEvents(icsEvents)

if (error) {
throw new Error(`Failed to generate ICS: ${error.message}`)
}

return value
}
48 changes: 36 additions & 12 deletions api/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,18 @@ dayjs.extend(utc)
dayjs.extend(timezone)

const TBD = 'TBD'
const ALL_DAY = 'All Day'

const isTBDValue = (value) =>
!value || (typeof value === 'string' && value.toUpperCase() === 'TBD')

const isAllDayValue = (value) =>
typeof value === 'string' &&
value.toLowerCase().replace(/[\s\-_]/g, '') === 'allday'

export const getEvents = (year) => {
const events_path = year ? `content/${year}/events` : 'content/events'
if (!fs.existsSync(events_path)) return []
const eventFiles = fs.readdirSync(events_path)

const events = eventFiles
Expand Down Expand Up @@ -108,27 +117,38 @@ const formatEventDateTime = (
formattedEndDate = endUTCDate.format('MMM D')
}

// All-day events
if (isAllDayValue(startTime) || isAllDayValue(endTime)) {
return {
date: formattedDate,
startTime: { utc: ALL_DAY, pt: ALL_DAY },
endTime: { utc: ALL_DAY, pt: ALL_DAY },
endDate: formattedEndDate,
timeDisplay: 'all-day',
}
}

// Start time
let formattedStartTime = {
utc: TBD,
pt: TBD,
}

if (startTime) {
let hasSpecificStartTime = false

if (!isTBDValue(startTime)) {
const [startHour, startMinute] = startTime.split(':')

const UTCStartTime = UTCDate.hour(startHour).minute(startMinute)

if (!isNaN(UTCStartTime)) {
const PTStartTime = UTCStartTime.tz('America/Los_Angeles')

const formattedUTCStartTime = UTCStartTime.format('HH:mm a')
const formattedPTStartTime = PTStartTime.format('HH:mm a')

formattedStartTime = {
utc: formattedUTCStartTime,
pt: formattedPTStartTime,
utc: UTCStartTime.format('h:mm a'),
pt: PTStartTime.format('h:mm a'),
}
hasSpecificStartTime = true
}
}

Expand All @@ -138,28 +158,32 @@ const formatEventDateTime = (
pt: TBD,
}

if (endTime && endTime !== TBD) {
let hasSpecificEndTime = false

if (!isTBDValue(endTime)) {
const [endHour, endMinute] = endTime.split(':')

const UTCEndTime = UTCDate.hour(endHour).minute(endMinute)

if (!isNaN(UTCEndTime)) {
const PTEndTime = UTCEndTime.tz('America/Los_Angeles')

const formattedUTCEndTime = UTCEndTime.format('HH:mm a')
const formattedPTEndTime = PTEndTime.format('HH:mm a')

formattedEndTime = {
utc: formattedUTCEndTime,
pt: formattedPTEndTime,
utc: UTCEndTime.format('h:mm a'),
pt: PTEndTime.format('h:mm a'),
}
hasSpecificEndTime = true
}
}

const timeDisplay =
hasSpecificStartTime || hasSpecificEndTime ? 'specific' : 'tbd'

return {
date: formattedDate,
startTime: formattedStartTime,
endTime: formattedEndTime,
endDate: formattedEndDate,
timeDisplay,
}
}
2 changes: 2 additions & 0 deletions common/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const Y2024 = makePath('/2024')

export const NEWS = makePath("/news")

export const SHIPS = makePath('/ships')

export const PARTNER_PACK = makePath('/partner-pack')

export const SECURITY_CHALLENGE = makePath('/security-challenge')
4 changes: 3 additions & 1 deletion components/chip/chip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
display: inline-flex;
align-items: center;
column-gap: 6px;
flex-shrink: 0;
white-space: nowrap;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;

background-color: $white;
@extend %subtitle-1;
Expand Down
Loading
Loading