Theme
On this page (7)
Component Skill — components/blog/component-skill.md

Native basis

The whole card is a single <a> wrapping the preview - one tab stop, one click target. Excerpt clamping uses -webkit-line-clamp (shipped everywhere), media uses aspect-ratio; authors compose the Avatar component.

Web Platform APIs

line-clampaspect-ratioContainer queries

Classes

.mk-blog.mk-blog-head.mk-blog-head-copy.mk-blog-eyebrow.mk-blog-title.mk-blog-desc.mk-blog-see-all.mk-blog-grid.mk-blog-card.mk-blog-media.mk-blog-body.mk-blog-meta.mk-blog-category.mk-blog-read.mk-blog-copy.mk-blog-card-title.mk-blog-excerpt.mk-blog-author.mk-blog-author-name.mk-blog-author-date

§Default

Each card is one link; the whole card is clickable.

§Featured post

data-variant='featured' on .mk-blog-grid: the first post spans two columns and two rows from 44rem; the others fill the column beside it.

§List

data-variant='list': rows with the picture beside the text and a rule between them - the author line steps aside.

§Minimal

data-variant='minimal': text-only posts (no figure) under a strong rule - for a quiet, editorial page.

§Overlay

data-variant='overlay': the text sits on the picture over a dark scrim; the picture zooms a little on hover.

data-variant='carousel': one row that scrolls sideways and snaps per post; category links (.mk-blog-filters) above it - the current one is filled.

§CSS view file

/* -- Blog (marketing) ----------------------------------------------
   Insights grid: media card with category/read-time meta, line-clamped
   excerpt, and author row composed from the avatar component. */
