← All changelog entries
April 27, 2026 · Feature

Projects, the surface — gallery, detail, header popdown, and the working-group content collection

Stood up the Projects surface for FullStack VC end-to-end: a page spec authored as the input artifact (`context-v/sitemap/pages/Page__projects-index.astro.md`), a `projects` content collection with a Zod schema covering identity, lifecycle (active / proposed / archived), working-group metadata, external links, and display hints; six active projects ported as content (Content Farm, MemoPop AI, Astro Knots, Augment It, Context Vigilance, Lossless Flavored Markdown) plus one proposed (Dealflow OS) and one archived (LP Update Bot) for layout testing; a new `HeroBannerWithMessageHierarchy.astro` that composes the existing `HeroContentCoreMessage` over an image banner (3 heights × 3 overlays × 2 alignments); a single `Section__ProjectGallery.astro` with four variants — `portfolio` (Working Group Cards) and `mission-brief` (numbered dossier) as the two 'standard' creative concepts road-tested in the design system, plus `lite` for proposed projects and `shelf` for the archive; a `JumboPopdown__Projects.astro` mounted in `Header.astro` that surfaces active and proposed projects from any page (hover-open on pointer:fine, click-toggle, Esc-closes, arrow-key navigation, role=menu/menuitem, mobile becomes a bottom drawer); a `ProjectMetadataDisplay.astro` strip and `ProjectReader.astro` body wrapper for the detail page (LFM pipeline + Sources). Three new semantic tokens added to `theme.css` (`--color-status-active`, `--color-status-proposed`, `--color-status-archived`) so status pills carry color through the two-tier system without any component reading `--color__*` directly. Three new design-system catalog entries land in `/design-system/sections/`, `/design-system/heroes/`, and `/design-system/components/` per the maintenance motion. The page spec format itself is new for this site — copying the LFM spec's frontmatter shape (initial-draft / current-draft / final-draft / first-published / last-updated dates, `at_semantic_version`, structured authors, image_prompt) so future page specs read consistently.

Authors
Michael Staton
Augmented with
Claude Code on Opus 4.7
Tags
#Projects#Working-Groups#Content-Collection#Page-Spec#Hero-Banner#Section-Gallery#Jumbo-Popdown#Header-Navigation#LFM#Design-System#Two-Tier-Tokens#Component-Variants#Sitemap

Changelog — 2026-04-27 (02)

Projects, the surface

Why Care?

For the community (the people doing the work). FullStack VC is a peer-learning community organized around working groups that ship projects together. Until today the community had a Dojo page, a Webinars schedule, and a Changelog — but nowhere on the site that said: here’s what we’re working on, here’s who’s in the room, here’s how to join. The Projects surface fixes that. A new visitor can land on /projects/, see five-or-six active working groups laid out as portfolio cards, click into any one and read its full spec, see its leads and cadence, and find the link to its repo or RSVP. They can also propose a new project, or read what we deliberately archived and why — because failure modes are some of the best content this community produces.

For Mike (the firm). This is the first proper content collection on this site that isn’t a webinar or a long-form chapter. The shape we just codified — projects with active | proposed | archived lifecycle, working-group metadata, popdown ordering, a status field that’s the source of truth (not directory placement) — is a template that the next collection (events? client engagements? case studies?) can copy. The JumboPopdown__Projects component is also reusable: anywhere on the site that needs a header-mounted dropdown showing a curated list of anything (events, partners, case studies), this component is the pattern to copy and adapt.

For lay readers wondering why this took a whole afternoon. Because we did it the disciplined way:

  1. We wrote the spec first — a real document at context-v/sitemap/pages/Page__projects-index.astro.md that names every component, defines every prop, lists every open question, and only then does the implementation start. The spec is the artifact you’d hand a new contributor.
  2. We introduced two creative concepts (Working Group Cards vs. Mission Briefs) for the active section and shipped both into the design system, so we can pick later without rebuilding.
  3. We extended the two-tier token system for status colors instead of hardcoding green/amber/grey, so when a future client wants to reskin the site, status pills move with the brand and not against it.
  4. We caught a small naming convention drift during review (the Txt suffix for text-content props vs. the visual element names like eyebrow) and saved it to memory so future components don’t re-litigate it.

The result is that the same change shipped: a spec, a content schema, six pieces of real content, three new components, two new pages, three design-system entries, and the wiring into the site header. End-to-end. This is what “the disciplined way” looks like in practice.

