add projects section
vi did:web:vt3e.cat
Mon, 11 May 2026 02:40:09 +0100
4 files changed,
338 insertions(+),
1 deletions(-)
A
src/components/Project.astro
@@ -0,0 +1,160 @@
+--- +import type { Project } from "@/types"; + +interface Props { + project: Project; +} + +const { + project: { name, description, languages, links }, +} = Astro.props; + +const { source, live, internal } = links; +const externalLinks = [ + { href: source, label: "source" }, + { href: live, label: "live" }, +].filter((link) => link.href); +--- + +<div class="project"> + <div class="meta"> + <h3>{name}</h3> + <p>{description}</p> + { + languages && languages.length > 0 && ( + <div class="languages"> + {languages.map((lang) => ( + <span + class="language" + title={lang.name} + style={{ "--colour": `var(--${lang.colour})` }} + > + {lang.name} + </span> + ))} + </div> + ) + } + </div> + + <div class="links"> + { + externalLinks.length > 0 && ( + <div class="link-section left"> + {externalLinks.map((link) => ( + <a href={link.href} target="_blank" rel="noreferrer"> + {link.label} + </a> + ))} + </div> + ) + } + + { + internal && ( + <div class="link-section right"> + <a href={`/p/${internal}`}>read about it</a> + </div> + ) + } + </div> +</div> + +<style> + .project { + display: flex; + flex-direction: column; + padding: 0.5rem; + } + + .meta { + display: flex; + flex-direction: column; + + h3 { + font-weight: 700; + font-size: 1.2rem; + } + + p { + font-size: 0.9rem; + color: hsl(var(--subtext1)); + margin-bottom: 0.5rem; + } + } + + .languages { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 0.25rem; + + .language { + font-size: 0.75rem; + padding: 0.1rem 0.5rem; + border-radius: 1rem; + font-weight: 550; + background-color: hsla(var(--surface0) / 1); + color: hsl(var(--text)); + border: 2px solid hsla(var(--colour) / 0.5); + } + } + + .links { + display: flex; + flex-direction: row; + align-items: center; + gap: 1ch; + margin-top: 1ch; + + .link-section { + display: flex; + flex-direction: row; + border-radius: 5rem; + overflow: hidden; + border: 2px solid hsla(var(--overlay0) / 0.25); + + background-color: hsla(var(--accent) / 0.1); + + &:has(a:focus-visible) { + outline-color: hsl(var(--accent)); + } + } + + a { + display: inline-block; + color: hsl(var(--text)); + padding: 0.5rem 0.75rem; + text-decoration: none; + line-height: 1; + font-weight: 600; + outline: none; + position: relative; + + & + &::before { + content: ""; + position: absolute; + left: -1px; + width: 2px; + height: 50%; + background-color: hsla(var(--accent) / 0.2); + } + + &:focus-visible, + &:hover { + background-color: hsla(var(--accent) / 1); + color: hsl(var(--on-accent)); + } + + &:active { + background-color: hsla(var(--accent) / 0.8); + color: hsl(var(--on-accent)); + } + } + + .right { + margin-left: auto; + } + } +</style>
A
src/constants.ts
@@ -0,0 +1,28 @@
+export type Language<T extends string = string> = { + name: T; + short: string; + colour: string; +}; + +export const LANGUAGES = { + TypeScript: { + name: "TypeScript", + short: "TS", + colour: "blue", + }, + Vue: { + name: "Vue", + short: "Vue", + colour: "green", + }, + Astro: { + name: "Astro", + short: "Astro", + colour: "green", + }, + Nix: { + name: "Nix", + short: "Nix", + colour: "blue", + }, +} as const satisfies Record<string, Language>;
M
src/pages/index.astro
→
src/pages/index.astro
@@ -1,8 +1,144 @@
--- import ProjectComponent from "@/components/Project.astro"; +import { LANGUAGES as LANG } from "@/constants"; import Layout from "@/layouts/Layout.astro"; + +import type { Project } from "@/types"; + +type Link = { + title: string; + href: string; +}; +const links: Link[] = [ + { + title: "tangled (git)", + href: "https://tangled.org/did:web:vt3e.cat", + }, + { + title: "bluesky", + href: "https://witchsky.app/profile/did:web:vt3e.cat", + }, +]; + +const projects: Project[] = [ + { + name: "bluebell", + description: "a pretty client for bluesky on the web.", + links: { + live: "https://bbell.vt3e.cat", + source: "https://tangled.org/bbell.vt3e.cat", + }, + languages: [LANG.Vue, LANG.TypeScript], + }, + { + name: "website", + description: "this website!!", + links: { + source: "https://tangled.org/vt3e.cat/catsite", + }, + languages: [LANG.Astro, LANG.TypeScript], + }, + { + name: "flake", + description: "my nix flake used on my server & desktop.", + links: { + source: "https://tangled.org/vt3e.cat/flake", + }, + languages: [LANG.Nix], + }, + { + name: "cmd", + description: "a fully typesafe, lighweight cli framework", + links: { + source: "https://git.vt3e.cat/cmd.git", + }, + languages: [LANG.TypeScript], + }, +]; --- <Layout> - bweh + <header class="page-head"> + <div class="title"> + <h1>viii</h1> + <p class="pronouns">it/she</p> + </div> + <p class="subtitle"> + demifem {"{rat,cat}"}girl frontend developer thing! + </p> + </header> + <section class="links"> + <header><h2>links</h2></header> + <ul> + { + links.map((link) => ( + <li> + <a class="link" href={link.href}> + {link.title} + </a> + </li> + )) + } + </ul> + </section> + <section class="projects"> + <header> + <h2>projects</h2> + <p>some things that are public that i am working on!</p> + </header> + <div class="project-list"> + {projects.map((project) => <ProjectComponent project={project} />)} + </div> + </section> </Layout> + +<style> + .links ul { + display: inline-flex; + flex-direction: column; + gap: 0.5rem; + list-style: none; + + li { + margin-left: 2ch; + } + + a { + --fg: var(--subtext1); + + display: inline-flex; + align-items: center; + gap: 1ch; + + font-size: 1.25rem; + font-weight: 500; + + svg { + width: 1.5rem; + height: 1.5rem; + } + } + } + + .projects .project-list { + border-radius: 1.5rem; + overflow: hidden; + border: 2px solid hsla(var(--overlay0) / 0.25); + + margin-top: 0.75rem; + display: flex; + flex-direction: column; + background-color: hsl(var(--container-bg)); + + .project:first-child { + padding-top: 0.75rem; + } + + .project:not(:last-child) { + border-bottom: 2px solid hsla(var(--overlay0) / 0.25); + } + + /*margin-left: -0.5rem; + margin-right: -0.5rem;*/ + } +</style>
A
src/types.ts
@@ -0,0 +1,13 @@
+import type { Language } from "./constants"; + +export type Project = { + name: string; + description: string; + languages: Language[]; + links: { + source?: string; + live?: string; + /** the link to the relevant page on this site */ + internal?: string; + }; +};