# IconWrapper

A cross-platform container that gives icons, labels, images, and avatars a consistent box, alignment, and shape. It resolves its dimensions and default background from the active theme.
<!-- BEGIN:xui-mcp-instructions:icon-wrapper -->
A square container with an optional background shape that holds a visual element — an icon, an image, a text label, an avatar, a brand logo, or custom content. Used wherever a visual identity element needs a consistent size, padding, and shape across different content types. Acts as a unifying frame for heterogeneous visual content in lists, cards, cells, and menus.

### When to use
- In lists, menus, table cells, and cards where different rows may contain icons, images, logos, or avatars — IconWrapper ensures they all occupy the same visual footprint
- To give a system icon a coloured or shaped background (e.g. a category icon with a tinted pill or circle)
- In ContextMenuCell left slots, Cell master slots, and AvatarGroup overflow chips where a contained visual element is expected
- When a brand logo or product image needs a neutral background container for visual consistency with surrounding icons
- As a slot in any component that accepts a *"visual identifier"* — the type of the visual can vary, but the size is always the same

### When not to use
- As an interactive button — use Icon button instead
- When the shape or background is not needed and the icon can stand alone — use the icon directly
- As a decorative element with no semantic relationship to the content — prefer inline SVG or CSS background

### Content guidelines
- Icon type — choose icons that are immediately recognisable for the category or entity they represent. Avoid abstract decorative icons. If no suitable icon exists, use Type=Label with initials as a fallback.
- Label type — limit label text to 1–2 characters. Use uppercase initials: *"JD"* for John Doe, *"PM"* for Product Management. Never use full words — they will overflow or be illegible at small sizes.
- Brand Logo type — use the official logo asset at the correct resolution. Provide a 2× asset for high-DPI screens. Ensure the logo has sufficient padding inside the container so it does not feel cramped — if the logo bleeds to the edges, switch to Shape=None or increase the container size.
- Alt text — for Type=Image and Type=Brand Logo, always provide descriptive alt text on the underlying <img> element. For Type=Icon and Type=Label where the content is decorative (the surrounding list item label already identifies the entity), use aria-hidden=*"true"* on the icon or label element.

### Behaviour guidelines
- Non-interactive — IconWrapper is a display component. It has no interactive states (Hover, Press, Focus, Active). If the content it represents must be clickable, wrap it in an Icon button or a Cell master with an interactive container.
- Consistent size per group — always use the same Size for all IconWrapper instances within one list, table, or card grid. The visual alignment of the group depends on all items sharing the same square footprint.
- Shape per content type — apply Shape=Full (circle) for person-related content (avatars, user icons). Apply Shape=Smooth (rounded square) for product, app, and category icons. Apply Shape=None when the visual element defines its own shape.
- Background colour — the background fill of Shape=Full and Shape=Smooth is controlled by a design token, not a prop. Apply colour through CSS tokens or class variants at the product level — e.g. a tinted background for a category icon.
- Image fitting — Type=Image should use object-fit: cover to fill the container without distortion. Apply object-position: center by default; adjust for content that has important detail near the edges.
- Fallback for missing content — when Type=Image and the image fails to load, fall back to Type=Icon with a placeholder icon rather than showing a broken image. Handle this at the product level.

### Accessibility
- IconWrapper itself is a presentational container — it must not have role=*"img"* or other landmark roles unless the entire component (wrapper + content) represents a standalone image with no surrounding label.
- Type=Icon: if the icon is purely decorative (the row label next to it already conveys the meaning), set aria-hidden=*"true"* on the SVG. If the icon carries meaning not communicated elsewhere, add aria-label to the SVG or the wrapper.
- Type=Image: the <img> inside must have alt text. If the image is decorative (the entity is named in the adjacent label), use alt="".
- Type=Brand Logo: the <img> must have alt text with the brand name — e.g. alt=*"Visa"*, alt=*"PayPal"*. Do not use alt="" for brand logos — they are meaningful identifiers.
- Type=Label: the text inside is rendered content. If the label is an abbreviation (initials), add aria-label on the wrapper with the full name — e.g. aria-label=*"John Doe"*.
- Type=Avatar: the Avatar's own accessibility attributes apply. IconWrapper adds no additional ARIA overhead.
- Never use IconWrapper as the only interactive element — it has no role=*"button"* or focus handling. If interaction is needed, the parent component must handle it.
<!-- END:xui-mcp-instructions:icon-wrapper -->

## Installation

```bash
npm install @xsolla/xui-icon-wrapper
```

## Imports

