Card
Box with a title and content.
Fridays
Build anything with other people who build things. Room and snacks provided.
Soldering
Learn to solder your first badge. Irons and kits on us.
workshop nov 11
Code
Copy into src/components/ph/ .
Card.astro
---
type Props = {
href?: string;
title?: string;
class?: string;
};
const { href, title, class: className } = Astro.props;
const Tag = href ? "a" : "div";
const external = href && /^https?:\/\//.test(href);
---
<Tag
href={href}
target={external ? "_blank" : undefined}
rel={external ? "noopener noreferrer" : undefined}
class:list={[
"flex flex-col gap-3 border border-border bg-card p-5 text-card-foreground no-underline",
href && "transition-colors duration-150 ease-quint hover:border-foreground",
className,
]}
>
{Astro.slots.has("media") && <div class="-mx-5 -mt-5 border-b border-border"><slot name="media" /></div>}
{title && <h3 class="ph-headline text-3xl">{title}</h3>}
<div class="flex flex-col gap-2 leading-[1.45]"><slot /></div>
</Tag>
Copy into components/ph/ (or src/components/ph/).
card.tsx
import type { ReactNode } from "react";
import { cn } from "./cn";
type CardProps = {
children?: ReactNode;
href?: string;
title?: string;
media?: ReactNode;
className?: string;
};
export function Card({ children, href, title, media, className }: CardProps) {
const classes = cn(
"flex flex-col gap-3 border border-border bg-card p-5 text-card-foreground no-underline",
href && "transition-colors duration-150 ease-quint hover:border-foreground",
className,
);
const inner = (
<>
{media && <div className="-mx-5 -mt-5 border-b border-border">{media}</div>}
{title && <h3 className="ph-headline text-3xl">{title}</h3>}
<div className="flex flex-col gap-2 leading-[1.45]">{children}</div>
</>
);
if (!href) return <div className={classes}>{inner}</div>;
const external = /^https?:\/\//.test(href);
return (
<a href={href} target={external ? "_blank" : undefined} rel={external ? "noopener noreferrer" : undefined} className={classes}>
{inner}
</a>
);
}
cn.ts
export const cn = (...parts: (string | false | null | undefined)[]) => parts.filter(Boolean).join(" ");