Item

ui-item lays out one row of content from parts, in source order: optional ui-item-media on the left, ui-item-content (with ui-item-title and ui-item-description) filling the middle, and ui-item-actions on the right. ui-item-group stacks items in a column and gives the group the ARIA role list.

Previewindex.html
Basic item A simple item with a title and a description. Action
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

  <div class="demo-medium">
    <ui-item variant="outline">
      <ui-item-content>
        <ui-item-title>Basic item</ui-item-title>
        <ui-item-description>A simple item with a title and a description.</ui-item-description>
      </ui-item-content>
      <ui-item-actions>
        <ui-button variant="outline" size="sm">Action</ui-button>
      </ui-item-actions>
    </ui-item>
  </div>
</body>
</html>

Examples

Variants

default has no border or background, outline adds a border, muted a subtle background.

Previewindex.html
Default Transparent background and border. Outline A visible border. Muted A muted background.
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
    .demo-stack {
      display: grid;
      gap: calc(var(--spacing) * 4);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

  <div class="demo-medium demo-stack">
    <ui-item>
      <ui-item-content>
        <ui-item-title>Default</ui-item-title>
        <ui-item-description>Transparent background and border.</ui-item-description>
      </ui-item-content>
    </ui-item>
    <ui-item variant="outline">
      <ui-item-content>
        <ui-item-title>Outline</ui-item-title>
        <ui-item-description>A visible border.</ui-item-description>
      </ui-item-content>
    </ui-item>
    <ui-item variant="muted">
      <ui-item-content>
        <ui-item-title>Muted</ui-item-title>
        <ui-item-description>A muted background.</ui-item-description>
      </ui-item-content>
    </ui-item>
  </div>
</body>
</html>

Small

size="sm" reduces the padding and gap. An <svg> in ui-item-media is sized to calc(var(--spacing) * 4).

Previewindex.html
Your profile has been verified.
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

  <div class="demo-medium">
    <ui-item variant="outline" size="sm">
      <ui-item-media>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
      </ui-item-media>
      <ui-item-content>
        <ui-item-title>Your profile has been verified.</ui-item-title>
      </ui-item-content>
      <ui-item-actions>
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
      </ui-item-actions>
    </ui-item>
  </div>
</body>
</html>

Icon media

variant="icon" puts the icon in a small bordered, muted box.

Previewindex.html
Security alert New login detected from an unknown device. Review
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

  <div class="demo-medium">
    <ui-item variant="outline">
      <ui-item-media variant="icon">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/></svg>
      </ui-item-media>
      <ui-item-content>
        <ui-item-title>Security alert</ui-item-title>
        <ui-item-description>New login detected from an unknown device.</ui-item-description>
      </ui-item-content>
      <ui-item-actions>
        <ui-button variant="outline" size="sm">Review</ui-button>
      </ui-item-actions>
    </ui-item>
  </div>
</body>
</html>

Image media

variant="image" crops an <img> into a rounded square.

Previewindex.html
Midnight City Lights Neon Dreams 3:45
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
    .demo-muted {
      margin: 0;
      font-size: var(--text-sm);
      color: var(--muted-foreground);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

  <div class="demo-medium">
    <ui-item variant="outline">
      <ui-item-media variant="image">
        <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%236366f1'/%3E%3Ccircle cx='44' cy='20' r='8' fill='%23e0e7ff'/%3E%3Cpath d='M0 64 22 30l18 22 10-12 14 24z' fill='%23c7d2fe'/%3E%3C/svg%3E" alt="">
      </ui-item-media>
      <ui-item-content>
        <ui-item-title>Midnight City Lights</ui-item-title>
        <ui-item-description>Neon Dreams</ui-item-description>
      </ui-item-content>
      <ui-item-actions>
        <span class="demo-muted">3:45</span>
      </ui-item-actions>
    </ui-item>
  </div>
</body>
</html>

With an avatar and several actions

Without variant, ui-item-media is a plain wrapper, so an avatar keeps its own look.

Previewindex.html
ER Evil Rabbit Last seen 5 months ago Message Invite
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/avatar.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

  <div class="demo-medium">
    <ui-item variant="outline">
      <ui-item-media>
        <ui-avatar><ui-avatar-fallback>ER</ui-avatar-fallback></ui-avatar>
      </ui-item-media>
      <ui-item-content>
        <ui-item-title>Evil Rabbit</ui-item-title>
        <ui-item-description>Last seen 5 months ago</ui-item-description>
      </ui-item-content>
      <ui-item-actions>
        <ui-button variant="outline" size="sm">Message</ui-button>
        <ui-button size="sm">Invite</ui-button>
      </ui-item-actions>
    </ui-item>
  </div>
</body>
</html>

Extra content

ui-item-content is a flex column, so anything after the description stacks under it.

Previewindex.html
Storage 7.2 GB of 10 GB used
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/progress.html"></import-element>

  <div class="demo-medium">
    <ui-item variant="muted">
      <ui-item-content>
        <ui-item-title>Storage</ui-item-title>
        <ui-item-description>7.2 GB of 10 GB used</ui-item-description>
        <ui-progress value="72" aria-label="Storage used"></ui-progress>
      </ui-item-content>
    </ui-item>
  </div>
</body>
</html>

ui-item-header and ui-item-footer take a full row above or below the media, content and actions, with their children spread apart.

Previewindex.html
Deployment Ready realm-site Production build from main, commit b6042d1. 2 minutes ago View logs
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
    .demo-muted {
      margin: 0;
      font-size: var(--text-sm);
      color: var(--muted-foreground);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/badge.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

  <div class="demo-medium">
    <ui-item variant="outline">
      <ui-item-header>
        <span class="demo-muted">Deployment</span>
        <ui-badge variant="secondary">Ready</ui-badge>
      </ui-item-header>
      <ui-item-content>
        <ui-item-title>realm-site</ui-item-title>
        <ui-item-description>Production build from main, commit b6042d1.</ui-item-description>
      </ui-item-content>
      <ui-item-footer>
        <span class="demo-muted">2 minutes ago</span>
        <ui-button variant="ghost" size="sm">View logs</ui-button>
      </ui-item-footer>
    </ui-item>
  </div>
</body>
</html>

Group

ui-item-group stacks items. Put a ui-separator between them for a divided list.

Previewindex.html
SC realm realm@example.com Remove ML maxleiter maxleiter@vercel.com Remove
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-medium {
      width: min(100%, calc(var(--spacing) * 120));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/avatar.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/separator.html"></import-element>

  <div class="demo-medium">
    <ui-item-group>
      <ui-item>
        <ui-item-media>
          <ui-avatar><ui-avatar-fallback>SC</ui-avatar-fallback></ui-avatar>
        </ui-item-media>
        <ui-item-content>
          <ui-item-title>realm</ui-item-title>
          <ui-item-description>realm@example.com</ui-item-description>
        </ui-item-content>
        <ui-item-actions>
          <ui-button variant="ghost" size="sm">Remove</ui-button>
        </ui-item-actions>
      </ui-item>
      <ui-separator></ui-separator>
      <ui-item>
        <ui-item-media>
          <ui-avatar><ui-avatar-fallback>ML</ui-avatar-fallback></ui-avatar>
        </ui-item-media>
        <ui-item-content>
          <ui-item-title>maxleiter</ui-item-title>
          <ui-item-description>maxleiter@vercel.com</ui-item-description>
        </ui-item-content>
        <ui-item-actions>
          <ui-button variant="ghost" size="sm">Remove</ui-button>
        </ui-item-actions>
      </ui-item>
    </ui-item-group>
  </div>
</body>
</html>

From state

Repeat items from a list with the repeat attribute, so each ui-item stays a direct child of the group and keeps the listitem role.

Previewindex.htmlitem-files-demo.html
Delete
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Item demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/item.html"></import-element>

  <import-element from="./item-files-demo.html"></import-element>
  <item-files-demo></item-files-demo>
</body>
</html>
<custom-element name="item-files-demo">
  <script>
    const folder = useState({
      files: [
        { id: "a", name: "invoice-march.pdf", size: "182 KB" },
        { id: "b", name: "team-photo.jpg", size: "2.4 MB" },
        { id: "c", name: "roadmap.md", size: "6 KB" },
      ],
    });

    function remove(file) {
      folder.files = folder.files.filter((item) => item.id !== file.id);
    }
  </script>
  <style>
    :host {
      display: block;
      width: min(100%, calc(var(--spacing) * 112));
    }
  </style>
  <ui-item-group>
    <ui-item size="sm" repeat="#folder.files" as="file" key="id">
      <ui-item-content>
        <ui-item-title><slot name="#file.name"></slot></ui-item-title>
        <ui-item-description><slot name="#file.size"></slot></ui-item-description>
      </ui-item-content>
      <ui-item-actions>
        <ui-button variant="ghost" size="sm" onclick="remove(file)">Delete</ui-button>
      </ui-item-actions>
    </ui-item>
  </ui-item-group>
</custom-element>

API

Parts

Part Slot Renders
ui-item One row, laid out as a wrapping flex container with a gap.
ui-item-group A column of items with the role list.
ui-item-media (default) A non-shrinking box for an icon, image or avatar. Slotted <svg> is sized to calc(var(--spacing) * 4).
ui-item-content (default) A flex column that fills the space between media and actions.
ui-item-title (default) The main line, in medium weight. Place it in ui-item-content.
ui-item-description (default) Secondary text in muted colour, clamped to two lines. Place it in ui-item-content.
ui-item-actions (default) A row of buttons or icons with a small gap.
ui-item-header (default) A full-width row above the rest, children spread apart.
ui-item-footer (default) A full-width row below the rest, children spread apart.

ui-item lays its parts out in source order. Put media first, then content, then actions. Every part has a single default slot that takes any content.

ui-item attributes

Attribute Type Default Description
variant "default" | "outline" | "muted" "default" Border and background style.
size "default" | "sm" "default" Padding and gap.

ui-item-media attributes

Attribute Type Default Description
variant "default" | "icon" | "image" "default" icon is a calc(var(--spacing) * 8) bordered, muted box. image is a calc(var(--spacing) * 10) rounded box whose <img> covers it. default is a plain wrapper.

The other parts have no attributes.

Accessibility

ui-item-group has the role list, and a ui-item directly inside a group has the role listitem, so screen readers report the list size. A standalone ui-item has no role. An item is not focusable or clickable by itself. Put interactive elements in ui-item-actions, or wrap the item in an <a> to make the whole row a link. Give an image in ui-item-media an alt text, or alt="" when the title already describes it.

Edit this page on GitHub · View as Markdown