A shaped container (circle or rounded square) of fixed size that frames an Icon, Image, or short text content.

Importing

The component can be imported via:

import { Coin, Icon } from '@customerio/pluma-components/react';
<Coin>
	<Icon name="person" />
</Coin>

Content

Coin renders whatever you put inside it. Common cases are an Icon, an Image, or short text content. Child Icon and Image components automatically read the coin's size from context, so you usually don't need to size them yourself.

AB
<InlineStack gap="200" alignBlock="center">
	<Coin>
		<Icon name="person" />
	</Coin>
	<Coin>
		<Image src="https://images.unsplash.com/photo-1605460375648-278bcbd579a6?q=80&w=500" />
	</Coin>
	<Coin>
		<Text>AB</Text>
	</Coin>
</InlineStack>

Size

The size prop selects from four tiers, defaulting to md. Child Icons and Images auto-size to match.

<InlineStack gap="200" alignBlock="center">
	<Coin size="sm">
		<Icon name="person" />
	</Coin>
	<Coin size="md">
		<Icon name="person" />
	</Coin>
	<Coin size="lg">
		<Icon name="person" />
	</Coin>
	<Coin size="xl">
		<Icon name="person" />
	</Coin>
</InlineStack>

Shape

Two shapes are available:

  • circle - a perfect circle shape
  • rounded-square - a square with moderately rounded corners
<InlineStack gap="200" alignBlock="center">
	<Coin shape="circle">
		<Icon name="person" />
	</Coin>
	<Coin shape="rounded-square">
		<Icon name="person" />
	</Coin>
</InlineStack>

Color variant

colorVariant sets a paired background + foreground from the label-* token family. Defaults to grey.

Pass null to opt out (no background, default foreground) — useful when you want a Coin styled with only a borderColor and a transparent background, or via an explicit backgroundColor / color.

<InlineStack gap="200" alignBlock="center">
	<Coin colorVariant="red">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="raspberry">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="clementine">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="yellow">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="green">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="teal">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="blue">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="plum">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="purple">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="grey">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant={null} borderColor="accent">
		<Icon name="person" />
	</Coin>
</InlineStack>

Overriding background and foreground

backgroundColor accepts the same values as Box's backgroundColor prop and overrides the background of the active colorVariant. color accepts the same values as Text's color prop (including 'inherit') and overrides the text color. The two colors (background and text) are independent — overriding one leaves the other from the variant intact.

<InlineStack gap="200" alignBlock="center">
	<Coin colorVariant="blue" backgroundColor="critical-subtle">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="blue" color="critical">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant={null} backgroundColor="active" color="bold">
		<Icon name="person" />
	</Coin>
</InlineStack>

Border color

borderColor accepts any of the semantic border-color tokens (accent, success, caution, critical, etc., including their -subtle / -minimal variants). The border is rendered as an inset shadow so it doesn't change the coin's outer dimensions — bordered and non-bordered Coins line up identically.

<InlineStack gap="200" alignBlock="center">
	<Coin colorVariant={null} borderColor="accent">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant={null} borderColor="success">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant={null} borderColor="critical">
		<Icon name="person" />
	</Coin>
	<Coin colorVariant="green" borderColor="success">
		<Icon name="person" />
	</Coin>
</InlineStack>

Image content and imageFit

imageFit controls how a child Image sits inside the coin. Values match CSS object-fit.

By default the Image is sized to the icon-sized box for the current size tier (e.g. 20px inside an md coin) and the imageFit value controls how its content fits that box. 'cover' is the one mode that also resizes the Image element itself — it grows to fill the entire coin shape edge-to-edge — and then crops + preserves aspect within that larger box, exactly like plain CSS object-fit: cover would. Use 'cover' for avatar-style profile photos that should bleed to the rim.

  • 'contain' (default) — image fits the icon-sized box while preserving aspect, with letterbox bars when aspects don't match. Works well for arbitrary images of unknown aspect ratio.
  • 'none' — image rendered at its natural size, clipped by the icon-sized box. Best for pre-sized assets (third-party logos, etc.).
  • 'fill' — image stretches to fill the icon-sized box, ignoring aspect.
  • 'cover' — image fills the entire coin shape, cropping as needed. Use for avatar-style profile photos.
