PixelRibbon
Absolutely-positioned decorative ribbon for cards — surface-aware, tone-driven, with corner-tilt presets.
- Five position presets (top-center/left/right, corner-tl/tr)
- Tone palette via shared ToneKey tokens
- Auto-tilt on corner positions with manual override
- Surface-aware borders, radius and display font
- Status
- stable
- Category
- cards
- Since
- v1.7.0
API
import { PixelRibbon } from '@pxlkit/ui-kit';
| Prop | Type | Default | Description |
|---|
position | 'top-center' | 'top-left' | 'top-right' | 'corner-tl' | 'corner-tr' | 'top-center' | Where the ribbon sits on its container. |
tone | 'neutral' | 'green' | 'cyan' | 'gold' | 'red' | 'purple' | 'pink' | 'gold' | Tone of the opaque fill. |
offset | 'sm' | 'md' | 'lg' | 'md' | How far a top ribbon rises above the container's edge. |
tilt | number | — | Tilt in degrees; the corners lean outwards by 12° when unset. |
surface | 'pixel' | 'linear' | — | Surface override; defaults to the nearest provider. |
children* (required) | React.ReactNode | — | The ribbon's text. |
Also takes the native attributes and event handlers of <div> (HTMLAttributes<HTMLDivElement>).
ref points to <div>.
Accessibility
WCAG target: 2.1 AA
Decorative text node — wrap in role=img with aria-label when content is purely visualpointer-events: none ensures the ribbon never intercepts card interactions
Ribbon is non-interactive; pair its message with the card heading so screen readers still convey the badge meaning.
Usage
import React from 'react';
import { PixelRibbon } from '@pxlkit/ui-kit';
function Container({ children }: { children: React.ReactNode }) {
return (
<div className="relative inline-block border-2 border-retro-border bg-retro-bg/60 p-8 text-retro-text">
<div>Card content</div>
{children}
</div>
);
}
export function Default() {
return (
<Container>
<PixelRibbon>New</PixelRibbon>
</Container>
);
}
Examples
Default
import React from 'react';
import { PixelRibbon } from '@pxlkit/ui-kit';
function Container({ children }: { children: React.ReactNode }) {
return (
<div className="relative inline-block border-2 border-retro-border bg-retro-bg/60 p-8 text-retro-text">
<div>Card content</div>
{children}
</div>
);
}
export function Default() {
return (
<Container>
<PixelRibbon>New</PixelRibbon>
</Container>
);
}
Corner tilted
import React from 'react';
import { PixelRibbon } from '@pxlkit/ui-kit';
function Container({ children }: { children: React.ReactNode }) {
return (
<div className="relative inline-block border-2 border-retro-border bg-retro-bg/60 p-8 text-retro-text">
<div>Card content</div>
{children}
</div>
);
}
export function CornerTilted() {
return (
<Container>
<PixelRibbon position="corner-tr" tone="red">
Hot
</PixelRibbon>
</Container>
);
}
Tones
import React from 'react';
import { PixelRibbon } from '@pxlkit/ui-kit';
function Container({ children }: { children: React.ReactNode }) {
return (
<div className="relative inline-block border-2 border-retro-border bg-retro-bg/60 p-8 text-retro-text">
<div>Card content</div>
{children}
</div>
);
}
export function Tones() {
return (
<div className="flex flex-wrap gap-6">
<Container>
<PixelRibbon tone="green">Free</PixelRibbon>
</Container>
<Container>
<PixelRibbon tone="cyan">Beta</PixelRibbon>
</Container>
<Container>
<PixelRibbon tone="purple">Pro</PixelRibbon>
</Container>
</div>
);
}
Position left
import React from 'react';
import { PixelRibbon } from '@pxlkit/ui-kit';
function Container({ children }: { children: React.ReactNode }) {
return (
<div className="relative inline-block border-2 border-retro-border bg-retro-bg/60 p-8 text-retro-text">
<div>Card content</div>
{children}
</div>
);
}
export function PositionLeft() {
return (
<Container>
<PixelRibbon position="top-left" offset="lg" tone="gold">
Sale
</PixelRibbon>
</Container>
);
}