```tsx
import { IconWrapper, type IconWrapperProps } from "@xsolla/xui-icon-wrapper";
```

## Quick start

```tsx
import * as React from "react";
import { IconWrapper } from "@xsolla/xui-icon-wrapper";
import { Star } from "@xsolla/xui-icons-base";

export default function QuickStart() {
  return (
    <IconWrapper size="md" shape="smooth">
      <Star size="100%" aria-hidden />
    </IconWrapper>
  );
}
```

## API Reference

### `<IconWrapper>`

| Prop              | Type                                                              | Default                                        | Description                                                                                                   |
| ----------------- | ----------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `testID`          | `string`                                                          | —                                              | Test ID for testing frameworks. On web this renders as `data-testid`; on React Native it renders as `testID`. |
| `children`        | `ReactNode`                                                       | —                                              | Element rendered inside the wrapper.                                                                          |
| `size`            | `"xxs" \| "xs" \| "sm" \| "md" \| "lg" \| "xl"`                   | `"md"`                                         | Wrapper size. Resolved against `theme.sizing.iconWrapper`.                                                    |
| `shape`           | `"none" \| "smooth" \| "full"`                                    | `"none"`                                       | Border-radius style. `"smooth"` rounds to ~size/6 (min 4px); `"full"` is fully circular.                      |
| `type`            | `"icon" \| "label" \| "image" \| "avatar" \| "brand" \| "custom"` | —                                              | Hint describing the wrapped content. Reserved for future styling rules; currently has no visual effect.       |
| `backgroundColor` | `string`                                                          | theme default (transparent for `shape="none"`) | Background colour override.                                                                                   |
| `borderColor`     | `string`                                                          | —                                              | Border colour. When set, a 1px border is drawn.                                                               |

Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).

## Examples

### Sizes

```tsx
import * as React from "react";
import { IconWrapper } from "@xsolla/xui-icon-wrapper";
import { Heart } from "@xsolla/xui-icons-base";

export default function Sizes() {
  return (
    <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
      <IconWrapper size="xxs">
        <Heart size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper size="xs">
        <Heart size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper size="sm">
        <Heart size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper size="md">
        <Heart size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper size="lg">
        <Heart size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper size="xl">
        <Heart size="100%" aria-hidden />
      </IconWrapper>
    </div>
  );
}
```

### Shapes

```tsx
import * as React from "react";
import { IconWrapper } from "@xsolla/xui-icon-wrapper";
import { User } from "@xsolla/xui-icons-base";

export default function Shapes() {
  return (
    <div style={{ display: "flex", gap: 16, alignItems: "center" }}>
      <IconWrapper shape="none" size="lg">
        <User size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper shape="smooth" size="lg">
        <User size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper shape="full" size="lg">
        <User size="100%" aria-hidden />
      </IconWrapper>
    </div>
  );
}
```

### Status badges

```tsx
import * as React from "react";
import { IconWrapper } from "@xsolla/xui-icon-wrapper";
import { Check, Warning, RemoveCr } from "@xsolla/xui-icons-base";

export default function StatusBadges() {
  return (
    <div style={{ display: "flex", gap: 16 }}>
      <IconWrapper shape="full" size="md" backgroundColor="#4CAF50">
        <Check color="#fff" size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper shape="full" size="md" backgroundColor="#FF9800">
        <Warning color="#fff" size="100%" aria-hidden />
      </IconWrapper>
      <IconWrapper shape="full" size="md" backgroundColor="#F44336">
        <RemoveCr color="#fff" size="100%" aria-hidden />
      </IconWrapper>
    </div>
  );
}
```

### Brand icon container

```tsx
import * as React from "react";
import { IconWrapper } from "@xsolla/xui-icon-wrapper";
import { Github, Discord, Twitch } from "@xsolla/xui-icons-brand";

export default function BrandTiles() {
  return (
    <div style={{ display: "flex", gap: 12 }}>
      <IconWrapper size="lg" shape="smooth">
        <Github size={24} aria-hidden />
      </IconWrapper>
      <IconWrapper size="lg" shape="smooth">
        <Discord size={24} aria-hidden />
      </IconWrapper>
      <IconWrapper size="lg" shape="smooth">
        <Twitch size={24} aria-hidden />
      </IconWrapper>
    </div>
  );
}
```

## Accessibility

- The wrapper is a presentational container — it does not add any ARIA attributes.
- Set `aria-label` on interactive children, or `aria-hidden` on decorative icons.
- For interactive icon-only controls, use `IconButton` from `@xsolla/xui-button` rather than wrapping a click handler around `IconWrapper`.
