website

Personal website
git clone https://github.com/michaelneuper/website.git
README | Files | Log | Refs

website/src/components/home/Projects.astro (2886B)


---
// Projects: a wheel of cards, out in the margin on wide screens, and the
// project picked on it, written up with its demo. Each project is a file in
// src/content/projects; src/scripts/home/projects.js turns the wheel, and a
// project's demo is src/scripts/demos/<demo>.js. Without JavaScript the
// projects are a plain list.
import { getCollection, render } from "astro:content";
import Buffer from "../frame/Buffer.astro";
import ProjectThumb from "./ProjectThumb.astro";
import "../../styles/home/projects.css";
import "../../styles/demos/demo.css";

const CATS: Record<string, string> = { hw: "hardware", sw: "software", math: "maths", sec: "security" };
const projects = await Promise.all(
  (await getCollection("projects"))
    .sort((a, b) => a.data.order - b.data.order)
    .map(async (p) => ({ ...p.data, Body: (await render(p)).Content })),
);
---
<Buffer id="projects" file="~/projects/" mode="Dired" metaAttrs={{ "data-pj": "meta" }}>
  <h2 class="h2">Projects</h2>

  <div class="pj">
    <div class="pj-wheel">
      <svg class="pj-spine" aria-hidden="true"><path /></svg>
      <div class="pj-cards" role="tablist" aria-label="Projects" aria-orientation="vertical">
        {projects.map((p, i) => (
          <button type="button" class="pj-card" id={`pj-${p.name}`} role="tab" data-cat={p.cat} aria-selected={i === 0 ? "true" : "false"} aria-controls="project-detail" tabindex={i === 0 ? 0 : -1}>
            <span class="pj-thumb"><ProjectThumb demo={p.demo} /></span>
            <span class="pj-label">{p.name}</span>
            {/* the category, which the card shows only as the colour of its swatch */}
            <span class="sr-only">, {CATS[p.cat] ?? p.cat}</span>
          </button>
        ))}
      </div>
      <p class="pj-hint" aria-hidden="true">drag or <kbd>↑</kbd> <kbd>↓</kbd></p>
    </div>

    <article class="pj-detail" id="project-detail" role="tabpanel"></article>

    <ol class="pj-list">
      {projects.map((p) => (
        <li class="proj" data-name={p.name} data-cat={p.cat} data-demo={p.demo} data-demo-note={p.demoNote}>
          <p class="proj-cat">{CATS[p.cat] ?? p.cat}</p>
          <h3 class="proj-name">{p.title}</h3>
          <p class="proj-sum">{p.summary}</p>
          <dl class="proj-facts">
            {p.stack && <div><dt>stack</dt><dd>{p.stack}</dd></div>}
            {p.links.length > 0 && <div><dt>links</dt><dd>{p.links.map((l) => <a href={l.href}>{l.text}</a>)}</dd></div>}
            {p.note && <div><dt>source</dt><dd>{p.note}</dd></div>}
          </dl>
          <div class="proj-body"><p.Body /></div>
        </li>
      ))}
    </ol>
  </div>
</Buffer>

<script>
  import { initProjects } from "../../scripts/home/projects.js";
  // the 8-bit computer's M-x commands; its project mounts the board as a demo
  import "../../scripts/demos/computer/commands.js";

  initProjects();
</script>