Blog Listing
Cards for an article index: the standard tile and the larger featured variant.
Why your design system drifts
Two artefacts maintained by hand will always diverge. Generating one from the other is the only fix that holds.
Dates and read times are strings
Neither card parses a Date or counts words. Format both upstream, where you know the reader's locale and how you want to round — "11 mins read" vs "~10 min" is an editorial choice, not something a card should decide.
Horizontal layout
Shipping dark mode from day one
Retrofitting a second theme is expensive. Building both from the same semantic layer costs almost nothing.
Featured card
Featured
A year of shipping from one Figma file
What we learned exporting 114 components 1:1, and the three places the approach nearly broke.
BlogListingCard props
| Prop | Type | Default | Description |
|---|---|---|---|
| image / imageAlt | string | Cover image and alt text. | |
| category | string | Category label above the title. | |
| readTime | string | Pre-formatted, e.g. "11 mins read" — nothing is computed from word count. | |
| title | string | Post title. | |
| excerpt | string | Required. The standfirst under the title. | |
| author | { name: string; date: string; avatarSrc?: string; avatarAlt?: string } | Byline. date is pre-formatted and rendered after the name. | |
| layout | 'default' | 'horizontal' | 'default' | Horizontal puts the image beside the text, for list views. |
BlogListingFeaturedCard props
| Prop | Type | Default | Description |
|---|---|---|---|
| image / imageAlt | string | Cover image and alt text. | |
| badgeLabel | string | 'Featured' | The pill on the image. |
| title | string | Post title. | |
| excerpt | string | Required. The standfirst under the title. | |
| author | { name; date; avatarSrc?; avatarAlt?; initials? } | Byline. initials render when there's no avatar image. | |
| orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout of image against text. |