Skip to main content

Project Primitives

Presentation primitives for repository summaries, project metadata, and responsive project grids

Overview

ProjectCard and ProjectGrid render project summaries without taking over your data pipeline. Use them when you already know which projects to show and need a stable display contract for cards, badges, and grid layout.

  • ProjectCard renders the card, link, optional stars, languages, and topics.
  • ProjectGrid provides the semantic list and responsive grid wrapper.
  • Fetching, ranking, filtering, and limiting metadata remain consumer responsibilities.

Contracts

ProjectCard props

  • name: string — rendered as the card heading and linked title
  • description: string — rendered as plain text and line-clamped to two lines
  • url: string — validated before use in href
  • stars?: number — shown only when greater than zero
  • languages? — optional badge entries with a language name and optional color
  • topics?: string[] — optional tags rendered with a # prefix

ProjectGrid contract

ProjectGrid takes no props. It renders a semantic unordered list and expects list items in the default slot.

The layout starts as a single column and switches to two columns at wider breakpoints.

Example Usage

Mapping project data into the grid

<ProjectGrid>
  {projects.map((project) => (
    <li class="w-full">
      <ProjectCard
        name={project.name}
        description={project.description}
        url={project.url}
        stars={project.stars}
        languages={project.languages}
        topics={project.topics}
      />
    </li>
  ))}
</ProjectGrid>

Live grid example

  • snurble

    stars 12

    Shared Astro design system primitives and design tokens for portfolio-style sites.

    • TypeScript
    • Astro
    • #design-system
    • #astro
    • #catppuccin
  • portfolio-site

    stars 8

    Personal site source that consumes shared cards to present projects and writing.

    • TypeScript
    • JavaScript
    • #portfolio
    • #astro
    • #homepage
  • copilot-cli-tools

    CLI automation workspace with optional metadata omitted when it is not available.

    • TypeScript
    • #copilot
    • #cli
    • #automation

Consumer Responsibilities

Data and state

  • Fetch project data from your API, content collection, or Git provider.
  • Decide ranking, filtering, grouping, and fallback behavior outside the primitives.
  • Limit topics and languages before rendering if your product needs a tighter display budget.
  • Handle loading, empty, and error states around the grid.

Link behavior and accessibility

The card title opens in a new tab with rel="noopener noreferrer". Use a descriptive name so the heading and link text remain meaningful in screen readers, browser tabs, and copied link lists.

Keep ProjectGrid children wrapped in <li> elements so the list structure stays semantic.

Security

URL validation

ProjectCard validates project URLs before rendering them into href. Safe values include:

  • HTTP and HTTPS URLs such as https://example.com
  • Explicit relative URLs such as /projects/snurble, ./page, and ../page

Dangerous or malformed URLs, including javascript:, data:, vbscript:, protocol-relative URLs, and obfuscated schemes with leading whitespace or control characters, are replaced with #.

Consumers should still validate upstream data and prefer fully qualified external URLs.

Color validation

Language colors are validated before they are written into inline styles. Accepted formats include:

  • Hex colors: #RGB, #RRGGBB, #RRGGBBAA
  • RGB and RGBA functions
  • HSL and HSLA functions
  • A limited set of common named colors

Invalid or suspicious strings are ignored so the badge still renders without unsafe styling. Choose colors upstream when they add meaning; omit them when they do not.