Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 34 additions & 8 deletions src/components/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import HeaderLogo from "@components/header/header-logo.astro";
import Search from "@components/Search.astro";
import ThemeToggle from "@components/ThemeToggle.astro";
import DateGate from "@ui/DateGate.astro";

import { NAV_MENUS } from "@data/nav";
import { buildLinkChecker } from "@utils/nav";
Expand Down Expand Up @@ -111,12 +112,24 @@ const activeMenus = NAV_MENUS.map((menu) => ({
),
)
}
<li class="nav-item nav-mobile-cta">
<a class="nav-link-btn" href="/jobs">Jobs</a>
</li>
<li class="nav-item nav-mobile-cta">
<a class="nav-link-btn" href="/tickets">Register Now</a>
</li>
<DateGate before="2026-07-20">
<Fragment slot="before">
<li class="nav-item nav-mobile-cta">
<a class="nav-link-btn" href="/jobs">Jobs</a>
</li>
<li class="nav-item nav-mobile-cta">
<a class="nav-link-btn" href="/tickets">Register Now</a>
</li>
</Fragment>
<Fragment slot="during">
<li class="nav-item nav-mobile-cta">
<a class="nav-link-btn" href="/jobs">Jobs</a>
</li>
<li class="nav-item nav-mobile-cta">
<a class="nav-link-btn" href="/tickets">Register Now</a>
</li>
</Fragment>
</DateGate>
</ul>

<button
Expand Down Expand Up @@ -160,8 +173,21 @@ const activeMenus = NAV_MENUS.map((menu) => ({
</svg>
</a>
<ThemeToggle />
<a href="/jobs" class="nav-cta-desktop">Jobs</a>
<a href="/tickets" class="nav-cta">Register Now</a>
<DateGate before="2026-07-20">
<Fragment slot="before">
<a href="/jobs" class="nav-cta-desktop">Jobs</a>
<a href="/tickets" class="nav-cta">Register Now</a>
</Fragment>
<Fragment slot="during">
<a href="/jobs" class="nav-cta-desktop">Jobs</a>
<a href="/tickets" class="nav-cta">Register Now</a>
</Fragment>
<Fragment slot="after">
<span class="nav-cta" style="opacity:0.5;cursor:default"
>See you next year!</span
>
</Fragment>
</DateGate>
<button
class="nav-toggle"
aria-label="Toggle menu"
Expand Down
19 changes: 17 additions & 2 deletions src/components/sections/hero/hero.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---

import DateGate from "@ui/DateGate.astro";
---

<section class="hero-main">
Expand Down Expand Up @@ -42,7 +42,10 @@ PYTHON">
> 18–23 July
</p>
<div class="hero-cta-row">
<a href="/tickets" class="hero-cta">Register Now</a>
<DateGate before="2026-07-20">
<a slot="before" href="/tickets" class="hero-cta">Register Now</a>
<a slot="during" href="/tickets" class="hero-cta">Register Now</a>
</DateGate>
<a href="#programme" class="hero-cta-alt">View Programme</a>
</div>
<div class="hero-flowers" aria-hidden="true">
Expand Down Expand Up @@ -362,6 +365,18 @@ PYTHON">
inset 0 0 20px oklch(0.831 0.142 84.4 / 0.1);
}

.hero-cta-after {
font-family: var(--font-heading, "Roboto", sans-serif);
font-size: 1.3rem;
font-weight: 700;
letter-spacing: 0.04em;
color: var(--color-text-secondary);
border: 3px solid var(--color-border);
border-radius: 2px;
padding: 1rem 2.5rem;
display: inline-block;
}

@media (max-width: 768px) {
.hero-main {
padding: 5rem 1.2rem 3rem;
Expand Down
79 changes: 79 additions & 0 deletions src/components/ui/DateGate.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
/**
* DateGate — conditionally render content based on build-time date.
*
* Defines a date window via `after` and/or `before` props. At build time,
* the component determines which time period "now" falls into and renders
* exactly one of three named slots: "before", "during", or "after".
*
* Any omitted slot simply renders nothing — use 2 slots for a simple
* on/off gate or all 3 for a full before/during/after split.
*
* Date strings are parsed as UTC midnight when no time portion is given
* (e.g. "2026-07-14" = 2026-07-14T00:00:00.000Z). For precise time-of-day
* gating in a specific timezone, include the offset:
* after="2026-07-14T00:00:00+02:00" (midnight CEST)
*
* @example
* // 3-state: before CfP / open / closed
* <DateGate after="2026-06-01" before="2026-07-01">
* <p slot="before">CfP opens June 1st!</p>
* <a slot="during" href="/cfp">Submit your talk</a>
* <p slot="after">CfP closed. See you in Kraków!</p>
* </DateGate>
*
* @example
* // 2-state: teaser → live (omit "after" slot)
* <DateGate after="2026-07-14">
* <p slot="before">Tickets coming soon!</p>
* <a slot="during" href="/tickets">Buy Tickets</a>
* </DateGate>
*/

interface Props {
/**
* Window start — ISO 8601 date string, inclusive.
* Date-only strings (e.g. "2026-07-14") are parsed as UTC midnight.
* Include offset for timezone-aware gating: "2026-07-14T00:00:00+02:00"
*/
after?: string;
/**
* Window end — ISO 8601 date string, inclusive.
* Same parsing rules as `after`.
*/
before?: string;
}

const { after, before } = Astro.props;

// --- Parse dates ---
// Date-only strings (no "T") get interpreted as UTC midnight by new Date().
// DateTime strings with offset (e.g. "...T00:00:00+02:00") parse correctly.
const parseDate = (s: string): Date => {
const iso = s.includes("T") ? s : `${s}T00:00:00.000Z`;
return new Date(iso);
};

const now = new Date();
const afterDate = after ? parseDate(after) : null;
const beforeDate = before ? parseDate(before) : null;

// --- Determine which period "now" falls into ---
type Period = "before" | "during" | "after";

let period: Period = "during";

if (afterDate && beforeDate) {
if (now < afterDate) period = "before";
else if (now > beforeDate) period = "after";
else period = "during";
} else if (afterDate) {
period = now < afterDate ? "before" : "during";
} else if (beforeDate) {
period = now > beforeDate ? "after" : "during";
}
---

{period === "before" ? <slot name="before" /> : null}
{period === "during" ? <slot name="during" /> : null}
{period === "after" ? <slot name="after" /> : null}
Loading