diff --git a/AGENTS.md b/AGENTS.md index 586624b..65df783 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,6 +59,8 @@ No install needed — `open-location-code` is already available at `pcd-website/ - **Astro** owns routing, layouts, metadata, and static content pages. `src/layouts/BaseLayout.astro` provides the document shell; `MapLayout.astro`, `SiteLayout.astro`, and `DocsLayout.astro` provide the map, standard content, and Organizer Kit shells respectively. - The Organizer Kit introduction lives in `src/content/organizer-kit/getting-started/introduction.md`; `/organize/` redirects to its canonical `/organize/getting-started/introduction/` route. +- The Organizer Kit's `partners.md` supplies the “Discover our Partners” overview introduction. `PartnerCards.astro` renders published Organizer Kit entries with `partnerCard` frontmatter, sorted by `order` then title. Each partner detail page in `src/content/organizer-kit/partner-pages/` owns its card's `logo` (relative image path validated by Astro), `summary` (one paragraph leading with the participant or organizer benefit, followed by a short partner description); its title and route supply the card name and link. The same data supplies the overview's shared Markdown and the detail-page logo. Detail pages use `hideFromNav: true` to stay published while being omitted from both shared navigation trees and prev/next links; `draft: true` still excludes a page from build output and cards. Partner card styles live in `src/styles/prose.css`. +- Partner detail routes pass their logo to `DocsLayout` through its optional `headingLogo` prop; the image stays inside the h1 with the page title as alt text. Its optional `backLink` prop renders the shared `BackButton` above the heading in place of the eyebrow; partner pages link back to `/organize/partners/`. - **Vue** handles the interactive map UI as `client:only="vue"` island components. Map-specific interactive features belong in Vue; static site and Organizer Kit pages belong in Astro and Markdown content collections. The map filter drawer in `MapView.vue` filters clustered markers by date (future, past, or all), format, and activity type; checkbox choices are ORed within a group and filter groups are ANDed together. ### Event list diff --git a/pcd-website/src/components/Footer.astro b/pcd-website/src/components/Footer.astro index 6501a78..3310ad4 100644 --- a/pcd-website/src/components/Footer.astro +++ b/pcd-website/src/components/Footer.astro @@ -179,22 +179,19 @@ const UTILITY_LINKS = [ .sponsor-logo { display: inline-flex; align-items: center; - padding: 0.5rem 0.625rem; - border: 1px solid var(--color-border); - border-radius: 4px; - background: #fff; + padding: 0.5rem 0; } .sponsor-logo img { display: block; - width: 200px; + width: auto; max-width: 100%; - height: auto; + height: 30px; + object-fit: contain; } - .sponsor-logo:hover { - border-color: var(--color-primary); - color: var(--color-primary); + .sponsor-logo:hover img { + opacity: 0.8; } .social-row { diff --git a/pcd-website/src/components/PartnerCards.astro b/pcd-website/src/components/PartnerCards.astro new file mode 100644 index 0000000..448ec4c --- /dev/null +++ b/pcd-website/src/components/PartnerCards.astro @@ -0,0 +1,24 @@ +--- +import { Image } from 'astro:assets'; +import { kitHref, type KitEntry } from '../config/organizer-kit-nav'; + +interface Props { + partners: KitEntry[]; +} + +const { partners } = Astro.props; +--- + +
+ {partners.map((partner) => { + const card = partner.data.partnerCard; + if (!card) return null; + return ( +
+

{partner.data.title}

+

{card.summary}

+

Learn more about {partner.data.title} →

+
+ ); + })} +
diff --git a/pcd-website/src/config/organizer-kit-nav.ts b/pcd-website/src/config/organizer-kit-nav.ts index 67a633f..ef0a60e 100644 --- a/pcd-website/src/config/organizer-kit-nav.ts +++ b/pcd-website/src/config/organizer-kit-nav.ts @@ -32,6 +32,7 @@ const TOP_LEVEL: ReadonlyArray = [ { page: 'peer-support-sessions' }, { page: 'code-of-conduct' }, { page: 'faq' }, + { page: 'partners' }, { page: 'about-processing-foundation' }, { page: 'contact' }, { page: 'documents' }, @@ -49,13 +50,14 @@ function toPage(entry: KitEntry): KitPage { /** * Builds the sidebar tree by walking TOP_LEVEL and pulling matching entries out - * of the collection. Throws if an entry exists that TOP_LEVEL never places, so + * of the collection. Pages with `hideFromNav` remain published but are omitted. + * Throws if any other entry exists that TOP_LEVEL never places, so * adding a Markdown file without listing it here fails the build rather than * silently producing an unreachable page. */ export async function getKitNav(): Promise { const allEntries = await getCollection('organizerKit'); - const entries = allEntries.filter((entry) => !entry.data.draft); + const entries = allEntries.filter((entry) => !entry.data.draft && !entry.data.hideFromNav); const allIds = new Set(allEntries.map((entry) => entry.id)); const byId = new Map(entries.map((entry) => [entry.id, entry])); diff --git a/pcd-website/src/config/sponsors.ts b/pcd-website/src/config/sponsors.ts index d219cce..2fbd121 100644 --- a/pcd-website/src/config/sponsors.ts +++ b/pcd-website/src/config/sponsors.ts @@ -1,5 +1,6 @@ import type { ImageMetadata } from 'astro'; import openProcessingLogo from '../images/openprocessing_logo.svg'; +import butterLogo from '../images/butter_logo.svg'; export interface Sponsor { name: string; @@ -13,4 +14,9 @@ export const SPONSORS: Sponsor[] = [ href: 'https://openprocessing.org', logo: openProcessingLogo, }, + { + name: 'Butter', + href: 'https://www.butter.video/', + logo: butterLogo, + }, ]; diff --git a/pcd-website/src/content.config.ts b/pcd-website/src/content.config.ts index a998561..17f926c 100644 --- a/pcd-website/src/content.config.ts +++ b/pcd-website/src/content.config.ts @@ -31,7 +31,7 @@ const pages = defineCollection({ const organizerKit = defineCollection({ loader: glob({ base: './src/content/organizer-kit', pattern: '**/*.md' }), - schema: z.object({ + schema: ({ image }) => z.object({ title: z.string(), // Sidebar grouping. `section` is the parent group's title; standalone // top-level pages omit it. `order` sorts a page within its section — the @@ -39,9 +39,15 @@ const organizerKit = defineCollection({ section: z.string().optional(), order: z.number().default(0), description: z.string().optional(), + partnerCard: z.object({ + logo: image(), + summary: z.string().min(1), + }).optional(), // Excludes the page from the sidebar and from build output entirely, for // TBD pages that shouldn't be publicly reachable yet. draft: z.boolean().default(false), + // Keeps a page published while omitting it from navigation and prev/next links. + hideFromNav: z.boolean().default(false), // Suppresses the "On this page" table of contents, for short pages whose // headings aren't worth navigating. hideToc: z.boolean().default(false), diff --git a/pcd-website/src/content/organizer-kit/partner-pages/butter.md b/pcd-website/src/content/organizer-kit/partner-pages/butter.md new file mode 100644 index 0000000..5114975 --- /dev/null +++ b/pcd-website/src/content/organizer-kit/partner-pages/butter.md @@ -0,0 +1,21 @@ +--- +title: Butter +hideFromNav: true +order: 1 +description: Free Butter Pro for PCD participants in October 2026 +draft: false +hideToc: true +partnerCard: + logo: ../../../images/butter_logo.svg + summary: Get free access to Butter Pro as a PCD participant during October 2026 and build a tool to get a chance to win a cash prize. +--- + +[Butter](https://www.butter.video/) is an online video editor with customizable blocks for motion graphics, animated type, and visual effects. + +## Butter Pro in October + +Butter is offering PCD participants access to Butter Pro during October 2026. Details on how to claim access will be shared here when available. + +## Creative Coding Challenge + +Details TBD diff --git a/pcd-website/src/content/organizer-kit/partner-pages/openprocessing.md b/pcd-website/src/content/organizer-kit/partner-pages/openprocessing.md new file mode 100644 index 0000000..3afd168 --- /dev/null +++ b/pcd-website/src/content/organizer-kit/partner-pages/openprocessing.md @@ -0,0 +1,23 @@ +--- +title: OpenProcessing +hideFromNav: true +order: 2 +description: One free month of OpenProcessing ProfessorPlus +draft: false +hideToc: true +partnerCard: + logo: ../../../images/openprocessing_logo.svg + summary: Get one free month of ProfessorPlus membership for your workshop during October 2026. +--- + +[OpenProcessing](https://openprocessing.org/) is a creative coding platform and community for creating, sharing, and exploring sketches in the browser. + +## Free ProfessorPlus for your workshop + +Sinan from OpenProcessing is offering PCD organizers one free month of ProfessorPlus membership for their workshop during October 2026. + +You can use the membership to try features such as collaborative code editing in your workshop. + +## How to request access + +Contact Sinan at OpenProcessing directly to request the one-month ProfessorPlus membership for your PCD workshop. diff --git a/pcd-website/src/content/organizer-kit/partners.md b/pcd-website/src/content/organizer-kit/partners.md new file mode 100644 index 0000000..93d2086 --- /dev/null +++ b/pcd-website/src/content/organizer-kit/partners.md @@ -0,0 +1,7 @@ +--- +title: Discover our Partners +description: Meet our partners and explore what they offer PCD organizers. +hideToc: true +--- + +Meet the platforms supporting our community and discover what they offer PCD organizers. diff --git a/pcd-website/src/images/butter_logo.svg b/pcd-website/src/images/butter_logo.svg new file mode 100644 index 0000000..3e66d58 --- /dev/null +++ b/pcd-website/src/images/butter_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcd-website/src/layouts/DocsLayout.astro b/pcd-website/src/layouts/DocsLayout.astro index 8c42406..153980b 100644 --- a/pcd-website/src/layouts/DocsLayout.astro +++ b/pcd-website/src/layouts/DocsLayout.astro @@ -1,11 +1,13 @@ --- -import type { MarkdownHeading } from 'astro'; +import type { ImageMetadata, MarkdownHeading } from 'astro'; +import { Image } from 'astro:assets'; import BaseLayout from './BaseLayout.astro'; import Header from '../components/Header.astro'; import Footer from '../components/Footer.astro'; import DocsSidebar from '../components/DocsSidebar.astro'; import DocsTableOfContents from '../components/DocsTableOfContents.astro'; import DocsPageActions from '../components/DocsPageActions.astro'; +import BackButton from '../components/BackButton.astro'; import { getKitNav } from '../config/organizer-kit-nav'; import '../styles/base.css'; import '../styles/docs.css'; @@ -15,8 +17,12 @@ interface Props { description: string; /** Rendered above the h1 as the section label. */ eyebrow?: string; + /** Parent navigation shown in place of the eyebrow. */ + backLink?: { href: string; label: string }; /** Page heading. Falls back to `title` when omitted. */ heading?: string; + /** Optional logo displayed within the h1, using the heading as alt text. */ + headingLogo?: ImageMetadata; headings?: MarkdownHeading[]; /** Suppresses the "On this page" table of contents. */ hideToc?: boolean; @@ -26,7 +32,7 @@ interface Props { editHref?: string; } -const { title, description, eyebrow, heading, headings = [], hideToc = false, markdown, editHref } = Astro.props; +const { title, description, eyebrow, backLink, heading, headingLogo, headings = [], hideToc = false, markdown, editHref } = Astro.props; const nav = await getKitNav(); const permalink = new URL(Astro.url.pathname, Astro.site ?? Astro.url.origin).href; @@ -49,9 +55,13 @@ const permalink = new URL(Astro.url.pathname, Astro.site ?? Astro.url.origin).hr
- {eyebrow &&

{eyebrow}

} -
-

{heading ?? title}

+ {backLink + ? + : eyebrow &&

{eyebrow}

} +
+

{headingLogo + ? + : heading ?? title}

{markdown && editHref && ( )} @@ -64,6 +74,23 @@ const permalink = new URL(Astro.url.pathname, Astro.site ?? Astro.url.origin).hr