Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ab0b0a2
Remove node list and dark mode (Phase 1 of multi-page migration)
SableRaf Aug 11, 2026
c864c8e
Split global.css into base.css and map.css (Phase 2 of multi-page mig…
SableRaf Aug 11, 2026
a272774
Add shared layouts and header (Phase 3 of multi-page migration)
SableRaf Aug 11, 2026
31ec5a3
Integrate header with the map (Phase 4 of multi-page migration)
SableRaf Aug 11, 2026
5fe2d80
Add footer and content pages (Phase 5 of multi-page migration)
SableRaf Aug 11, 2026
704c130
Add Organizer Kit docs section with sidebar (Phase 6)
SableRaf Aug 11, 2026
227a447
Port Organizer Kit docs styling
SableRaf Aug 11, 2026
2d4d20a
Refine Organizer Kit navigation
SableRaf Aug 11, 2026
b5aabe9
Add "Back to top" and "Edit this page on GitHub" links to Organizer K…
SableRaf Aug 12, 2026
edef064
Style text links like Just the Docs
SableRaf Aug 12, 2026
aea0b72
Credit Just the Docs in Organizer Kit
SableRaf Aug 12, 2026
8a51e2a
Hide redundant bullets on checklists and page TOC
SableRaf Aug 12, 2026
21aff92
Add Discord invite URL
SableRaf Aug 12, 2026
d25fba2
Add anchor links to Organizer Kit headers
SableRaf Aug 12, 2026
f505443
Install Prettier and format css
SableRaf Aug 12, 2026
f87fbcc
Update photography-and-video.md
SableRaf Aug 12, 2026
6b2edc7
Address organizer kit review feedback
SableRaf Aug 12, 2026
079a7d1
Add responsive mobile navigation
SableRaf Aug 12, 2026
c301ddf
Add copy to markdown feature
SableRaf Aug 12, 2026
9ad5108
Update code-of-conduct.md
SableRaf Aug 12, 2026
ad98b4e
Update faq.md
SableRaf Aug 12, 2026
22a89e9
Update faq.md
SableRaf Aug 12, 2026
2b956fb
Hide draft Organizer Kit pages from publish
SableRaf Aug 12, 2026
d75e2d0
Initial text for activity guides page
SableRaf Aug 12, 2026
6c26173
Split Activity Guides into Organizer Kit pages
SableRaf Aug 12, 2026
d74706d
Update footer external links and styling
SableRaf Aug 13, 2026
0b5e80d
Change title to "PCD Organizer's Kit"
SableRaf Aug 13, 2026
8042fc7
Share prose styles across content pages
SableRaf Aug 13, 2026
b4825c7
Fix heading anchor slugs diverging from Astro's IDs
SableRaf Aug 13, 2026
ab6b6d8
Reorganize org kit content
SableRaf Aug 13, 2026
ae5be70
Clean up and update organizer role guidance
SableRaf Aug 13, 2026
c55c6ec
Clarify Foundation mission and safety
SableRaf Aug 13, 2026
dc8bc91
Update introduction.md
SableRaf Aug 13, 2026
f5835e8
Better acknowledgement
SableRaf Aug 13, 2026
baa70d5
Update code-of-conduct.md
SableRaf Aug 13, 2026
1ac5525
Add PCD 2026 logo
SableRaf Aug 13, 2026
3b52f48
Update faq.md
SableRaf Aug 13, 2026
aecad60
Hide TBD forum and map pages
SableRaf Aug 13, 2026
1d85c57
Move and expand organizer kit acknowledgements
SableRaf Aug 13, 2026
d78ac78
Keep Organize underlined across all org kit sub-pages
SableRaf Aug 13, 2026
843e856
Update photography-and-video.md
SableRaf Aug 13, 2026
cac80b7
Update minimum-viable-pcd.md
SableRaf Aug 13, 2026
af364ad
Add organizer welcome letter
SableRaf Aug 13, 2026
1e2c4f3
Tighten code of conduct wording
SableRaf Aug 13, 2026
14b12d2
Revise organizer kit planning content
SableRaf Aug 13, 2026
1fa38b3
Update minimum-viable-pcd.md
SableRaf Aug 13, 2026
c6ab7e6
Update Footer.astro
SableRaf Aug 13, 2026
8179db4
Remove PF from sponsor list
SableRaf Aug 13, 2026
382e239
Soften event promo guidance
SableRaf Aug 13, 2026
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ Thumbs.db
.idea/
*.swp
*.tmp

# Design scratch (throwaway click dummies, not part of the site)
.click-dummy/

.references
30 changes: 19 additions & 11 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ No install needed — `open-location-code` is already available at `pcd-website/

### Astro + Vue split

- **Astro** (`src/pages/index.astro`) is the single entry point — a static HTML shell with `<head>`, font/CSS links, and BASE_URL injection. No new Astro pages should be added.
- **Vue** handles all interactive UI as `client:only="vue"` island components. New UI features go in Vue components, not Astro pages.
- **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.
- **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.

### Data loading at build time

Expand All @@ -66,32 +66,43 @@ Event data lives in `src/content/events/<event-id>/`:

`src/lib/nodes.ts` loads all events at Astro build time using `import.meta.glob()` + `getCollection('events')`, validates plus codes with `OpenLocationCode`, decodes lat/lng, and returns a sorted `Node[]` array passed as props to `<MapView>`.

The global Markdown pipeline runs `rehype-table-wrapper` and `rehype-heading-anchors`, which respectively wrap rendered tables in `.table-wrapper` and add permalink anchors to h2–h6. Their presentation styles live in the shared `prose.css` layer, scoped to both `.prose` and `.docs-prose`, because both plugins apply to all Markdown collections.

**If a plus_code is invalid or too short, the build fails with a clear error — this is intentional.**

**"Confirmed" events in data.json:** An event is included in the `/data.json` feed if it is present in `loadNodes()` and has no `placeholder: true` flag. There are currently no other event states (draft, hidden, etc.). If new states are added in future, the filter in `src/pages/data.json.ts` must be updated explicitly.

### Key implementation details

- **Leaflet CSS** is loaded via `<link>` tags in `index.astro`, NOT via JS imports — avoids SSR issues since MapView is `client:only="vue"`.
- **Leaflet CSS** is loaded via `<link>` tags in `MapLayout.astro`, NOT via JS imports — avoids SSR issues since MapView is `client:only="vue"`.
- **`open-location-code`** exports `{ OpenLocationCode }` as a named export — use `new OpenLocationCode()` (not static methods).
- **`leaflet.markercluster`** causes a circular dependency warning, suppressed via `rollupOptions.onwarn` in `astro.config.mjs`.
- **Deep linking:** `?event=<id-or-uid>` query param auto-opens the event detail panel. Both the slug `id` and the short `uid` are accepted.
- **Map style preference** persisted in `localStorage`.
- **Event UIDs:** Each event has a stable 7-char hex `uid` stored in both `metadata.json` and `content.md` frontmatter. UIDs never change after creation. Three static URL formats are generated per event: `/event/<slug>` (redirects to canonical), `/event/<slug>-<uid>` (canonical, has OG tags, redirects into SPA), and `/event/<uid>` (short form, redirects to canonical). The canonical URL is what the share button copies.

### Component roles

| File | Role |
|---|---|
| `src/components/MapView.vue` | Leaflet map, marker clustering, keyboard shortcuts, tile layer switching |
| `src/components/MapView.vue` | Leaflet map, marker clustering, keyboard shortcuts |
| `src/components/NodePanel.vue` | Slide-in event detail panel with minimap, calendar links, share button |
| `src/components/NodeList.vue` | Alphabetical event list overlay with map style switcher + dark mode toggle |
| `src/components/LanguageSwitcher.vue` | Language selector dropdown in the top bar |
| `src/components/CopyMarkdownButton.astro` | Copies an Organizer Kit page as Markdown with accessible success/error feedback |
| `src/components/Header.astro` | Shared fixed site header and primary navigation |
| `src/components/Footer.astro` | Shared site footer, policy links, community links, and sponsors |
| `src/layouts/BaseLayout.astro` | Shared HTML document shell and metadata |
| `src/layouts/MapLayout.astro` | Map-page shell and Leaflet stylesheet links |
| `src/layouts/SiteLayout.astro` | Standard static content-page shell |
| `src/layouts/DocsLayout.astro` | Organizer Kit shell with sidebar, page TOC, and footer |
| `src/lib/analytics.ts` | `trackEvent()` Fathom helper + `AnalyticsEvent` type + event-name constants |
| `src/lib/nodes.ts` | `Node` interface + `loadNodes()` |
| `src/lib/format.ts` | `formatDate()`, `formatDateRange()`, `calendarLinks()`, etc. |
| `src/lib/popup.ts` | Leaflet popup HTML generation (`makePopupContent()`) |
| `src/styles/global.css` | Design tokens (CSS custom properties), IBM Plex Sans, Leaflet overrides |
| `src/styles/base.css` | Shared design tokens, reset, typography, focus, and skip-link styles |
| `src/styles/map.css` | Map layout, controls, popup styling, and Leaflet overrides |
| `src/styles/prose.css` | Standard static content-page presentation styles |
| `src/styles/docs/*.css` | Organizer Kit's modular Just-the-Docs-derived tokens, layout, navigation, and Markdown presentation styles |
| `src/lib/rehype-table-wrapper.mjs` | Markdown rehype plugin that wraps rendered tables for horizontal scrolling |
| `src/pages/data.json.ts` | Static JSON feed of confirmed events, served at /data.json |
| `src/content.config.ts` | Astro content collection Zod schema for events |
| `src/config.ts` | Global static constants (contact email, etc.) |
Expand Down Expand Up @@ -143,10 +154,7 @@ Use `src/config.ts` for static, non-secret values that are referenced across mul

## UI / Styling Rules

- Always support both light and dark mode for any new or modified UI elements.
- Dark mode is toggled via `[data-theme="dark"]` on `<html>` (set by `NodeList.vue`).
- Dark mode uses CSS custom properties defined in `global.css` under `[data-theme="dark"]`.
- **Vue scoped styles cannot target ancestor-based dark mode selectors** — put those overrides in `global.css`.
- The site is light-mode only — there is no dark mode, no `[data-theme]` toggling, and no theme-related CSS. Do not reintroduce it without an explicit decision to do so.

## Accessibility

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ A new pull request will be automatically created with the updates. A maintainer
- **[leaflet.markercluster](https://github.com/Leaflet/Leaflet.markercluster)** — for clustering map markers
- **[Open Location Code](https://github.com/google/open-location-code)** — for geocoding plus codes to lat/lng
- **[GitHub Workflows](https://github.com/features/actions)** — for event submission, review, and data management
- **[Just the Docs](https://github.com/just-the-docs/just-the-docs)** — the Organizer Kit theme is adapted from it

## Developing

Expand Down Expand Up @@ -74,6 +75,10 @@ LLMs can be useful, but they also make mistakes and should be treated with cauti

If you notice issues, bugs, factual mistakes, or anything else that could be improved, please feel free to open an issue or suggest changes. Contributions and corrections are always welcome.

## Credits

The Organizer Kit documentation theme (see [`src/styles/docs/`](./pcd-website/src/styles/docs/)) is adapted from [Just the Docs](https://github.com/just-the-docs/just-the-docs), Copyright (c) 2016 Patrick Marsceill, used under the MIT License.

## License

This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.
5 changes: 5 additions & 0 deletions pcd-website/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// @ts-check
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
import rehypeTableWrapper from './src/lib/rehype-table-wrapper.mjs';
import rehypeHeadingAnchors from './src/lib/rehype-heading-anchors.mjs';

// https://astro.build/config
export default defineConfig({
output: 'static',
site: 'https://day.processing.org',
base: '/',
integrations: [vue({ appEntrypoint: '/src/i18n/vuePlugin' })],
markdown: {
rehypePlugins: [rehypeTableWrapper, rehypeHeadingAnchors],
},
vite: {
build: {
rollupOptions: {
Expand Down
21 changes: 20 additions & 1 deletion pcd-website/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion pcd-website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@
"@luomus/leaflet-smooth-wheel-zoom": "^1.0.0",
"astro": "^5.17.1",
"focus-trap": "^8.0.0",
"github-slugger": "^2.0.0",
"leaflet": "^1.9.4",
"leaflet.markercluster": "^1.5.3",
"micromark": "^4.0.2",
"micromark-extension-gfm": "^3.0.0",
"open-location-code": "^1.0.3",
"unist-util-visit": "^5.1.0",
"vue": "^3.5.29",
"vue-i18n": "^11.3.0"
},
"devDependencies": {
"@types/leaflet": "^1.9.21",
"@types/leaflet.markercluster": "^1.5.6"
"@types/leaflet.markercluster": "^1.5.6",
"prettier": "^3.9.6"
}
}
69 changes: 69 additions & 0 deletions pcd-website/src/components/CopyMarkdownButton.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
import icons from '@iconify-json/bi/icons.json';

interface Props {
markdown: string;
}

const { markdown } = Astro.props;
const copyIcon = icons.icons.copy;
---

<button class="copy-markdown" type="button" data-copy-markdown={markdown}>
<svg
class="copy-markdown__icon"
width="1em"
height="1em"
viewBox="0 0 16 16"
aria-hidden="true"
>
<Fragment set:html={copyIcon.body} />
</svg>
<span class="copy-markdown__label" aria-live="polite">Copy as Markdown</span>
</button>

<script>
const fallbackCopy = (text: string) => {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.append(textarea);
textarea.select();
const copied = document.execCommand('copy');
textarea.remove();
if (!copied) throw new Error('Copy command failed');
};

const copyText = async (text: string) => {
if (navigator.clipboard?.writeText) {
try {
await navigator.clipboard.writeText(text);
return;
} catch {
// The legacy command can still work when clipboard permissions are denied.
}
}
fallbackCopy(text);
};

document.querySelectorAll<HTMLButtonElement>('[data-copy-markdown]').forEach((button) => {
button.addEventListener('click', async () => {
const label = button.querySelector<HTMLElement>('.copy-markdown__label');
if (!label) return;

try {
const markdown = button.dataset.copyMarkdown ?? '';
await copyText(markdown);
label.textContent = 'Copied!';
} catch {
label.textContent = 'Could not copy';
}

window.setTimeout(() => {
label.textContent = 'Copy as Markdown';
}, 2000);
});
});
</script>
75 changes: 75 additions & 0 deletions pcd-website/src/components/DocsSidebar.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
import { KIT_BASE, type KitNavNode, type KitPage } from '../config/organizer-kit-nav';
import NavExpanderIcon from './NavExpanderIcon.astro';

interface Props {
nav: KitNavNode[];
/** Path of the page being rendered, used to mark the current link. */
currentPath: string;
}