The Idea in One Diagram

              ┌─────────────────────────────────────────────┐
              │         src/content/projects/*.md           │
              │   (status: active | proposed | archived)    │
              └────────────────────┬────────────────────────┘

                  ┌────────────────┼─────────────────┐
                  │                │                 │
                  ▼                ▼                 ▼
       getStaticPaths()  loadProjectsByStatus  loadProjectsForPopdown
                  │                │                 │
                  ▼                ▼                 ▼
       /projects/[slug]/    /projects/        Header.astro
       (detail page)        (gallery page)    (every page)
                                   │                 │
                                   ▼                 ▼
                       Section__ProjectGallery   JumboPopdown__Projects
                          ├─ portfolio              (active + proposed,
                          │  (active)                role=menu, hover/click,
                          ├─ mission-brief           Esc/arrows, mobile drawer)
                          │  (alternate concept)
                          ├─ lite (proposed)
                          └─ shelf (archived)

All loaders are pure (read collection → filter → sort → return). Zero runtime fetches. The popdown does not phone home.

What Got Built

The page spec — context-v/sitemap/pages/Page__projects-index.astro.md

A 540-line spec authored before the code, listing problem, goal, prior art, page composition, content model (Zod schema in full), component inventory with prop interfaces and visual contracts, routing & data flow, SEO, accessibility, theme & token compliance, design-system integration, six open questions for resolution, acceptance criteria, and an implementation phase plan. Frontmatter copies the LFM spec’s shape (date_authored_initial_draft, date_authored_current_draft, at_semantic_version, structured authors array, image_prompt) so the next page spec on this site doesn’t have to reinvent the form.

Content collection — projects

Added to src/content.config.ts next to pages, webinars, changelog, ventureWorkflows. Loader globs src/content/projects/**/*.md while excluding README.md (the README is documentation, not a project entry). The Zod schema covers:

z.object({
  // Identity
  title, slug?, lede, summary?, scope?,

  // Lifecycle — status is the source of truth
  status: z.enum(['active', 'proposed', 'archived']),
  date_initiated?, date_archived?, date_last_activity?,

  // Working group
  working_group_name?, working_group_leads?, working_group_members?,
  members_count?, cadence?, rsvp_url?,

  // External surfaces
  links: { repo?, site?, demo?, figma?, spec?, notes?, videos?[] },

  // Discovery
  tags?, category?, origin?,

  // Display
  hero_image?, thumbnail?, icon?, banner_overlay?, card_accent?,

  // Behavior
  publish, feature_in_popdown, popdown_order?,

  // Authorship + versioning
  authors?, augmented_with?, at_semantic_version?,
  date_created?, date_modified?,
})

Two design decisions worth naming:

  • status is authoritative, directory is for ergonomics. Authors can drop a file in active/, proposed/, or archived/, but the status field is what filtering reads. This means a project graduating from proposed → active is a one-line frontmatter edit, not a file move.
  • feature_in_popdown + popdown_order give authors control over the header. Not every project needs to crowd the popdown. The default is feature_in_popdown: true, and popdown_order is honored before falling back to date_last_activity desc. The archived project (LP Update Bot) has feature_in_popdown: false so it doesn’t ghost the dropdown.

Six active projects ported (plus 1 proposed, 1 archived)

ProjectStatusWorking group cadence
Content FarmactiveBi-weekly · Wednesdays 10:00 PT
MemoPop AIactiveWeekly · Tuesdays 14:00 PT
Astro KnotsactiveBi-weekly · Mondays 11:00 PT
Augment ItactiveWeekly · Thursdays 13:00 PT
Context VigilanceactiveWeekly · Fridays 11:00 PT
Lossless Flavored MarkdownactiveBi-weekly · Mondays 14:00 PT
Dealflow OSproposedLooking for 4–6 members
LP Update BotarchivedArchived 2026-02-28

Each body is real prose, not lorem-ipsum — the layouts get tested against the kind of copy they’ll actually carry. LFM was added per the spec note that “the Lossless Flavored Markdown package could also be included as a current project” — and now it is.

HeroBannerWithMessageHierarchy.astro — image banner that wraps the existing message hierarchy

Located at src/components/heroes/HeroBannerWithMessageHierarchy.astro. Wraps the existing HeroContentCoreMessage (the eyebrow → h1 → h2 → supporting → CTA composition used on /dojo) inside a full-bleed image banner. Composition matrix:

  • 3 heightsshort (~20–26rem), standard (~26–35rem), tall (~35–45rem)
  • 3 overlaysgradient (mode-aware horizontal scrim that’s heavier where the content sits), scrim (uniform vertical fade), none (caveat: only for cooperative photography)
  • 2 alignmentsleft (default — content offset from the left gutter) or center

