From 62344b79699dd629b00cabf274ea84e619554d39 Mon Sep 17 00:00:00 2001
From: aamoghS
Date: Mon, 5 Oct 2026 12:05:10 -0400
Subject: [PATCH 1/4] fix(hacklytics): give /hacklytics the Hacklytics site's
look
/hacklytics is where people land from hacklytics.io, but it rendered in
the portal's newsletter style with the "Query." wordmark. It now wears
the event site's look, scoped to the route:
- .hl-theme overrides the portal tokens on the page wrapper (dark grey
ground, bloom pink accent, Silkscreen headings, Instrument Sans text),
so the shared primitives and the interest form follow unchanged
- fonts loaded with next/font in the route layout
- masthead is the Hacklytics wordmark linking to hacklytics.io
- a still flower bed from the event site's sprites, a few in bloom
- metadata no longer calls it "premier"
---
.../app/(portal)/hacklytics/layout.tsx | 28 ++++-
.../mainweb/app/(portal)/hacklytics/page.tsx | 115 ++++++++++++++++--
sites/mainweb/app/(portal)/liquid-glass.css | 45 +++++++
3 files changed, 175 insertions(+), 13 deletions(-)
diff --git a/sites/mainweb/app/(portal)/hacklytics/layout.tsx b/sites/mainweb/app/(portal)/hacklytics/layout.tsx
index 749312c5..21032edb 100644
--- a/sites/mainweb/app/(portal)/hacklytics/layout.tsx
+++ b/sites/mainweb/app/(portal)/hacklytics/layout.tsx
@@ -1,6 +1,22 @@
import type { Metadata } from "next";
+import { Instrument_Sans, Silkscreen } from "next/font/google";
import { db, hackathons, inArray, eq, and, asc } from "@query/db";
+// The Hacklytics site's own faces (sites/hacklytics2027): Silkscreen for
+// headings, Instrument Sans for text. Scoped to this route by .hl-theme.
+const silkscreen = Silkscreen({
+ subsets: ["latin"],
+ weight: ["400", "700"],
+ variable: "--font-hl-pixel",
+ display: "swap",
+});
+
+const instrumentSans = Instrument_Sans({
+ subsets: ["latin"],
+ variable: "--font-hl-sans",
+ display: "swap",
+});
+
const SITE = "https://datasciencegt.org";
const URL = `${SITE}/hacklytics`;
@@ -65,7 +81,7 @@ export async function generateMetadata(): Promise {
// The root layout's template appends the organisation.
title: "Hacklytics",
description:
- "Georgia Tech's premier data science hackathon, run by Data Science @ Georgia Tech.",
+ "Georgia Tech's data science hackathon, run by Data Science @ Georgia Tech.",
alternates: { canonical: URL },
};
}
@@ -73,7 +89,7 @@ export async function generateMetadata(): Promise {
const dates = formatRange(edition.startDate, edition.endDate);
const description =
edition.description ??
- "Georgia Tech's premier data science hackathon, open to students nationwide.";
+ "Georgia Tech's data science hackathon, open to students nationwide.";
const title = `${edition.name} | ${dates}`;
return {
@@ -141,7 +157,7 @@ async function EventJsonLd() {
eventAttendanceMode: "https://schema.org/OfflineEventAttendanceMode",
description:
edition.description ??
- "Georgia Tech's premier data science hackathon, open to students nationwide.",
+ "Georgia Tech's data science hackathon, open to students nationwide.",
url: URL,
image: [`${SITE}/logo512.png`],
organizer: {
@@ -201,9 +217,11 @@ export default function HacklyticsLayout({
children: React.ReactNode;
}) {
return (
- <>
+
{children}
- >
+
);
}
diff --git a/sites/mainweb/app/(portal)/hacklytics/page.tsx b/sites/mainweb/app/(portal)/hacklytics/page.tsx
index 535bb534..0949fcfb 100644
--- a/sites/mainweb/app/(portal)/hacklytics/page.tsx
+++ b/sites/mainweb/app/(portal)/hacklytics/page.tsx
@@ -68,19 +68,115 @@ const formatDeadline = (deadline: Date) =>
});
/**
- * The sidebar is suppressed here, so the page carries its own masthead line:
- * the club's wordmark, linking home.
+ * The sidebar is suppressed here, so the page carries its own masthead: the
+ * Hacklytics wordmark (linking to the event site) and who runs it.
*/
function Masthead() {
return (
-
- Query.
-
- Data Science @ GT
+ Hacklytics
+
+ by Data Science @ GT
+
);
}
@@ -122,6 +218,7 @@ export default function HacklyticsPage() {
The next Hacklytics has not been announced. Follow Data Science @ GT
and it will show up here first.
+
);
}
@@ -226,6 +323,8 @@ export default function HacklyticsPage() {
More about {event.name}
) : null}
+
+
);
}
diff --git a/sites/mainweb/app/(portal)/liquid-glass.css b/sites/mainweb/app/(portal)/liquid-glass.css
index 113e249f..b467607e 100644
--- a/sites/mainweb/app/(portal)/liquid-glass.css
+++ b/sites/mainweb/app/(portal)/liquid-glass.css
@@ -403,3 +403,48 @@ a {
.card-printed {
position: relative;
}
+
+/* ===== HACKLYTICS (the public /hacklytics page) =====
+ The page people reach from hacklytics.io, so it wears that site's look,
+ not the portal's: dark grey ground, one bloom pink, pixel headings. The
+ tokens are overridden on the wrapper, so the portal primitives and the
+ interest form inside follow without their own variants. Same values as
+ sites/hacklytics2027/app/globals.css. */
+.hl-theme {
+ --bg-primary: #131715;
+ --bg-secondary: #1a1f1d;
+ --bg-tertiary: #131715;
+ --bg-card: #1a1f1d;
+ --bg-elevated: #1a1f1d;
+ --bg-input: #1a1f1d;
+ --text-primary: #edefea;
+ --text-secondary: #c3c9c4;
+ --text-muted: #a9b0aa;
+ --text-subtle: #868e88;
+ --text-on-accent: #16060c;
+ --border-subtle: #2a312d;
+ --border-medium: #39413c;
+ --border-hover: #5a625c;
+ --border-accent: #ff4f8b;
+ --accent: #ff4f8b;
+ --accent-primary: #ff4f8b;
+ --accent-secondary: #ff6a9d;
+ --accent-hover: #ff6a9d;
+ --accent-dim: rgba(255, 79, 139, 0.12);
+ --font-display: var(--font-hl-pixel), ui-monospace, monospace;
+ --font-ui: var(--font-hl-sans), ui-sans-serif, system-ui, sans-serif;
+ color-scheme: dark;
+ min-height: 100vh;
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ font-family: var(--font-ui);
+}
+
+/* Pixel glyphs carry their own spacing; the serif's negative tracking would
+ run them together. */
+.hl-theme h1,
+.hl-theme h2,
+.hl-theme h3 {
+ letter-spacing: 0;
+ font-weight: 700;
+}
From 70b8fac7842daa99d07d9120e6a6f2248e5f8ae7 Mon Sep 17 00:00:00 2001
From: aamoghS
Date: Mon, 5 Oct 2026 12:59:29 -0400
Subject: [PATCH 2/4] fix(hacklytics): kill the service worker and stale caches
Visitors from before the 2027 redesign kept getting the old site: the
service worker served pages stale-while-revalidate from a cache that
outlived deploys, and sw.js itself matched the **/*.js rule and was
served "immutable" for a year.
- public/sw.js is now a kill switch: browsers with the old worker pick
it up on their next update check, delete every cache, unregister and
reload open tabs from the network. Keep it deployed for a season
- the site no longer registers a service worker
(ServiceWorkerRegistrar removed)
- firebase.json (hacklytics): only fingerprinted /_next/static/** is
immutable; unhashed public fonts join images at 1 day + SWR; HTML is
max-age=0, must-revalidate; /sw.js now only matches its no-store rule
---
firebase.json | 6 +-
sites/hacklytics2027/app/layout.tsx | 2 -
.../components/ServiceWorkerRegistrar.tsx | 30 ---
sites/hacklytics2027/public/sw.js | 211 +++---------------
4 files changed, 28 insertions(+), 221 deletions(-)
delete mode 100644 sites/hacklytics2027/components/ServiceWorkerRegistrar.tsx
diff --git a/firebase.json b/firebase.json
index 2f16842b..ab4d69df 100644
--- a/firebase.json
+++ b/firebase.json
@@ -13,7 +13,7 @@
"trailingSlash": true,
"headers": [
{
- "source": "**/*.@(js|css|woff|woff2|ttf|eot|otf)",
+ "source": "/_next/static/**",
"headers": [
{
"key": "Cache-Control",
@@ -26,7 +26,7 @@
]
},
{
- "source": "**/*.@(jpg|jpeg|png|gif|webp|avif|ico|svg)",
+ "source": "**/*.@(jpg|jpeg|png|gif|webp|avif|ico|svg|woff|woff2|ttf|eot|otf)",
"headers": [
{
"key": "Cache-Control",
@@ -43,7 +43,7 @@
"headers": [
{
"key": "Cache-Control",
- "value": "public, max-age=3600, s-maxage=86400"
+ "value": "public, max-age=0, must-revalidate"
},
{
"key": "X-Content-Type-Options",
diff --git a/sites/hacklytics2027/app/layout.tsx b/sites/hacklytics2027/app/layout.tsx
index a1bbce16..8651d301 100644
--- a/sites/hacklytics2027/app/layout.tsx
+++ b/sites/hacklytics2027/app/layout.tsx
@@ -2,7 +2,6 @@ import "./globals.css";
import type { Metadata, Viewport } from "next";
import { Instrument_Sans, Silkscreen } from "next/font/google";
import Navbar from "../components/Navbar";
-import ServiceWorkerRegistrar from "../components/ServiceWorkerRegistrar";
import Footer from "../components/Footer";
import { INTEREST_URL } from "../lib/links";
@@ -123,7 +122,6 @@ export default function RootLayout({ children }: { children: React.ReactNode })
{children}
-