const { nav, currentPath } = Astro.props;

const normalize = (path: string) => (path.endsWith('/') ? path : `${path}/`);
const current = normalize(currentPath);
const isCurrent = (href: string) => normalize(href) === current;

const isGroupActive = (pages: KitPage[]) => pages.some((page) => isCurrent(page.href));
---

<nav id="docs-nav" class="docs-nav" aria-label="Organizer Kit">
<a class="nav-list-link nav-list-link--home" href={`${KIT_BASE}/`} aria-current={isCurrent(`${KIT_BASE}/`) ? 'page' : undefined}>
Organizer Kit
</a>

<ul class="nav-list">
{nav.map((node) => {
if (node.kind === 'page') {
const active = isCurrent(node.page.href);
return <li class:list={['nav-list-item', { active }]}>
<a
class:list={['nav-list-link', { active }]}
href={node.page.href}
aria-current={active ? 'page' : undefined}
>
{node.page.title}
</a>
</li>;
}
const groupActive = isGroupActive(node.pages);
return <li class:list={['nav-list-item', { active: groupActive }]}>
<button class="nav-list-expander nav-list-toggle btn-reset" type="button" aria-label={`${node.title} submenu`} aria-expanded={groupActive ? 'true' : 'false'}><NavExpanderIcon /></button>
<button class="nav-list-link nav-list-link--static nav-list-group-toggle nav-list-toggle btn-reset" type="button" aria-expanded={groupActive ? 'true' : 'false'}>{node.title}</button>
<ul class="nav-list">
{node.pages.map((page) => {
const active = isCurrent(page.href);
return <li class:list={['nav-list-item', { active }]}>
<a class:list={['nav-list-link', { active }]}
href={page.href}
aria-current={active ? 'page' : undefined}
>
{page.title}
</a>
</li>;
})}
</ul>
</li>;
})}
</ul>
</nav>

