Sakuga Back to Sakuga
Theme
Sound
Jump to a component

Core · Content

MediaCard

MediaCard shows one episode, short or manga issue: its artwork at its real shape, then a title and a metadata line.

@anicanon/sakuga/uiv1.0Used onWeb · iOS · Android

Live, from @anicanon/sakuga 2.0.0

Sakuga for iOS and Android is coming

iOS

Coming soon

MediaCard for iOS is in the roughs. The tokens it will read are the ones on this page, the same on every platform.

Android

Coming soon

MediaCard for Android is in the roughs. The tokens it will read are the ones on this page, the same on every platform.

Use it for
One episode, short or manga issue: its art at its real shape (16:9, 9:16 or a manga page), its title and a metadata line.
Not for
  • A picture in a frame, such as a take or a library itemThumb
  • An episode or an issue announced but not out yetPremiereSlot
  • A page of an issue in the editorMangaPage
  • A cast member on the cast pageCastCard

Guidelines

  • kind sets the frame: episode 16:9, short 9:16, manga 1:1.414 with a paper corner and a spine shadow. Never put one kind's art in another's frame.
  • Continue watching passes logo (the series logo laid over the art) and progress (0–1).
  • Shorts pass duration and count; episodes pass duration; manga pass badge for New.
  • meta joins facts with a middle dot: "EBA · Season 1 · Episode 1". In a creator's list it says where the production is ("Cut · 3 takes", "14 Oct · 3.4K views"); attention marks the ones waiting for the creator with the accent dot, below the art, never over it.
  • A linked card's frame sinks on press; the art leans in on hover and focus, and watched progress draws in when the card appears.
  • lightMap is the art's baked light map: its own highlights glow where the pointer is, and at the centre under keyboard focus (light on art). It is on wherever there is a map; light={false} turns it off on a surface that must stay still.
  • Give the card a width from the layout (a grid column or a fixed shelf width); the frame keeps its ratio.
  • The corner follows the size: thumbnails 160px wide or less use radius-sm, 64px or less radius-xs, so a small frame never turns into a pill.
MediaCardExample.jsxReact
import { Badge, MediaCard } from '@anicanon/sakuga/ui';

export default function MediaCardExample() {
  return (
    <>
      <MediaCard
        kind="episode"
        width={280}
        image="/art/eba-s1e1.webp"
        lightMap="/art/eba-s1e1.light.webp"
        logo="/art/eba-logo.webp"
        logoAlt="EBA"
        progress={0.28}
        title="The Legacy of Blood"
        meta="EBA · Season 1 · Episode 1"
        href="#"
      />
      <MediaCard
        kind="short"
        width={140}
        image="/art/eba-cinema.webp"
        lightMap="/art/eba-cinema.light.webp"
        imagePosition="62% center"
        duration="0:24"
        count="1.2M views"
        title="The popcorn incident"
        href="#"
      />
      <MediaCard
        kind="manga"
        width={150}
        image="/art/bad-memories-the-winter-p00.webp"
        lightMap="/art/bad-memories-the-winter-p00.light.webp"
        alt="The Winter cover"
        badge={<Badge variant="new">New</Badge>}
        title="The Winter"
        meta="Bad Memories · 15 pages"
        href="#"
      />
    </>
  );
}

PropsMediaCardProps

kindrequired
'episode' | 'short' | 'manga'
Sets the frame ratio: episode 16:9, short 9:16, manga 1:1.414.
imagerequired
string
alt
string
imagePosition
string
lightMap
string
The art's baked light map: it glows with its own highlights under the pointer, at its centre under keyboard focus (light on art).
light
boolean
On wherever there is a lightMap; false turns it off on a surface that must stay still.
title
ReactNode
meta
ReactNode
duration
string
m:ss, shown as a Badge.
count
string
badge
ReactNode
logo
string
The series logo laid over the artwork (Continue watching).
logoAlt
string
progress
number
0–1 watched.
attention
boolean
The meta line says this one waits for you (takes to review, a plan to approve, ready to publish): it reads in the text colour with the accent dot.
href
string
label
string
width
number | string
className
string
MediaCardExample.swiftiOS · coming soon
MediaCardExample.ktAndroid · coming soon

