Docs
Next

PostMeta

The author and date byline the default post cards share.

On this page

PostMeta renders a post's byline: the author, a • separator and the date. PostCard and HighlightCard both use it, and you can use it in your own card or post header. Each side can be hidden on its own, and the separator only appears when both show.

PostMeta
import { PostMeta } from '@systhemaui/next'

export default function Demo() {
  return (
    <PostMeta
      className="post-byline"
      author="Mira Kovač"
      authorAvatar={{ url: '/demo-assets/avatar-1.webp' }}
      date="September 14, 2026"
      dateTime="2026-09-14T09:00:00.000Z"
      showAuthor
      showDate
    />
  )
}

ImportLink to this section

import { PostMeta } from '@systhemaui/next'

In a React app without Next.js, import it from @systhemaui/react.

ExamplesLink to this section

From a PostViewLink to this section

postByline(post) returns the author, the formatted date and the ISO dateTime from a PostView, formatted in post.locale (en-US by default):

import { PostMeta, postByline, type PostView } from '@systhemaui/next'

export function Byline({ post }: { post: PostView }) {
  const { author, date, dateTime } = postByline(post)
  return (
    <PostMeta
      className="post-byline"
      author={author}
      authorAvatar={post.author?.avatar}
      date={date}
      dateTime={dateTime}
      showAuthor
      showDate
    />
  )
}

One side onlyLink to this section

showAuthor and showDate are required, so the caller decides what shows. A side with no value (null) is skipped as well, and when neither side renders, PostMeta renders nothing.

Author only, date only
import { PostMeta } from '@systhemaui/next'

export default function Demo() {
  return (
    <div className="flex flex-col items-center gap-4">
      <PostMeta
        className="post-byline"
        author="Jonas Weber"
        date={null}
        showAuthor
        showDate={false}
      />
      <PostMeta
        className="post-byline"
        author={null}
        date="September 2, 2026"
        dateTime="2026-09-02"
        showAuthor={false}
        showDate
      />
    </div>
  )
}

Styling each sideLink to this section

className goes on the row, and authorClassName and dateClassName are merged over each side's default classes with tailwind-merge.

<PostMeta
  className="post-card-meta"
  author="Mira Kovač"
  date="September 14, 2026"
  showAuthor
  showDate
  dateClassName="uppercase tracking-wide"
/>

PropsLink to this section

PropTypeDefaultDescription
author (required)string | null-
authorAvatarAuthorAvatar | null-
date (required)string | null-
dateTimestring-
showAuthor (required)boolean-
showDate (required)boolean-
className (required)string-Root class for the meta row (e.g. post-card-meta/post-highlight-meta).
authorClassNamestring-
dateClassNamestring-

PostMeta does not format dates. Pass a display string as date and the machine-readable value as dateTime, or use postByline.

HTML and CSSLink to this section

<div class="post-meta text-small flex flex-wrap items-center gap-x-2">
  <span class="post-meta-author inline-flex items-center gap-2">
    <img class="post-meta-avatar size-6 rounded-full object-cover" src="/media/mira.webp" alt="Mira Kovač" />
    Mira Kovač
  </span>
  <span class="post-meta-separator" aria-hidden="true">•</span>
  <time class="post-meta-date" datetime="2026-09-14T09:00:00.000Z">September 14, 2026</time>
</div>

The .post-meta* hooks come from the posts CSS layer, which mutes the byline against the current text color.