The overlay uses color-mix(in srgb, var(--color-background) 92%, transparent) style mixes, so the same overlay rule produces a near-black scrim in dark mode, a near-bone scrim in light mode, and a deep-violet scrim in vibrant mode — without any per-mode rules. All three are inspectable side-by-side at /design-system/heroes/hero-banner-with-message-hierarchy with the ModeThemeToggle at the top of the page.

The component reads only Tier-2 semantic tokens (--color-background, --color-text, --color-primary, --fx-headline-gradient, --font-display, --font-code, --font-body). No component in this batch references --color__* named tokens directly — the two-tier discipline holds.

Section__ProjectGallery.astro — one component, four variants, two creative concepts in the design system

A single section component that renders a pre-filtered projects: ProjectEntry[] array as a card grid. The variant is picked by the parent — the component itself doesn’t know about status. Variants:

VariantDefault forCard treatment
portfolioactiveBanner thumbnail (or icon-fallback gradient) + title + lede + leads in monospace + tag chips. 1/2/3-column responsive grid. Default standard concept.
mission-brief(alternate active)01/02/03 number badge in primary color + monospace eyebrow (category) + title + paragraph (summary or lede) + cadence/status footer with dashed top rule. Alternate standard concept — visually echoes AreasOfVentureGrid.
liteproposedNo thumbnail. Dashed border. Icon + title + lede + “Join the working group →” CTA. 3-column at md+.
shelfarchivedCompact horizontal rows (no card surface, no shadow), separated by hairline borders. Date metadata in monospace at the right.

Both portfolio and mission-brief are road-tested at /design-system/sections/project-gallery so the team can pick the better fit for active. The unused concept doesn’t get deleted — it stays in the design system as a future variant for sections of a different page where dossier rhythm fits better. Variants we don’t choose for this page often turn out to be the right answer for a different page.

Empty-state behavior: when projects.length === 0 and emptyHide: true (the default), the section returns null — no <section> wrapper, no header, no spacer. The proposed and archived sections on /projects/ will simply vanish if the community is between cycles, instead of leaving ghost headings.

Status badges read the new semantic tokens:

.pcard__status[data-status="active"]   { --badge-color: var(--color-status-active); }
.pcard__status[data-status="proposed"] { --badge-color: var(--color-status-proposed); }
.pcard__status[data-status="archived"] { --badge-color: var(--color-status-archived); }

Glow on the dot is box-shadow: 0 0 8px var(--badge-color) — a tiny detail that reads as a heartbeat in dark and vibrant modes, and as a soft halo in light mode.

JumboPopdown__Projects.astro — header-mounted dropdown that surfaces working groups everywhere

Mounted in Header.astro between Dojo and Webinars. Receives a pre-loaded ProjectEntry[] (no runtime fetch — the loader runs at build time inside the header’s frontmatter). Conforms to the patterns in astro-knots/context-v/blueprints/Jumbotron-Popdown-Patterns.md.

Behavior:

  • Trigger — real <button> with aria-haspopup="menu", aria-expanded, and aria-controls. The chevron rotates 180° via CSS when is-open is set.
  • Hover open — only on (pointer: fine) (mouse/trackpad). 80ms open delay, 160ms close delay so a fast pointer pass-through doesn’t flap the panel. Touch falls through to click-toggle.
  • KeyboardEsc closes and returns focus to the trigger; ↑/↓/←/→ move focus between menu items; Home/End jump to first/last; click outside closes.
  • Layout — at ≥720px, a centered floating panel positioned below the trigger with a ~38rem grid; at <720px, a bottom drawer pinned to the viewport bottom with a 1-column stack (it would be a touch-target-hostile postage stamp otherwise).
  • Animationopacity + 6px translate, 180ms ease. Respects prefers-reduced-motion: reduce (transition: none).
  • Two-section layout — Active projects in a 2-column grid; if showProposed: true, a proposed sub-section appears below a dashed rule with the same item shape but a dashed item border. Each item carries a status dot that uses the same --color-status-* tokens as the gallery cards.

Footer row offers two ways out: View all projects → (to /projects/) and Propose a project + (to /projects/propose, which the spec notes is a separate work item).

ProjectMetadataDisplay.astro + ProjectReader.astro — the detail page