Each token reads a role, and the role lands on a value in each theme. Change the role and every component that reads it follows.

Colour

media-card-titleMediaCard: title under the artwork.
text-primaryneutral-925#0b0a10
text-primaryneutral-0#ffffff
media-card-spineMediaCard manga: the flat band of the binding down the cover's left edge, the same in both themes.
black-a50rgba(0,0,0,0.5)
black-a50rgba(0,0,0,0.5)
media-card-metaMediaCard: metadata under the title.
text-mutedneutral-600#5c5b67
text-mutedneutral-400#a3a3a7
media-card-placeholderMediaCard: frame behind artwork while it loads.
bg-surfaceneutral-0#ffffff
bg-surfaceneutral-900#111117
media-card-scrimMediaCard: shade under a series logo laid over the artwork.
bg-scrim-strongscrim-a78rgba(7,7,10,0.78)
bg-scrim-strongscrim-a78rgba(7,7,10,0.78)

Space and shape

media-card-radius
radius-md
12px
media-card-manga-radius
radius-xs
4px
media-card-art-filter
its own value
saturate(0.9) brightness(0.92)
media-card-gap
gap-md
8px
media-card-episode-frame-aspect-ratio
ratio-video
16/9
media-card-short-frame-aspect-ratio
ratio-short
9/16
media-card-short-frame-radius
radius-lg
16px
media-card-manga-frame-aspect-ratio
ratio-page
1/1.414
media-card-frame-radius-sm
radius-sm
8px
media-card-frame-radius-xs
radius-xs
4px
media-card-img-hover-scale
scale-zoom
1.04
media-card-logo-left
inset-sm
8px
media-card-logo-bottom
inset-md
12px
media-card-tl-inset
inset-sm
8px
media-card-br-inset
inset-sm
8px
media-card-bl-inset
inset-sm
8px
media-card-meta-margin-top
its own value
calc(-1 * 4px)
media-card-manga-spine-width
its own value
8px
media-card-progress-inset
inset-sm
8px
media-card-frame-xs-max
its own value
64px
media-card-frame-sm-max
its own value
160px
media-card-meta-attention-gap
gap-sm
6px
media-card-frame-active-scale
scale-press-lg
.98
media-card-img-focus-scale
scale-zoom
1.04
media-card-frame-light-scale
scale-zoom
1.04

Type

media-card-font-family
font-sans
Funnel Sans
media-card-title-font-size
text-label-size
14px
Aa
media-card-title-line-height
text-label-line-height
20px
media-card-title-font-weight
text-label-weight
600
media-card-meta-font-size
text-caption-size
13px
Aa
media-card-meta-line-height
text-caption-line-height
18px
media-card-meta-attention-font-weight
weight-strong
600

Motion

media-card-img-duration
motion-slower
560ms
media-card-img-easing
ease-standard
cubic-bezier(0.2, 0, 0, 1)
media-card-img-develop-animation
motion-develop
sk-develop 560ms cubic-bezier(0, 0, 0, 1)
media-card-frame-duration
motion-fast
160ms
media-card-frame-easing
ease-standard
cubic-bezier(0.2, 0, 0, 1)
media-card-frame-active-duration
motion-instant
100ms
media-card-frame-light-duration
motion-slower
560ms
media-card-frame-light-easing
ease-standard
cubic-bezier(0.2, 0, 0, 1)

Checked automatically with axe-core 4.11.4 against WCAG 2.2 A and AA, on the live preview above, in light and dark, on 26 October 2026.

Result
No issues found, in light or in dark.
By design
With href it is a link that Tab reaches and Enter follows, named by label if given, else by its content in order (any artwork and logo alt text, badge, duration, count, title, meta) ending with "waiting for you" when attention is set and "watched 40%" from progress; without href the same reads as text. The artwork's alt is alt and the logo's logoAlt (both empty by default), and the spine, scrim, light and progress bar are hidden.
Not tested
The automated check doesn't test keyboard use, focus order or what a screen reader says. The lines above are the design system's own account of them.