Skip to content
Merged
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
3 changes: 2 additions & 1 deletion src/components/Projects/ProjectList.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type { CollectionEntry } from "astro:content";

import ProjectCard from "./ProjectCard.astro";
import { getProjectPath } from "../../lib/projectPages";

interface Props {
heading: string;
Expand All @@ -24,7 +25,7 @@ const { heading, headingId, intro, minColumnWidth = 280, projects } =
<ProjectCard
name={project.data.title}
description={project.data.description}
url={`/projects/${project.id}`}
url={getProjectPath(project.id)}
language={project.data.language ?? null}
stars={project.data.stars ?? 0}
imageUrl={project.data.imageUrl ?? ""}
Expand Down
7 changes: 7 additions & 0 deletions src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,16 @@ const projectsCollection = defineCollection({
liveUrl: z.string().url().optional(),
order: z.number().int().positive(),
repoUrl: z.string().url(),
contributionGuidance: z.string().optional(),
currentState: z.string().optional(),
goalDocUrl: z.string().url().optional(),
goalSummary: z.string().optional(),
nextSteps: z.array(z.string()).optional(),
roadmapDocUrl: z.string().url().optional(),
stars: z.number().int().nonnegative().optional(),
technologies: z.array(z.string()).optional(),
title: z.string(),
whatAndWhy: z.string().optional(),
}),
});

Expand Down
8 changes: 8 additions & 0 deletions src/content/projects/css-community-reset.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@ imageUrl: "https://opengraph.githubassets.com/8ee4947881a615c98b4308d4e1887b9392
language: "CSS"
stars: 16
technologies: ["CSS"]
whatAndWhy: "A small, opinionated reset that collects the CSS defaults I keep wanting at the start of new projects. It exists as a practical notebook: part reusable baseline, part record of community thinking around modern resets."
goalSummary: "Make the opening move of a stylesheet feel less repetitive while still staying readable enough that someone can copy, audit, and adapt it."
currentState: "The project is a compact CSS package with a README that credits the reset work and browser-behavior notes it draws from."
nextSteps:
- "Keep the reset intentionally small as browser defaults and community practice shift."
- "Document the reasoning behind each rule so changes remain easy to review."
- "Add GOAL.md and ROADMAP.md when the project moves into the shared project-page workflow."
contributionGuidance: "The best contributions are focused suggestions with a clear browser behavior, accessibility, or typography reason behind them. Open an issue before broadening the reset."
---
8 changes: 8 additions & 0 deletions src/content/projects/ossreleasefeed-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@ imageUrl: "https://opengraph.githubassets.com/582d0382d7093bcd8c120f0ae972246626
language: "TypeScript"
stars: 0
technologies: ["TypeScript", "RSS", "GitHub"]
whatAndWhy: "OSSReleaseFeed creates permanent feed URLs for GitHub release activity, without asking people to create an account or connect OAuth. It is for the person who still wants the quiet reliability of a feed reader when tracking open source releases."
goalSummary: "Provide lightweight Atom and JSON feeds for repositories discovered through GitHub topics or a user's starred projects, keeping the public surface simple: configure preferences, keep the URL, read releases wherever you already read feeds."
currentState: "The rewrite is under active development and is not yet in public beta. The planned stack combines a React/Vite frontend with a Cloudflare Worker backend built with Hono, Effect, and TypeScript."
nextSteps:
- "Ship the first public beta once the feed-generation path is stable."
- "Harden topic and starred-repository feed behavior before inviting broad usage."
- "Add project docs that can feed the longer public project page without making builds depend on GitHub."
contributionGuidance: "The repository is not open for outside contributions yet. For now, the useful way to engage is to watch the repo, follow issues as they appear, and try the beta once it is announced."
---
8 changes: 8 additions & 0 deletions src/content/projects/web-platform-pulse.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@ imageUrl: "https://opengraph.githubassets.com/e3937f860da25df07b660fed4a1f0e0a42
language: "TypeScript"
stars: 0
technologies: ["TypeScript", "Web Platform"]
whatAndWhy: "Web Platform Pulse is a daily digest for the web platform: Baseline transitions, browser support changes, browser releases, runtime releases, and standards-position movement in one place. It exists because the raw signals are valuable, but scattered."
goalSummary: "Turn many noisy source feeds into a small, trustworthy digest with provenance links, deduplication, and a reader/email delivery model that makes platform change easier to follow."
currentState: "The prototype has a documented pipeline shape, source adapters, significance scoring, persistence, email delivery, and a small reader app. Deployment notes currently target free-tier services."
nextSteps:
- "Keep improving source adapters and correlation so repeated runs stay idempotent."
- "Tune significance scoring as real-world changes reveal what readers actually need first."
- "Move from prototype deployment notes toward a more durable public operating model."
contributionGuidance: "Helpful contributions are likely to be source-specific fixes, digest-quality improvements, or test cases that capture subtle web platform change events. Start with an issue so the signal stays connected to the project goal."
---
16 changes: 16 additions & 0 deletions src/lib/projectPages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type ProjectEntryLike = {
id: string;
};

