Skip to content

Commit a53c05b

Browse files
committed
add date gates
1 parent 0e845a2 commit a53c05b

3 files changed

Lines changed: 130 additions & 10 deletions

File tree

src/components/Header.astro

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import HeaderLogo from "@components/header/header-logo.astro";
33
import Search from "@components/Search.astro";
44
import ThemeToggle from "@components/ThemeToggle.astro";
5+
import DateGate from "@ui/DateGate.astro";
56
67
import { NAV_MENUS } from "@data/nav";
78
import { buildLinkChecker } from "@utils/nav";
@@ -111,12 +112,24 @@ const activeMenus = NAV_MENUS.map((menu) => ({
111112
),
112113
)
113114
}
114-
<li class="nav-item nav-mobile-cta">
115-
<a class="nav-link-btn" href="/jobs">Jobs</a>
116-
</li>
117-
<li class="nav-item nav-mobile-cta">
118-
<a class="nav-link-btn" href="/tickets">Register Now</a>
119-
</li>
115+
<DateGate before="2026-07-20">
116+
<Fragment slot="before">
117+
<li class="nav-item nav-mobile-cta">
118+
<a class="nav-link-btn" href="/jobs">Jobs</a>
119+
</li>
120+
<li class="nav-item nav-mobile-cta">
121+
<a class="nav-link-btn" href="/tickets">Register Now</a>
122+
</li>
123+
</Fragment>
124+
<Fragment slot="during">
125+
<li class="nav-item nav-mobile-cta">
126+
<a class="nav-link-btn" href="/jobs">Jobs</a>
127+
</li>
128+
<li class="nav-item nav-mobile-cta">
129+
<a class="nav-link-btn" href="/tickets">Register Now</a>
130+
</li>
131+
</Fragment>
132+
</DateGate>
120133
</ul>
121134

122135
<button
@@ -160,8 +173,21 @@ const activeMenus = NAV_MENUS.map((menu) => ({
160173
</svg>
161174
</a>
162175
<ThemeToggle />
163-
<a href="/jobs" class="nav-cta-desktop">Jobs</a>
164-
<a href="/tickets" class="nav-cta">Register Now</a>
176+
<DateGate before="2026-07-20">
177+
<Fragment slot="before">
178+
<a href="/jobs" class="nav-cta-desktop">Jobs</a>
179+
<a href="/tickets" class="nav-cta">Register Now</a>
180+
</Fragment>
181+
<Fragment slot="during">
182+
<a href="/jobs" class="nav-cta-desktop">Jobs</a>
183+
<a href="/tickets" class="nav-cta">Register Now</a>
184+
</Fragment>
185+
<Fragment slot="after">
186+
<span class="nav-cta" style="opacity:0.5;cursor:default"
187+
>See you next year!</span
188+
>
189+
</Fragment>
190+
</DateGate>
165191
<button
166192
class="nav-toggle"
167193
aria-label="Toggle menu"

src/components/sections/hero/hero.astro

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
2+
import DateGate from "@ui/DateGate.astro";
33
---
44

55
<section class="hero-main">
@@ -42,7 +42,10 @@ PYTHON">
4242
> 18–23 July
4343
</p>
4444
<div class="hero-cta-row">
45-
<a href="/tickets" class="hero-cta">Register Now</a>
45+
<DateGate before="2026-07-20">
46+
<a slot="before" href="/tickets" class="hero-cta">Register Now</a>
47+
<a slot="during" href="/tickets" class="hero-cta">Register Now</a>
48+
</DateGate>
4649
<a href="#programme" class="hero-cta-alt">View Programme</a>
4750
</div>
4851
<div class="hero-flowers" aria-hidden="true">
@@ -362,6 +365,18 @@ PYTHON">
362365
inset 0 0 20px oklch(0.831 0.142 84.4 / 0.1);
363366
}
364367

368+
.hero-cta-after {
369+
font-family: var(--font-heading, "Roboto", sans-serif);
370+
font-size: 1.3rem;
371+
font-weight: 700;
372+
letter-spacing: 0.04em;
373+
color: var(--color-text-secondary);
374+
border: 3px solid var(--color-border);
375+
border-radius: 2px;
376+
padding: 1rem 2.5rem;
377+
display: inline-block;
378+
}
379+
365380
@media (max-width: 768px) {
366381
.hero-main {
367382
padding: 5rem 1.2rem 3rem;

src/components/ui/DateGate.astro

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
/**
3+
* DateGate — conditionally render content based on build-time date.
4+
*
5+
* Defines a date window via `after` and/or `before` props. At build time,
6+
* the component determines which time period "now" falls into and renders
7+
* exactly one of three named slots: "before", "during", or "after".
8+
*
9+
* Any omitted slot simply renders nothing — use 2 slots for a simple
10+
* on/off gate or all 3 for a full before/during/after split.
11+
*
12+
* Date strings are parsed as UTC midnight when no time portion is given
13+
* (e.g. "2026-07-14" = 2026-07-14T00:00:00.000Z). For precise time-of-day
14+
* gating in a specific timezone, include the offset:
15+
* after="2026-07-14T00:00:00+02:00" (midnight CEST)
16+
*
17+
* @example
18+
* // 3-state: before CfP / open / closed
19+
* <DateGate after="2026-06-01" before="2026-07-01">
20+
* <p slot="before">CfP opens June 1st!</p>
21+
* <a slot="during" href="/cfp">Submit your talk</a>
22+
* <p slot="after">CfP closed. See you in Kraków!</p>
23+
* </DateGate>
24+
*
25+
* @example
26+
* // 2-state: teaser → live (omit "after" slot)
27+
* <DateGate after="2026-07-14">
28+
* <p slot="before">Tickets coming soon!</p>
29+
* <a slot="during" href="/tickets">Buy Tickets</a>
30+
* </DateGate>
31+
*/
32+
33+
interface Props {
34+
/**
35+
* Window start — ISO 8601 date string, inclusive.
36+
* Date-only strings (e.g. "2026-07-14") are parsed as UTC midnight.
37+
* Include offset for timezone-aware gating: "2026-07-14T00:00:00+02:00"
38+
*/
39+
after?: string;
40+
/**
41+
* Window end — ISO 8601 date string, inclusive.
42+
* Same parsing rules as `after`.
43+
*/
44+
before?: string;
45+
}
46+
47+
const { after, before } = Astro.props;
48+
49+
// --- Parse dates ---
50+
// Date-only strings (no "T") get interpreted as UTC midnight by new Date().
51+
// DateTime strings with offset (e.g. "...T00:00:00+02:00") parse correctly.
52+
const parseDate = (s: string): Date => {
53+
const iso = s.includes("T") ? s : `${s}T00:00:00.000Z`;
54+
return new Date(iso);
55+
};
56+
57+
const now = new Date();
58+
const afterDate = after ? parseDate(after) : null;
59+
const beforeDate = before ? parseDate(before) : null;
60+
61+
// --- Determine which period "now" falls into ---
62+
type Period = "before" | "during" | "after";
63+
64+
let period: Period = "during";
65+
66+
if (afterDate && beforeDate) {
67+
if (now < afterDate) period = "before";
68+
else if (now > beforeDate) period = "after";
69+
else period = "during";
70+
} else if (afterDate) {
71+
period = now < afterDate ? "before" : "during";
72+
} else if (beforeDate) {
73+
period = now > beforeDate ? "after" : "during";
74+
}
75+
---
76+
77+
{period === "before" ? <slot name="before" /> : null}
78+
{period === "during" ? <slot name="during" /> : null}
79+
{period === "after" ? <slot name="after" /> : null}

0 commit comments

Comments
 (0)