projects/[slug].astro polymorphically picks its hero: when hero_image is present in frontmatter, it uses HeroBannerWithMessageHierarchy; otherwise it falls back to a plain HeroContentCoreMessage on a --color-surface band. Below the hero:

  • ProjectMetadataDisplay.astro — a compact card surface containing the status pill, cadence, last-activity timestamp, working-group leads (avatars or initials-monogram fallback) and members, an external-link icon row (repo / site / demo / figma / spec / notes), an optional RSVP CTA, and a scope paragraph. Uses an initials helper ("Michael Staton""MS") so leads without avatar URLs still get a visual anchor.
  • ProjectReader.astro — wraps the LFM-rendered MDAST tree (parseMarkdown(entry.body)) inside a 70ch reading column with prose typography (h2 with display font, h3 sub, body 1.0625rem at 1.75 line-height, links underlined with --color-primary, blockquotes with a primary left border, code chips with --color-surface background). Top chrome: back-to-projects link on the left, optional “Edit on source” link on the right. Renders Sources automatically when tree.data.citations.ordered is non-empty.

The page also ends with a small adjacent-projects nav: previous + next within the same status group. For a 6-project active set, the navigation wraps so chapter 6’s “next” is chapter 1 — useful for browsing without scrolling back to the gallery.

Status tokens added to the two-tier system

Three new Tier-2 semantic tokens in theme.css:

.theme-default {
  /* ... existing brand tokens ... */
  --color-status-active:   var(--color__lime-terminal);
  --color-status-proposed: var(--color__amber-flare);
  --color-status-archived: var(--color__graphite-400);
}

Important detail: the values are the same across all three modes. We don’t redefine status colors per mode because the cognitive mapping (lime = go, amber = pending, grey = past) holds equally in light, dark, and vibrant — and the natural intensity shifts come from the surrounding card surface, which already adapts via --fx-card-bg. One semantic token, one mental model, three mode renders.

Components read var(--color-status-{active|proposed|archived}) only. Nothing in this changelog reads a --color__* token directly. The two-tier discipline that astro-knots/CLAUDE.md codifies stays unbroken.

Design system catalog — three new entries

Per the maintenance motion in astro-knots/context-v/reminders/Design-System-Pages-Per-Site.md, every new component lands in /design-system/ in the same change. Three new sub-pages plus three new entries in the catalog index:

  • /design-system/sections/project-gallery — all four variants rendered with real data, plus an empty-state demonstration.
  • /design-system/heroes/hero-banner-with-message-hierarchy — three banners stacked: standard/gradient/left, short/scrim/center, tall/none/left without CTA.
  • /design-system/components/jumbo-popdown-projects — two staged popdowns (default with proposed, then showProposed: false) plus an a11y checklist.

The design-system index now has a “Sub-pages” section linking to all three, and the existing “Catalog index” gained six new component entries (Hero, Gallery, Popdown, MetadataDisplay, Reader — plus the spec exists as the meta-artifact).

Naming Convention Caught and Codified

During spec review you flagged this: my first draft used eyebrow?: string as a prop in two component interfaces. The visual element is called an eyebrow (it’s typography for the small uppercase tracked-out lead-in above a headline) — but as a prop, the convention you want is the Txt suffix, matching the existing headerTxt / subheaderTxt / supportingTxt shape. So:

<!-- The prop carries content. The class describes the rendered element. -->
<Eyebrow>{contextSetterTxt}</Eyebrow>
<!-- or -->
<h3 class="eyebrow">{contextSetterTxt}</h3>

Two namespaces, deliberately separate: the role of the text is “context setter,” the visual element is “eyebrow.” When the visual treatment changes (eyebrow becomes a kicker, becomes a number badge), the prop name still describes the content’s role — no consumer rename cascade.

Saved to memory as feedback_prop-naming-conventions.md so future components don’t re-litigate this. All three new component prop interfaces now use contextSetterTxt, headingTxt, headerTxt, subheaderTxt, supportingTxt, introTxt, ctaLabelTxt, triggerLabelTxt. The visual element names (eyebrow, headline, heading, subheader, lede, cta-row) appear only as CSS classes and slot identifiers.

Authoring Affordances

  • A new project is a single markdown file. Drop src/content/projects/active/your-project.md with frontmatter (title, lede, status: active, working_group_leads, cadence, tags) and body — the gallery, detail page, and popdown all wire up automatically. No code changes.
  • Promote a project from proposed → active. One-line frontmatter edit (status: proposedstatus: active). It moves between sections automatically. No file move required.
  • Curate the popdown. Set feature_in_popdown: false to hide a project from the header; set popdown_order: 1 to pin to the top. Defaults are sensible (everything visible, ordered by popdown_order then date_last_activity desc, capped at 6 items).
  • Hero image is optional. A project without hero_image gets a clean message-hierarchy hero on a surface band. A project with one gets the full-bleed banner. The page composes either way.
  • Citations work. The detail body flows through @lossless-group/lfm’s parseMarkdown(), which means hex-code footnotes, GFM tables, directive callouts, and the auto-rendered <Sources> list are all available in project bodies. We didn’t ship any cited project content this round, but the pipeline is there for the next one.