<script is:inline>
(() => {
const nav = document.getElementById('docs-nav');
if (!nav) return;
nav.addEventListener('click', (event) => {
let target = event.target;
while (target && !(target.classList && target.classList.contains('nav-list-toggle'))) target = target.parentNode;
if (!target) return;
event.preventDefault();
const item = target.parentNode;
const expanded = String(item.classList.toggle('active'));
item.querySelectorAll('.nav-list-toggle').forEach((toggle) => toggle.setAttribute('aria-expanded', expanded));
});
})();
</script>
22 changes: 22 additions & 0 deletions pcd-website/src/components/DocsTableOfContents.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
import type { MarkdownHeading } from 'astro';

interface Props {
headings: MarkdownHeading[];
}

/** Keep the page TOC concise; deeper headings make it harder to scan. */
const { headings } = Astro.props;
const anchors = headings.filter((heading) => heading.depth === 2);
---

{anchors.length > 0 && (
<nav class="docs-page-toc" aria-label="On this page">
<p class="docs-page-toc__title">On this page</p>
<ul>
{anchors.map((heading) => (
<li><a href={`#${heading.slug}`}>{heading.text}</a></li>
))}
</ul>
</nav>
)}
1 change: 1 addition & 0 deletions pcd-website/src/components/ExternalLinkIcon.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7" /><path d="M8 7h9v9" /></svg>
Loading