Theme
On this page (4)
Component Skill — components/team-member/component-skill.md

Native basis

An <article> labelled by the name: a portrait (descriptive alt) with an optional location tag and a list of icon links over its bottom edge, then the name, the role and a short bio.

Web Platform APIs

<article>:has()object-fitaspect-ratioprefers-reduced-motion

Classes

.mk-team-member.mk-team-member-portrait.mk-team-member-photo.mk-team-member-tag.mk-team-member-links.mk-team-member-body.mk-team-member-name.mk-team-member-role.mk-team-member-bio

Data attributes

data-variant="horizontal", data-variant="compact".

§Team grid

Portrait cards: hover a card - or Tab into it - and the LinkedIn, GitHub, X and email links slide up over the photo (labelled per person; always shown on touch screens). A location tag sits in the corner.

§Horizontal

data-variant='horizontal': a card with the portrait beside the bio and the links always shown - for leadership and board pages.

§Compact

data-variant='compact': a round portrait with a ring, the name and the role on one row - for long team lists.

§CSS view file

/* -- Team Member (website · Company) --------------------------------------
   Presents one person with a portrait, role, and biography - a portrait card whose links slide in on hover, a profile beside its bio, or a compact row for long lists. */
@layer components {
  /* every box in the block measures border-box - the same on any page */
  :where(.mk-team-member, .mk-team-member *, .mk-team-member ::before, .mk-team-member ::after) { box-sizing: border-box; }
  .mk-team-member { display: flex; flex-direction: column; gap: 0.875rem; }
  .mk-team-member-portrait { position: relative; overflow: hidden; isolation: isolate; border-radius: var(--radius-xl); background: var(--muted);
    &::after { content: ""; position: absolute; inset: 40% 0 0; z-index: 0; background: linear-gradient(transparent, oklch(0.12 0.02 260 / 0.65)); opacity: 0; transition: opacity 300ms ease; pointer-events: none; }
  }
  .mk-team-member-photo { display: block; width: 100%; aspect-ratio: 4 / 5; object-fit: cover; transition: scale 600ms cubic-bezier(.2,.7,.2,1); }
  .mk-team-member:is(:hover, :focus-within) .mk-team-member-photo { scale: 1.05; }
  .mk-team-member:is(:hover, :focus-within) .mk-team-member-portrait::after { opacity: 1; }
  .mk-team-member-tag {
    position: absolute;
    top: 0.75rem;
    left: 0.75rem;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.1875rem 0.5rem 0.1875rem 0.375rem;
    border-radius: 999px;
    background: color-mix(in oklch, var(--background) 82%, transparent);
    color: var(--foreground);
    font-size: 0.75rem;
    font-weight: 600;
    backdrop-filter: blur(8px);
    & svg { width: 0.75rem; height: 0.75rem; }
  }
  .mk-team-member-links {
    position: absolute;
    inset: auto 0.75rem 0.75rem;
    z-index: 1;
    display: flex;
    gap: 0.375rem;
    margin: 0;
    padding: 0;
    list-style: none;
    opacity: 0;
    translate: 0 0.75rem;
    transition: opacity 250ms ease, translate 250ms ease;
    & a { display: grid; place-items: center; width: 2.25rem; height: 2.25rem; border-radius: 999px; background: oklch(1 0 0 / 0.92); color: oklch(0.2 0 0); box-shadow: var(--shadow-sm); transition: background-color 150ms ease, color 150ms ease; }
    & a:hover { background: var(--primary); color: var(--primary-foreground); }
    & a:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
    & svg { width: 1rem; height: 1rem; }
  }
  .mk-team-member:is(:hover, :focus-within) .mk-team-member-links { opacity: 1; translate: 0 0; }
  @media (hover: none) {
    .mk-team-member-links { opacity: 1; translate: 0 0; }
    .mk-team-member-portrait::after { opacity: 1; }
  }
  .mk-team-member-body { display: grid; gap: 0.125rem; }
  .mk-team-member-name { margin: 0; font-size: 1.125rem; font-weight: 600; letter-spacing: -0.01em; }
  .mk-team-member-role { margin: 0; color: var(--primary); font-size: 0.875rem; font-weight: 500; }
  .mk-team-member-bio { margin: 0.5rem 0 0; color: var(--muted-foreground); font-size: 0.9375rem; line-height: 1.6; text-wrap: pretty; }
  .mk-team-member[data-variant="horizontal"] {
    display: grid;
    grid-template-columns: minmax(8rem, 40%) minmax(0, 1fr);
    align-items: center;
    gap: 1.5rem;
    padding: 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    background: var(--card);
    color: var(--card-foreground);
    & .mk-team-member-photo { aspect-ratio: 1; }
    & .mk-team-member-links { opacity: 1; translate: 0 0; inset: auto 0.5rem 0.5rem; gap: 0.25rem; }
    & .mk-team-member-links a { width: 2rem; height: 2rem; }
    & .mk-team-member-portrait::after { opacity: 1; }
    & .mk-team-member-name { font-size: 1.25rem; }
  }
  .mk-team-member[data-variant="compact"] {
    flex-direction: row;
    align-items: center;
    gap: 0.875rem;
    & .mk-team-member-portrait { flex: none; width: 3rem; height: 3rem; border-radius: 50%; box-shadow: 0 0 0 2px var(--background), 0 0 0 3px var(--border); }
    & .mk-team-member-photo { height: 100%; aspect-ratio: 1; }
    & .mk-team-member-portrait::after, & .mk-team-member-tag, & .mk-team-member-links { display: none; }
    & .mk-team-member-name { font-size: 0.9375rem; }
    & .mk-team-member-role { color: var(--muted-foreground); font-weight: 400; }
  }
  /* -- Accessibility ------------------------------------------- */
  @media (prefers-reduced-motion: reduce) {
    .mk-team-member-photo, .mk-team-member-links, .mk-team-member-portrait::after { transition: none; }
    .mk-team-member:is(:hover, :focus-within) .mk-team-member-photo { scale: none; }
  }
  @media (forced-colors: active) {
    .mk-team-member-links a { border: 1px solid CanvasText; }
    .mk-team-member[data-variant="horizontal"] { border-color: CanvasText; }
  }
}

Comments, ideas or improvements? Edit this page's source on GitHub