Yak-Shaving Deferred

  • /projects/propose page. Linked from the gallery hero CTA and from the popdown footer, but not implemented. Belongs in its own spec. Until then the link 404s — to be addressed before we publish externally.
  • OG image for the index. /projects/index.astro currently uses /imageRep__AgenticVC-Dojo.webp as a placeholder hero banner. A purpose-generated /og/projects.png (and per-project hero images) is a separate banner-generation pass via the existing gen:content-banners script.
  • Lead avatars. Schema accepts avatar URLs, but we don’t have an authoritative source for member portraits. Initials-monogram fallback ships as the default; real avatars trail.
  • Status taxonomy expansion. Spec § Open Questions flagged whether paused (working group dormant but project not archived) and graduated (shipped, no longer needs a working group) deserve their own statuses. Three states ship now; revisit when a real project hits a fourth state.
  • Detail-page hero polymorphism, exotic case. Currently: image present → banner hero, image absent → plain hero. There’s a third case (image present but you want the plain hero treatment) that we didn’t add a flag for. Add a hero_treatment: 'banner' | 'plain' enum if a project ever needs it.
  • Search/filter UI on the gallery. Tags, leads, category — none filterable on the index yet. Defer until 12+ projects exist; six is browsable without help.

Verification

  • pnpm astro check — 5 pre-existing errors in changelog/[id].astro and utils/api-connectors/ideogram.ts. 0 errors introduced by this changeset.
  • pnpm dev — server boots in 1.6s on :4324. All nine new routes return HTTP 200:
    /                                                          HTTP 200
    /projects/                                                 HTTP 200
    /projects/content-farm/                                    HTTP 200
    /projects/lossless-flavored-markdown/                      HTTP 200
    /projects/lp-update-bot/                                   HTTP 200
    /design-system/                                            HTTP 200
    /design-system/sections/project-gallery                    HTTP 200
    /design-system/heroes/hero-banner-with-message-hierarchy   HTTP 200
    /design-system/components/jumbo-popdown-projects           HTTP 200
  • Header popdown wired and verified via raw HTML — six active project items rendered, two footer links, status dots present, role="menu" and role="menuitem" attributes correct.
  • All three modes (light / dark / vibrant) inherit cleanly from the existing --fx-card-* tokens — no per-mode overrides written for any new component.

Files Touched

See frontmatter files_modified. The astro-knots/context-v/sitemap/pages/Page__projects-index.astro.md spec is the input artifact and ships in the same change as the implementation — that’s the discipline. Three design-system pages and one design-system index update ship alongside per the maintenance motion.

Files modified (25)
  • sites/fullstack-vc/context-v/sitemap/pages/Page__projects-index.astro.md
  • sites/fullstack-vc/src/content.config.ts
  • sites/fullstack-vc/src/content/projects/active/content-farm.md
  • sites/fullstack-vc/src/content/projects/active/memopop-ai.md
  • sites/fullstack-vc/src/content/projects/active/astro-knots.md
  • sites/fullstack-vc/src/content/projects/active/augment-it.md
  • sites/fullstack-vc/src/content/projects/active/context-vigilance.md
  • sites/fullstack-vc/src/content/projects/active/lossless-flavored-markdown.md
  • sites/fullstack-vc/src/content/projects/proposed/dealflow-os.md
  • sites/fullstack-vc/src/content/projects/archived/lp-update-bot.md
  • sites/fullstack-vc/src/content/projects/README.md
  • sites/fullstack-vc/src/lib/load-projects.ts
  • sites/fullstack-vc/src/components/heroes/HeroBannerWithMessageHierarchy.astro
  • sites/fullstack-vc/src/components/sections/Section__ProjectGallery.astro
  • sites/fullstack-vc/src/components/sections/ProjectMetadataDisplay.astro
  • sites/fullstack-vc/src/components/sections/ProjectReader.astro
  • sites/fullstack-vc/src/components/ui/menus/JumboPopdown__Projects.astro
  • sites/fullstack-vc/src/components/basics/Header.astro
  • sites/fullstack-vc/src/pages/projects/index.astro
  • sites/fullstack-vc/src/pages/projects/[slug].astro
  • sites/fullstack-vc/src/pages/design-system/sections/project-gallery.astro
  • sites/fullstack-vc/src/pages/design-system/heroes/hero-banner-with-message-hierarchy.astro
  • sites/fullstack-vc/src/pages/design-system/components/jumbo-popdown-projects.astro
  • sites/fullstack-vc/src/pages/design-system/index.astro
  • sites/fullstack-vc/src/styles/theme.css