Skip to main content

Experience Primitives

Presentation primitives for job history cards, experience lists, and supporting skill badges

Overview

These primitives help you present work history without bundling in data fetching, date formatting, or HTML sanitization logic. Use them once your experience data is already normalized into display-ready values.

Key boundary: ExperienceCard.description is rendered with set:html. Only pass sanitized or otherwise trusted rich text.

Contracts

ExperienceCard props

interface Props {
  logo: string;
  title: string;
  company: string;
  description: string;
  start: string;
  end: string;
}

The default slot is optional and is typically used for a SkillIconList or other supporting metadata rendered above the trusted description block.

ExperienceList contract

ExperienceList takes no props and renders a semantic unordered list with stack spacing between entries.

<ExperienceList>
  <li>
    <ExperienceCard ... />
  </li>
</ExperienceList>

SkillIconList and SkillIcon contract

interface SkillIconListProps {
  title?: string;
}

<SkillIconList title="Stack:">
  <SkillIcon>
    <YourIconComponent title="TypeScript" />
  </SkillIcon>
</SkillIconList>

SkillIcon is slot-only. Consumers provide the actual icon or label markup.

Example Usage

Composed example

<ExperienceCard
  logo="/logo.png"
  title="Senior Developer"
  company="ACME Corp"
  description="<p>Trusted HTML description</p>"
  start="2020-01"
  end="2023-12"
>
  <SkillIconList title="Stack:">
    <SkillIcon>
      <YourIconComponent title="TypeScript" />
    </SkillIcon>
  </SkillIconList>
</ExperienceCard>

Live example

  • Senior Software Engineer

    Example Technologies

    -

    Stack:

    Led development of distributed systems and mentored junior developers. Architected scalable microservices handling millions of requests daily.

  • Software Developer

    Startup Inc

    -

    Stack:

    Built initial product MVP and established engineering practices. Worked across the full stack from database design to frontend implementation.

Consumer Responsibilities

Data preparation

  • Fetch and normalize experience data before rendering these primitives.
  • ExperienceCard currently uses the same start and end strings for both datetime attributes and visible text.
  • Pass values that are valid for datetime and also acceptable display strings. If you need localized display text, wrap the primitive or extend its API rather than passing conflicting formats.

Trusted HTML boundary

ExperienceCard.description is a trusted HTML boundary. Sanitize markdown output, CMS content, or user-authored rich text before it reaches the component.

Do not treat the description prop as a convenience for arbitrary HTML fragments from untrusted sources.

Icons and imagery

The current primitive uses the experience title as the logo image alt text. Treat that as a limitation: if the role title is not the right accessible name for the logo, wrap the component or extend the API rather than assuming the built-in alt text is sufficient.

SkillIcon only handles spacing. Consumers are responsible for accessible icon markup, labels, and any decorative/icon-only hiding behavior.

Accessibility and Layout Notes

  • Preserve list semantics by wrapping each experience entry in an <li>.
  • ExperienceCard uses semantic article and time elements for machine-readable timelines.
  • The card layout collapses to a single column on smaller screens.
  • The shared package handles spacing and typography tokens; consumer apps own surrounding section headings and context.