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
75 changes: 75 additions & 0 deletions blocks/session-card/session-card.css
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,78 @@
.session-card-sep {
color: #c5c5cd;
}

/* ── No-image cards ───────────────────────────────────────────────────────
* Today none of the AI CoC sessions have a per-session cover image, so
* every card hits this branch. The 16/9 dark-gradient placeholder no
* longer justifies its space — collapse it to a compact strip that just
* holds the AI COC pill. (Hover play overlay also hidden — no thumbnail
* to "play" into.) Applied in both light and dark mode for consistency.
*
* The `--no-image` modifier is intentional BEM-style on top of the kebab
* base class — disable the linter for these two selectors only.
*/
/* stylelint-disable selector-class-pattern, no-descending-specificity */
.session-card--no-image .session-card-media {
aspect-ratio: auto;
min-height: 44px;
background: transparent;
border-bottom: 1px solid #ececef;
}

.session-card--no-image .session-card-placeholder,
.session-card--no-image .session-card-play {
display: none;
}
/* stylelint-enable selector-class-pattern, no-descending-specificity */

/* ── Dark mode ────────────────────────────────────────────────────────────
* The AI CoC hub is intentionally dark-themed even in light mode (red
* gradient hero), so a bright white card body sitting on a near-black page
* looks jarring when the OS dark mode is on. Mirror the page tone by
* darkening the card itself and flipping the text colors.
*
* Scoped under prefers-color-scheme so light-mode rendering is byte-identical.
*/
@media (prefers-color-scheme: dark) {
.session-card {
/* Lighter than the page bg (#0b0b0d) so cards lift visibly. The
* earlier #1a1a1c was too close to the page tone and the cards
* blended in. */
background: #2d2d33;
border-color: #3d3d44;
}

.session-card:hover,
.session-card:focus-visible {
/* Brighter shadow so the hover lift still reads on a dark page. */
box-shadow: 0 8px 24px rgb(0 0 0 / 60%);
}

.session-card-title {
color: #f5f5f7;
}

.session-card-description {
color: #d4d4d8;
}

.session-card-meta {
color: #9a9aa0;
}

.session-card-presenter {
color: #f5f5f7;
}

.session-card-sep {
color: #666;
}

/* Dark variant of the no-image media strip — softer border matching the
* lighter card body. */
/* stylelint-disable-next-line selector-class-pattern */
.session-card--no-image .session-card-media {
border-bottom-color: #3d3d44;
}
}
30 changes: 29 additions & 1 deletion blocks/session-card/session-card.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
import { createOptimizedPicture } from '../../scripts/lib-franklin.js';

/**
* Format whatever the index gives us for sessionDate into a short,
* human-readable string. Word doc cells that look like dates get serialised
* as Excel serial numbers ("46175") by the publishing pipeline — without
* this, the card shows the raw serial. Plain MM-DD-YYYY strings get
* pretty-formatted too.
*/
function formatSessionDate(raw) {
if (!raw) return '';
const t = String(raw).trim();
let d = null;
const m = t.match(/^(\d{1,2})[-/](\d{1,2})[-/](\d{4})$/);
if (m) {
d = new Date(Date.UTC(+m[3], +m[1] - 1, +m[2]));
} else if (/^\d+$/.test(t)) {
// Excel serial → JS Date. Same epoch math as session-feed.js.
d = new Date(Math.round((Number(t) - (1 + 25567 + 1)) * 86400 * 1000));
}
if (!d || Number.isNaN(d.getTime())) return t;
return d.toLocaleDateString('en-US', {
month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC',
});
}

/**
* Build a session card.
*
Expand All @@ -25,6 +49,9 @@ export function buildSessionCard(session, eager = false) {

const card = document.createElement('a');
card.className = 'session-card';
// Tag image-less cards so the CSS can collapse the empty 16:9 media area
// to a compact pill strip — otherwise it's just dead space above the title.
if (!image) card.classList.add('session-card--no-image');
card.href = path;

// ── media ─────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -78,7 +105,8 @@ export function buildSessionCard(session, eager = false) {
const meta = document.createElement('p');
meta.className = 'session-card-meta';
const parts = [];
if (sessionDate) parts.push(`<span class="session-card-date">${sessionDate}</span>`);
const dateText = formatSessionDate(sessionDate);
if (dateText) parts.push(`<span class="session-card-date">${dateText}</span>`);
if (presenter) parts.push(`<span class="session-card-presenter">${presenter}</span>`);
meta.innerHTML = parts.join('<span class="session-card-sep">·</span>');
body.append(meta);
Expand Down
29 changes: 23 additions & 6 deletions styles/aicoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,13 @@ body.aicoc main a:hover { color: var(--aicoc-accent-3); }
body.aicoc-hub main > div:first-of-type {
position: relative;
overflow: hidden;
padding: 80px 24px 72px;

/* Contained hero, not full-bleed — gives the page bg breathing room
* around a centered dark block, instead of dark edge-to-edge. */
max-width: 1200px;
margin: 32px auto 0;
padding: 64px 32px 56px;
border-radius: 24px;
color: #fff;
text-align: center;
background:
Expand Down Expand Up @@ -158,13 +164,24 @@ body.aicoc-hub main hr {
display: none;
}

/* Any section in main BEYOND the first one gets the light background.
* Vertical padding here also acts as breathing room above the feed so the
* hero ends crisply and the feed isn't crammed against it. */
/* Any section in main BEYOND the first one (where the session-feed sits).
* Constrained to match the hero width, with the page bg showing on either
* side. */
/* stylelint-disable-next-line no-descending-specificity */
body.aicoc-hub main > div ~ div {
background: var(--aicoc-bg);
padding: 24px 0 32px;
max-width: 1200px;
margin: 0 auto;
padding: 32px 0 48px;
background: transparent;
}

/* Dark mode: match the body bg to the hero's darkest gradient stop so the
* hero's rounded corners blend seamlessly with the page instead of showing
* lighter-grey corners. */
@media (prefers-color-scheme: dark) {
body.aicoc {
background-color: #0b0b0d;
}
}

/* Defensive: if the session-feed somehow ends up nested in the hero div
Expand Down
Loading