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';
PropTypeDefaultDescription
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.
tiltnumber—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 visual
  • pointer-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>
  );
}

Related