<InlineStack gap="200" alignBlock="center">
	<Coin imageFit="contain">
		<Image src="https://images.unsplash.com/photo-1605460375648-278bcbd579a6?q=80&w=500" />
	</Coin>
	<Coin imageFit="none">
		<Image src="https://images.unsplash.com/photo-1605460375648-278bcbd579a6?q=80&w=500" />
	</Coin>
	<Coin imageFit="fill">
		<Image src="https://images.unsplash.com/photo-1605460375648-278bcbd579a6?q=80&w=500" />
	</Coin>
	<Coin imageFit="cover">
		<Image src="https://images.unsplash.com/photo-1605460375648-278bcbd579a6?q=80&w=500" />
	</Coin>
</InlineStack>

Status indicator

statusIndicatorColor renders a colored dot in the top-right corner. The coin's background is masked behind it so the page background shows through between the dot and the rim — the dot reads as a discrete element no matter what surface the coin is sitting on.

<InlineStack gap="200" alignBlock="center">
	<Coin statusIndicatorColor="success">
		<Icon name="person" />
	</Coin>
	<Coin statusIndicatorColor="caution">
		<Icon name="person" />
	</Coin>
	<Coin statusIndicatorColor="critical">
		<Icon name="person" />
	</Coin>
</InlineStack>

Composing with custom content

Coin propagates size, shape, and imageFit to descendants via context, which Icon and Image consume automatically — that's why you usually don't need to size them yourself.

Text-like children (Text, Heading, etc.) don't read the context. The foreground color set on the Coin still reaches them via the CSS cascade, so when you want a Text inside a Coin to pick up the coin's text color, pass color="inherit":

AB
<Coin colorVariant="blue">
	<Text color="inherit">AB</Text>
</Coin>

Overriding the context defaults

The context-derived defaults are only that — defaults. Any prop set directly on a child Icon or Image wins.

For Icons, an explicit size opts out of the size-from-Coin defaulting:

<InlineStack gap="200" alignBlock="center">
	<Coin size="xl" colorVariant="blue">
		<Icon name="person" />
	</Coin>
	<Coin size="xl" colorVariant="blue">
		<Icon name="person" size="sm" />
	</Coin>
</InlineStack>

For Images, the context also defaults withBoundingBox to true (so the image's outer box matches the icon-sized box used by Icons). Setting withBoundingBox={false} removes the bbox padding so the image fills the sized box directly:

<InlineStack gap="200" alignBlock="center">
	<Coin size="lg">
		<Image src={logo} />
	</Coin>
	<Coin size="lg">
		<Image src={logo} withBoundingBox={false} />
	</Coin>
</InlineStack>

API

PlumaCoin extends Box

The border color of the coin. Rendered as an inset box-shadow rather than a real border, so it doesn't take layout space — bordered and non-bordered Coins share identical outer dimensions, and a child Image in imageFit="cover" mode can fill edge-to-edge with the border painting on top of it.

The foreground (text + currentColor) of the coin. Use values from the Text component's color token enum. Overrides the foreground half of colorVariant when set.

Default:'grey'

Sets both background and foreground colors from a list of matching background and text colors. Defaults to 'grey'. Each color is independently overridable via backgroundColor and color.

Pass null to opt out entirely (no background, default foreground), e.g. for a Coin styled purely via borderColor or an explicit backgroundColor/color.

Default:'contain'

How a child Image should fit inside the coin. Mirrors CSS object-fit:

  • 'contain' (default) — letterboxes the image to fit the icon box while preserving aspect. Works well for arbitrary images of unknown aspect ratio.
  • 'none' — image at its natural size, clipped by the icon box. Good for pre-sized assets (third-party logos, etc.).
  • 'fill' — stretches the image to fill the icon box, ignoring aspect.
  • 'cover' — fills the entire coin edge-to-edge (not the icon box), cropping as needed. Use for profile photos / avatar-style imagery.

Always overridable per Image via its own objectFit prop.

Default:'circle'

The shape of the coin.

Default:'md'

The size of the coin.

Renders a colored circular indicator dot in the top-right corner, with a cutout in the coin so the page background shows through between the dot and the coin edge.