ClassStyles
posts-listwidth: 100%;max-width: var(--container-width);margin-inline: auto;
posts-list-grid& { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: var(--gap-sm, 1.25rem); row-gap: var(--gap-md, 2.5rem); }& > .post-card { animation-delay: var(--post-card-stagger-delay, 0ms); }& > * > .post-card { animation-delay: var(--post-card-stagger-delay, 0ms); }
posts-list-rowdisplay: flex;flex-direction: column;row-gap: var(--gap-md, 2.5rem);
posts-list-grid-2@media (width >= 640px) { & { grid-template-columns: repeat(2, minmax(0, 1fr)); } }@media (width >= 640px) { & > :nth-child(2n - 1) { --post-card-stagger-delay: 0ms; } }@media (width >= 640px) { & > :nth-child(2n) { --post-card-stagger-delay: calc(var(--tw-stagger-delay, 120ms) * 1); } }
posts-list-grid-3@media (width >= 640px) { & { grid-template-columns: repeat(2, minmax(0, 1fr)); } }@media (width >= 1192px) { & { grid-template-columns: repeat(3, minmax(0, 1fr)); } }@media (width >= 640px) and (width < 1192px) { & > :nth-child(2n - 1) { --post-card-stagger-delay: 0ms; } }@media (width >= 640px) and (width < 1192px) { & > :nth-child(2n) { --post-card-stagger-delay: calc(var(--tw-stagger-delay, 120ms) * 1); } }+ 3 more rules
posts-list-grid-4@media (width >= 640px) { & { grid-template-columns: repeat(2, minmax(0, 1fr)); } }@media (width >= 1192px) { & { grid-template-columns: repeat(4, minmax(0, 1fr)); } }@media (width >= 640px) and (width < 1192px) { & > :nth-child(2n - 1) { --post-card-stagger-delay: 0ms; } }@media (width >= 640px) and (width < 1192px) { & > :nth-child(2n) { --post-card-stagger-delay: calc(var(--tw-stagger-delay, 120ms) * 1); } }+ 4 more rules
posts-list-interspacewidth: 100%;
posts-list-emptycolor: color-mix(in srgb, currentColor 60%, transparent);text-align: center;padding-block: var(--gap-md, 2.5rem);
posts-list-actionsdisplay: flex;align-items: center;justify-content: center;gap: var(--gap-sm, 1.25rem);
posts-list-load-more&:disabled, .posts-list-load-more[disabled] { opacity: 0.4; cursor: not-allowed; }
posts-list-pagination& { display: flex; justify-content: center; align-items: center; gap: var(--gallery-navigation-gap-x, 10px); }+ 13 more rules
post-card& { color: var(--color-typography-body); }.posts-list-grid > .post-card { animation-delay: var(--post-card-stagger-delay, 0ms); }.posts-list-grid > * > .post-card { animation-delay: var(--post-card-stagger-delay, 0ms); }
post-card-label& { color: color-mix(in srgb, currentColor 60%, transparent); }&.text-label { color: color-mix(in srgb, currentColor 60%, transparent); }
post-highlight-label& { color: color-mix(in srgb, currentColor 60%, transparent); }&.text-label { color: color-mix(in srgb, currentColor 60%, transparent); }
post-typecolor: color-mix(in srgb, currentColor 60%, transparent);
text-label& { font-family: var(--font-label-font-family); font-size: var(--typography-label-font-size); font-weight: var(--font-label-font-weight); letter-spacing: var(--typography-label-letter-spacing); line-height: var(--typography-label-line-height); text-transform: uppercase; text-decoration: none; font-style: var(--font-label-font-style); }.post-card-label.text-label { color: color-mix(in srgb, currentColor 60%, transparent); }+ 1 more rule
post-card-titletext-wrap: balance;
post-card-read-morecolor: var(--color-link-text-default, currentColor);font-weight: 600;text-decoration-line: underline;
post-highlight-read-morecolor: var(--color-link-text-default, currentColor);font-weight: 600;text-decoration-line: underline;
post-card-media-placeholderwidth: 100%;height: 100%;background-color: color-mix(in srgb, currentColor 6%, transparent);
post-card-row@media (width >= 640px) { & .post-card-body { align-self: start; justify-content: flex-start; } }
post-card-body@media (width >= 640px) { .post-card-row .post-card-body { align-self: start; justify-content: flex-start; } }
post-highlightposition: relative;overflow: hidden;border-radius: var(--card-default-border-radius, 12px);
highlight-cardposition: relative;overflow: hidden;border-radius: var(--card-default-border-radius, 12px);
post-highlight-scrimposition: absolute;inset: 0;background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.35) 35%, rgba(0,0,0,0) 70%);pointer-events: none;
post-highlight-overlayposition: relative;z-index: 1;
post-highlight-excerptcolor: color-mix(in srgb, #ffffff 85%, transparent);
post-metadisplay: flex;align-items: center;flex-wrap: wrap;gap: 0.5em;color: color-mix(in srgb, currentColor 60%, transparent);
post-byline-metadisplay: flex;align-items: center;flex-wrap: wrap;gap: 0.5em;color: color-mix(in srgb, currentColor 60%, transparent);
post-meta-avatarwidth: var(--post-byline-avatar-size, 1.75em);height: var(--post-byline-avatar-size, 1.75em);border-radius: 9999px;object-fit: cover;flex-shrink: 0;
post-meta-separatoropacity: 0.5;
post-meta-author-role& { opacity: 0.7; }&::before { content: ", "; }
archive-filterdisplay: flex;flex-direction: column;gap: var(--gap-sm, 1.25rem);
archive-filter-bardisplay: flex;flex-wrap: wrap;align-items: center;gap: var(--gap-sm, 1.25rem);
archive-filter-labelflex: 0 0 auto;color: color-mix(in srgb, currentColor 60%, transparent);
archive-filter-searchflex: 1 1 16rem;min-width: 0;margin: 0;
archive-filter-selectflex: 1 1 16rem;min-width: 0;margin: 0;
archive-filter-chipsdisplay: flex;flex-wrap: wrap;align-items: center;gap: var(--chip-gap, 0.5rem);flex: 1 1 16rem;min-width: 0;
archive-filter-chip-togglecursor: pointer;appearance: none;
archive-filter-chip-toggle-activebackground-color: var(--color-chip-hover-background);border-color: var(--color-chip-hover-border);color: var(--color-chip-hover-text);
archive-filter-activedisplay: flex;flex-wrap: wrap;align-items: center;gap: var(--chip-gap, 0.5rem);
archive-filter-chip& { appearance: none; cursor: pointer; display: inline-flex; align-items: center; gap: var(--chip-gap); font-size: var(--chip-font-size); line-height: var(--chip-line-height); letter-spacing: calc(var(--chip-letter-spacing, 0) * 1px); padding-inline: var(--chip-padding-x); padding-block: var(--chip-padding-y); border-radius: var(--chip-border-radius); border-style: solid; border-width: var(--chip-border-width); background-color: var(--color-chip-normal-background); border-co…+ 1 more rule
archive-filter-chip-removefont-size: 1.2em;line-height: 1;opacity: 0.7;
post-related-titlemargin-bottom: var(--gap-sm, 1.25rem);
post-single& .article > :not(section, .figure-w-full, .figure-w-screen, .figure-w-container, a:not([class*="card"]), div:not(.columns, .stack, [class*="card"], [class*="accordion"])) { max-width: var(--post-reading-width, calc(var(--container-width) - (var(--article-padding-x) * 2))); }
post-share-button-labelfont-size: var(--text-small-font-size, 0.875rem);line-height: 1;
post-share-icon-label& .post-share-list > a { display: inline-flex; align-items: center; gap: var(--gap-xs, 0.5rem); width: auto; }& .post-share-list > button { display: inline-flex; align-items: center; gap: var(--gap-xs, 0.5rem); width: auto; }
post-share-list.post-share-icon-label .post-share-list > a { display: inline-flex; align-items: center; gap: var(--gap-xs, 0.5rem); width: auto; }.post-share-icon-label .post-share-list > button { display: inline-flex; align-items: center; gap: var(--gap-xs, 0.5rem); width: auto; }.post-share-pills .post-share-list > a { display: inline-flex; align-items: center; gap: var(--gap-xs, 0.5rem); width: auto; }+ 3 more rules
post-share-pills& .post-share-list > a { display: inline-flex; align-items: center; gap: var(--gap-xs, 0.5rem); width: auto; }& .post-share-list > button { display: inline-flex; align-items: center; gap: var(--gap-xs, 0.5rem); width: auto; }& .post-share-list > a { padding: var(--gap-xs, 0.5rem) var(--gap-sm, 1.25rem); border-radius: 9999px; border: 1px solid var(--color-separator-line, currentColor); }+ 1 more rule
post-share-sticky-bottomposition: fixed;left: 0;right: 0;bottom: 0;z-index: 40;justify-content: center;background-color: var(--color-foundations-surface-bg);border-top: 1px solid var(--color-separator-line, currentColor);padding-block: var(--gap-sm, 1.25rem);
post-share-floating-sidebar@media (width >= 1192px) { & { position: fixed; left: var(--gap-lg, 1.5rem); top: 50%; transform: translateY(-50%); flex-direction: column; align-items: center; z-index: 40; } }

Next.jsLink to this section

@systhemaui/next re-exports PostMeta from @systhemaui/react unchanged.

AccessibilityLink to this section

  • The separator is aria-hidden, so screen readers hear "Mira Kovač September 14, 2026".
  • The date is a <time> element; pass dateTime so it carries a machine-readable value.
  • The avatar's alt is authorAvatar.alt, or the author's name when that is empty, so a screen reader hears the name twice when an avatar shows. An empty alt falls back to the name too.