export function getProjectPath(projectId: string) {
return `/projects/${projectId}`;
}

export function getProjectStaticPaths<ProjectEntry extends ProjectEntryLike>(
projects: readonly ProjectEntry[],
) {
return projects.map((project) => ({
params: { slug: project.id },
props: { project },
}));
}
254 changes: 224 additions & 30 deletions src/pages/projects/[slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,225 @@
import { getCollection } from "astro:content";

import BaseLayout from "../../layouts/BaseLayout.astro";
import { getProjectStaticPaths } from "../../lib/projectPages";

export async function getStaticPaths() {
const projects = await getCollection("projects");

return projects.map((project) => ({
params: { slug: project.id },
props: { project },
}));
return getProjectStaticPaths(projects);
}

const { project } = Astro.props;
const pageTitle = project.data.title;
const pageDescription = project.data.description;
const technologies = project.data.technologies ?? [];
const whatAndWhy = project.data.whatAndWhy ?? project.data.description;
const goalSummary =
project.data.goalSummary ??
"A fuller project goal summary is coming as this page is expanded from the repository docs.";
const currentState =
project.data.currentState ??
"This project has a place in the curated list. The deeper status notes will be filled in as the starter project pages are completed.";
const nextSteps = project.data.nextSteps ?? [
"Write the project-specific goal summary.",
"Connect the page to the repository documentation workflow.",
];
const contributionGuidance =
project.data.contributionGuidance ??
"Follow the repository for now. Contribution notes will become more specific as the project page is completed.";
const projectLinks = [
{
href: project.data.repoUrl,
label: "Repository",
},
...(project.data.liveUrl
? [
{
href: project.data.liveUrl,
label: "Live project",
},
]
: []),
...(project.data.goalDocUrl
? [
{
href: project.data.goalDocUrl,
label: "GOAL.md",
},
]
: []),
...(project.data.roadmapDocUrl
? [
{
href: project.data.roadmapDocUrl,
label: "ROADMAP.md",
},
]
: []),
];
---

<BaseLayout pageTitle={pageTitle} pageDescription={pageDescription}>
<main class="u-container" id="main-content">
<main class="u-container project-page" id="main-content">
<p class="back-link"><a href="/projects">Back to What I want to exist</a></p>
<header class="project-header">
<header class="project-hero">
<p class="project-kicker">Project</p>
<h1>{pageTitle}</h1>
<p class="project-description">{pageDescription}</p>
<p class="project-description">{whatAndWhy}</p>

<dl class="project-facts" aria-label="Project facts">
{
project.data.language && (
<>
<dt>Primary language</dt>
<dd>{project.data.language}</dd>
</>
)
}
<dt>Kind</dt>
<dd>{project.data.category === "demo" ? "Little demo" : "Open source project"}</dd>
</dl>
</header>

{
technologies.length > 0 && (
<section aria-labelledby="technologies">
<h2 id="technologies">Technologies</h2>
<section class="project-section project-section--lead" aria-labelledby="goal">
<h2 id="goal">Goal</h2>
<p>{goalSummary}</p>
</section>

<div class="project-split">
<section class="project-section" aria-labelledby="current-state">
<h2 id="current-state">Where it is</h2>
<p>{currentState}</p>
</section>

<section class="project-section" aria-labelledby="next-steps">
<h2 id="next-steps">What comes next</h2>
<ul class="next-step-list">
{nextSteps.map((nextStep) => <li>{nextStep}</li>)}
</ul>
</section>
</div>

<section class="project-section" aria-labelledby="technologies">
<h2 id="technologies">Technologies used</h2>
{
technologies.length > 0 ? (
<ul class="technology-list">
{technologies.map((technology) => <li>{technology}</li>)}
</ul>
</section>
)
}
) : (
<p>Technology notes will be added as this project page is expanded.</p>
)
}
</section>

<section class="project-section" aria-labelledby="contributing">
<h2 id="contributing">How people can get involved</h2>
<p>{contributionGuidance}</p>
</section>

<section aria-labelledby="project-links">
<section class="project-section project-section--links" aria-labelledby="project-links">
<h2 id="project-links">Project links</h2>
<ul>
<li>
<a href={project.data.repoUrl} rel="noopener noreferrer">
View the repository
</a>
</li>
<ul class="project-link-list">
{
project.data.liveUrl && (
projectLinks.map((link) => (
<li>
<a href={project.data.liveUrl} rel="noopener noreferrer">
Visit the live project
<a href={link.href} rel="noopener noreferrer">
{link.label}
<span aria-hidden="true">&rarr;</span>
</a>
</li>
)
))
}
</ul>
</section>
</main>
</BaseLayout>

<style>
.project-page {
--project-rule: var(--size-1) solid var(--color-border);
}

.back-link {
margin-block-end: var(--size-24);
}

.project-header {
border-block-end: var(--size-1) solid var(--color-border);
margin-block-end: var(--size-32);
padding-block-end: var(--size-24);
.project-hero {
border-block-end: var(--project-rule);
display: grid;
gap: var(--size-24);
grid-template-columns: 1fr;
margin-block-end: var(--spacing-section-default);
padding-block-end: var(--size-32);
}

.project-kicker {
color: var(--color-accent);
font-size: var(--typography-sm-font-size);
font-weight: 700;
letter-spacing: 0;
margin: 0;
text-transform: uppercase;
}

.project-hero h1 {
margin-block: 0;
}

.project-description {
font-size: var(--typography-size-medium);
font-size: var(--typography-size-small-medium);
margin-block: 0;
max-inline-size: 64ch;
}

.project-facts {
border-inline-start: var(--size-4) solid var(--color-accent);
display: grid;
gap: var(--size-4) var(--size-16);
grid-template-columns: max-content 1fr;
margin: 0;
padding-inline-start: var(--size-16);
}

.project-facts dt {
color: var(--color-text-muted);
font-size: var(--typography-sm-font-size);
}

.project-facts dd {
margin: 0;
}

.project-section {
border-block-start: var(--project-rule);
padding-block-start: var(--size-24);
}

.project-section + .project-section,
.project-split + .project-section,
.project-section + .project-split {
margin-block-start: var(--spacing-section-default);
}

.project-section h2 {
margin-block-start: 0;
}

.project-section--lead {
border-block-start: 0;
padding-block-start: 0;
}

.project-split {
display: grid;
gap: var(--size-32);
grid-template-columns: 1fr;
margin-block-start: var(--spacing-section-default);
}

.next-step-list {
padding-inline-start: var(--size-24);
}

.technology-list {
Expand All @@ -87,4 +236,49 @@ const technologies = project.data.technologies ?? [];
border-radius: var(--border-radius-default);
padding: var(--size-4) var(--size-8);
}

.project-link-list {
display: flex;
flex-wrap: wrap;
gap: var(--size-16);
list-style: none;
padding: 0;
}

.project-link-list a {
align-items: center;
border: var(--size-1) solid var(--color-border);
border-radius: var(--border-radius-default);
display: inline-flex;
gap: var(--size-8);
padding: var(--size-8) var(--size-12);
text-decoration: none;
}

.project-link-list a:hover,
.project-link-list a:focus-visible {
border-color: var(--color-accent);
}

@media (min-width: 48rem) {
.project-hero {
grid-template-columns: minmax(0, 1fr) minmax(14rem, 18rem);
}

.project-kicker,
.project-hero h1,
.project-description {
grid-column: 1;
}

.project-facts {
align-self: end;
grid-column: 2;
grid-row: 2 / span 2;
}

.project-split {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
}
</style>
Loading