@layer components {
  .mk-blog {
    container: mk-blog / inline-size;
    display: flex;
    flex-direction: column;
    gap: 3rem;
    width: 100%;
    max-width: 80rem;
    margin-inline: auto;
    padding-inline: 1.25rem;
  }
  .mk-blog-head { display: flex; gap: 1.25rem; align-items: flex-start; }
  .mk-blog-head-copy {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
    width: 100%;
    max-width: 48rem;
    text-align: left;
  }
  .mk-blog-eyebrow { color: var(--primary); font-size: 1rem; font-weight: 500; }
  .mk-blog-title {
    line-height: 1.15;
    margin: 0;
    font-size: 1.875rem;
    font-weight: 500;
    letter-spacing: -0.025em;
    text-wrap: balance;
  }
  .mk-blog-desc { margin: 0; max-width: 42rem; color: var(--muted-foreground); font-size: 1rem; text-wrap: pretty; }
  .mk-blog-see-all { display: none; margin-inline-start: auto; align-self: flex-end; flex-shrink: 0; }
  .mk-blog-grid { display: grid; gap: 1.25rem; width: 100%; }
  .mk-blog-card { display: flex; flex-direction: column; color: inherit; text-decoration: none; }
  .mk-blog-media {
    position: relative;
    aspect-ratio: 3 / 2;
    width: 100%;
    margin: 0;
    overflow: hidden;
    border-radius: var(--radius-lg);
    background: var(--muted);
    & img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
      transition: opacity 150ms ease;
    }
  }
  .mk-blog-card:hover .mk-blog-media img { opacity: 0.9; }
  .mk-blog-body { display: flex; flex: 1; flex-direction: column; gap: 0.75rem; padding-block: 0.875rem; }
  .mk-blog-meta { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
  .mk-blog-category { color: var(--primary); font-size: 0.875rem; font-weight: 500; }
  /* read time: a muted pill with a clock glyph */
  .mk-blog-read {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.125rem 0.5rem;
    border-radius: 999px;
    background: var(--muted);
    color: var(--muted-foreground);
    font-size: 0.75rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    & svg { width: 0.75rem; height: 0.75rem; flex: none; }
  }
  .mk-blog-copy { display: grid; gap: 0.25rem; }
  .mk-blog-card-title {
    line-height: 1.3;
    margin: 0;
    font-size: 1.125rem;
    font-weight: 500;
    letter-spacing: -0.025em;
    text-wrap: balance;
  }
  /* excerpt clamp: exactly 3 lines (source: line-clamp-3) */
  .mk-blog-excerpt {
    margin: 0;
    color: var(--muted-foreground);
    font-size: 0.875rem;
    text-wrap: pretty;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .mk-blog-author { display: flex; align-items: center; gap: 0.5rem; margin-block-start: auto; }
  .mk-blog-author-name { font-size: 0.875rem; font-weight: 500; }
  .mk-blog-author-date { color: var(--muted-foreground); font-size: 0.75rem; }
  @container mk-blog (min-width: 30rem) {
    .mk-blog-grid { grid-template-columns: 1fr 1fr; }
  }
  @container mk-blog (min-width: 44rem) {
    .mk-blog-grid { grid-template-columns: repeat(3, 1fr); }
  }
  @container mk-blog (min-width: 52rem) {
    .mk-blog-title { font-size: 2.25rem; }
    .mk-blog-see-all { display: inline-flex; }
    .mk-blog-grid { gap: 2.25rem; }
  }
  /* -- variants ------------------------------------------------- */
  /* featured: the first post spans two columns and two rows */
  .mk-blog-grid[data-variant="featured"] > :first-child {
    & .mk-blog-card-title { font-size: 1.5rem; }
    & .mk-blog-excerpt { -webkit-line-clamp: 4; font-size: 1rem; }
    /* the card spans two rows - keep the author with the text, not at the bottom */
    & .mk-blog-author { margin-block-start: 0.5rem; }
  }
  /* list: rows with the picture beside the text, separated by rules */
  .mk-blog-grid[data-variant="list"] {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    & .mk-blog-card { flex-direction: row; align-items: center; gap: 1.25rem; padding-block: 1.25rem; border-block-end: 1px solid var(--border); }
    & .mk-blog-card:first-child { padding-block-start: 0; }
    & .mk-blog-media { flex: none; width: 34%; max-width: 15rem; aspect-ratio: 4 / 3; }
    & .mk-blog-body { padding-block: 0; }
    & .mk-blog-author { display: none; }
  }
  /* minimal: text only - a rule on top of each post */
  .mk-blog-grid[data-variant="minimal"] .mk-blog-card {
    padding-block-start: 1rem;
    border-block-start: 2px solid var(--foreground);
    & .mk-blog-card-title { font-size: 1.25rem; }
    &:hover .mk-blog-card-title { text-decoration: underline; text-underline-offset: 0.2em; }
  }
  /* overlay: the text sits on the picture, a scrim keeps it readable */
  .mk-blog-grid[data-variant="overlay"] .mk-blog-card {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    min-height: 22rem;
    border-radius: var(--radius-xl);
    color: #fff;
    & .mk-blog-media { position: absolute; inset: 0; z-index: -2; aspect-ratio: auto; border-radius: 0; }
    & .mk-blog-media img { transition: scale 400ms ease; }
    &:hover .mk-blog-media img { opacity: 1; scale: 1.04; }
    &::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(to top, oklch(0.12 0.02 260 / 0.9), oklch(0.12 0.02 260 / 0.1) 70%); }
    & .mk-blog-body { justify-content: flex-end; padding: 1.25rem; }
    & .mk-blog-category { color: inherit; }
    & .mk-blog-read { background: oklch(1 0 0 / 0.16); color: inherit; }
    & :is(.mk-blog-excerpt, .mk-blog-author-date) { color: oklch(1 0 0 / 0.75); }
    & .mk-blog-author { margin-block-start: 0.25rem; }
  }
  /* carousel: one row that scrolls sideways and snaps per post */
  .mk-blog-grid[data-variant="carousel"] {
    display: flex;
    gap: 1.25rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: 0.25rem;
    padding-block-end: 0.75rem;
    scrollbar-width: thin;
    & > * { flex: 0 0 min(82%, 20rem); scroll-snap-align: start; }
  }
  /* category filters: links with the current one filled */
  .mk-blog-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    & a {
      padding: 0.375rem 0.875rem;
      border: 1px solid var(--border);
      border-radius: 999px;
      color: var(--muted-foreground);
      font-size: 0.875rem;
      font-weight: 500;
      text-decoration: none;
    }
    & a:hover { color: var(--foreground); border-color: color-mix(in oklch, var(--foreground) 30%, var(--border)); }
    & a[aria-current] { border-color: var(--foreground); background: var(--foreground); color: var(--background); }
    & a:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
  }
  @container mk-blog (min-width: 44rem) {
    .mk-blog-grid[data-variant="featured"] > :first-child { grid-column: span 2; grid-row: span 2; }
    .mk-blog-grid[data-variant="featured"] > :first-child .mk-blog-media { aspect-ratio: 16 / 10; }
  }
  /* -- Accessibility ------------------------------------------- */
  @media (prefers-reduced-motion: reduce) {
    .mk-blog-grid[data-variant="overlay"] .mk-blog-media img { transition: none; }
    .mk-blog-grid[data-variant="overlay"] .mk-blog-card:hover .mk-blog-media img { scale: none; }
  }
  @media (forced-colors: active) {
    .mk-blog-filters a[aria-current] { border-color: Highlight; background: Highlight; color: HighlightText; }
  }
  @media (prefers-reduced-motion: reduce) {
    .mk-blog-media img { transition: none; }
  }
  @media (forced-colors: active) {
    .mk-blog-card { color: LinkText; }
  }
}

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