Skip to content

Commit e21af31

Browse files
committed
Happy pride month
1 parent d1b0baa commit e21af31

2 files changed

Lines changed: 94 additions & 31 deletions

File tree

src/layouts/BaseLayout.astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
import '../styles/global.css';
3+
import SeasonalBanner from '../components/SeasonalBanner.tsx';
34
import ConditionalSnow from '../components/ConditionalSnow.tsx';
45
import ConditionalChristmas from '../components/ConditionalChristmas.tsx';
56
import ConditionalOcean from '../components/ConditionalOcean.tsx';
@@ -38,6 +39,7 @@ const { title, description = 'IPython - Productive Interactive Computing' } = As
3839
</script>
3940
</head>
4041
<body class="bg-white dark:bg-ipython-dark text-gray-900 dark:text-gray-50">
42+
<SeasonalBanner client:load />
4143
<slot />
4244
<ConditionalSnow client:load />
4345
<ConditionalChristmas client:load />

src/lib/themeUtils.ts

Lines changed: 92 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -274,26 +274,83 @@ export function getCurrentTheme(): string {
274274
}
275275

276276
/**
277-
* Check if we should auto-apply winter theme
278-
* Returns true if current month is December or January,
279-
* and the last theme change was NOT in December or January
277+
* Configuration for a seasonal period.
278+
* - id: stable identifier for the season (used to remember banner dismissal)
279+
* - themeId: the color theme auto-applied during this season
280+
* - banner: dismissable banner text shown during this season (empty string = no banner)
280281
*/
281-
function shouldAutoApplyWinterTheme(): boolean {
282-
if (typeof localStorage === "undefined") {
283-
return true;
284-
}
282+
export interface SeasonalConfig {
283+
id: string;
284+
themeId: string;
285+
banner: string;
286+
}
287+
288+
/**
289+
* Seasonal configuration keyed by month (0 = January, 11 = December).
290+
* Edit the `banner` text here to change what the seasonal banner says.
291+
*/
292+
export const seasonalConfigs: Record<number, SeasonalConfig> = {
293+
0: {
294+
id: "winter",
295+
themeId: "winter",
296+
banner: "❄️ Happy holidays from the IPython team!",
297+
},
298+
5: {
299+
id: "pride",
300+
themeId: "rainbow",
301+
banner:
302+
"🏳️‍🌈 Happy Pride Month! IPython celebrates and supports our diverse community.",
303+
},
304+
11: {
305+
id: "winter",
306+
themeId: "winter",
307+
banner: "❄️ Happy holidays from the IPython team!",
308+
},
309+
};
285310

311+
/**
312+
* Get the seasonal configuration for a given month, or null if none.
313+
*/
314+
export function getSeasonalConfigForMonth(month: number): SeasonalConfig | null {
315+
return seasonalConfigs[month] ?? null;
316+
}
317+
318+
/**
319+
* Get the current month's seasonal configuration, or null if none.
320+
*/
321+
export function getCurrentSeasonalConfig(): SeasonalConfig | null {
322+
return getSeasonalConfigForMonth(new Date().getMonth());
323+
}
324+
325+
/**
326+
* Get the seasonal theme for a given month (0 = January, 11 = December),
327+
* or null if that month has no seasonal theme.
328+
*/
329+
function getSeasonalThemeForMonth(month: number): string | null {
330+
return getSeasonalConfigForMonth(month)?.themeId ?? null;
331+
}
332+
333+
/**
334+
* Determine which seasonal theme should be auto-applied right now, if any.
335+
* Returns the seasonal theme id (e.g. "winter" or "rainbow") when the current
336+
* month has one and the user has NOT manually picked a theme during the same
337+
* season; otherwise returns null.
338+
*/
339+
function getAutoSeasonalTheme(): string | null {
286340
const now = new Date();
287-
const currentMonth = now.getMonth(); // 0 = January, 11 = December
288-
const isDecOrJan = currentMonth === 11 || currentMonth === 0;
341+
const seasonalTheme = getSeasonalThemeForMonth(now.getMonth());
289342

290-
if (!isDecOrJan) {
343+
if (!seasonalTheme) {
291344
console.info("Nothing specific using default theme");
292-
return false;
345+
return null;
346+
}
347+
348+
if (typeof localStorage === "undefined") {
349+
return seasonalTheme;
293350
}
294351

295352
const stored = localStorage.getItem("colorTheme");
296-
if (!stored) return true; // No stored theme, apply winter
353+
if (!stored) return seasonalTheme; // No stored theme, apply seasonal
297354

298355
try {
299356
const parsed = JSON.parse(stored);
@@ -308,19 +365,21 @@ function shouldAutoApplyWinterTheme(): boolean {
308365
}
309366

310367
if (storedDate) {
311-
const storedMonth = storedDate.getMonth();
312-
const storedIsDecOrJan = storedMonth === 11 || storedMonth === 0;
313-
// If stored date is NOT in Dec/Jan, auto-apply winter
314-
return !storedIsDecOrJan;
368+
// If the stored theme was chosen during the current season, respect the
369+
// user's choice; otherwise auto-apply the seasonal theme.
370+
const storedSeasonalTheme = getSeasonalThemeForMonth(
371+
storedDate.getMonth()
372+
);
373+
return storedSeasonalTheme === seasonalTheme ? null : seasonalTheme;
315374
}
316375
} catch {
317376
console.log("Something wrong in local storage");
318377

319-
// If parsing fails, assume old format - apply winter
320-
return true;
378+
// If parsing fails, assume old format - apply seasonal
379+
return seasonalTheme;
321380
}
322381

323-
return false;
382+
return seasonalTheme;
324383
}
325384

326385
/**
@@ -444,15 +503,16 @@ export function initializeThemeWatcher(): () => void {
444503
themeWatcherInitialized = true;
445504

446505
const checkTheme = () => {
447-
// Check if we should auto-apply winter theme first
448-
if (shouldAutoApplyWinterTheme()) {
506+
// Check if we should auto-apply a seasonal theme first
507+
const seasonalTheme = getAutoSeasonalTheme();
508+
if (seasonalTheme) {
449509
const currentApplied = getCurrentTheme();
450-
// Only apply winter if it's not already applied
451-
if (currentApplied !== "winter") {
452-
applyTheme("winter", false); // Apply winter but don't store it
453-
notifyThemeChange(getStoredTheme()); // Notify with stored theme, not 'winter'
510+
// Only apply the seasonal theme if it's not already applied
511+
if (currentApplied !== seasonalTheme) {
512+
applyTheme(seasonalTheme, false); // Apply seasonal theme but don't store it
513+
notifyThemeChange(getStoredTheme()); // Notify with stored theme, not the seasonal one
454514
}
455-
return; // Don't check stored theme when winter is auto-applied
515+
return; // Don't check stored theme when a seasonal theme is auto-applied
456516
}
457517

458518
const storedTheme = getStoredTheme();
@@ -533,11 +593,12 @@ export function initializeThemeWatcher(): () => void {
533593

534594
// Apply initial theme (only if no valid theme parameter was provided)
535595
if (!themeParamApplied) {
536-
// Check if we should auto-apply winter theme
537-
if (shouldAutoApplyWinterTheme()) {
538-
console.log("will apply winter");
539-
applyTheme("winter", false); // Apply winter but don't store it
540-
notifyThemeChange("winter"); // Notify with the stored theme, not 'winter'
596+
// Check if we should auto-apply a seasonal theme
597+
const seasonalTheme = getAutoSeasonalTheme();
598+
if (seasonalTheme) {
599+
console.log("will apply seasonal theme", seasonalTheme);
600+
applyTheme(seasonalTheme, false); // Apply seasonal theme but don't store it
601+
notifyThemeChange(seasonalTheme);
541602
} else {
542603
const initialTheme = getStoredTheme();
543604
applyTheme(initialTheme);

0 commit comments

Comments
 (0)