> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unleeshed.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Display Commentaries

> Best practices for showing Unleeshed content on your platform

## Overview

This guide covers best practices for displaying AI-generated commentary on your platform, ensuring a great user experience while meeting licensing requirements.

## Required Attribution

All displayed commentaries **must** include Unleeshed attribution:

<Frame>
  <img src="https://mintlify.s3.us-west-1.amazonaws.com/unleeshed/images/attribution-example.png" alt="Attribution example" />
</Frame>

```html theme={null}
<footer class="unleeshed-attribution">
  Powered by <a href="https://unleeshed.ai">Unleeshed</a>
</footer>
```

## Display Patterns

### Single Persona Commentary

For displaying one persona's take:

```jsx theme={null}
function CommentaryCard({ commentary }) {
  return (
    <article className="commentary-card">
      <header className="persona-header">
        <img 
          src={commentary.persona.image_url} 
          alt={commentary.persona.name}
          className="persona-avatar"
        />
        <div className="persona-info">
          <h3>{commentary.persona.name}</h3>
          <span className="expertise">NBA Analyst</span>
        </div>
      </header>
      
      <div className="commentary-content">
        <p>{commentary.content}</p>
      </div>
      
      {commentary.audio_url && (
        <AudioPlayer src={commentary.audio_url} />
      )}
      
      <footer className="attribution">
        Powered by <a href="https://unleeshed.ai">Unleeshed</a>
      </footer>
    </article>
  );
}
```

### Multiple Personas (Debate Style)

Show different perspectives side-by-side:

```jsx theme={null}
function DebateView({ topic, commentaries }) {
  return (
    <section className="debate-view">
      <header className="topic">
        <h2>{topic.content}</h2>
      </header>
      
      <div className="perspectives">
        {commentaries.map(c => (
          <article key={c.commentary_id} className="perspective">
            <div className="persona">
              <img src={c.persona.image_url} alt="" />
              <h3>{c.persona.name}</h3>
            </div>
            <blockquote>{c.content}</blockquote>
          </article>
        ))}
      </div>
      
      <footer className="attribution">
        Powered by <a href="https://unleeshed.ai">Unleeshed</a>
      </footer>
    </section>
  );
}
```

### Tabbed Navigation

Let users switch between personas:

```jsx theme={null}
function TabbedCommentary({ commentaries }) {
  const [activeId, setActiveId] = useState(commentaries[0]?.persona.id);
  const active = commentaries.find(c => c.persona.id === activeId);
  
  return (
    <div className="tabbed-commentary">
      <nav className="persona-tabs">
        {commentaries.map(c => (
          <button
            key={c.persona.id}
            onClick={() => setActiveId(c.persona.id)}
            className={c.persona.id === activeId ? 'active' : ''}
          >
            <img src={c.persona.image_url} alt="" />
            <span>{c.persona.name}</span>
          </button>
        ))}
      </nav>
      
      {active && (
        <article className="active-commentary">
          <p>{active.content}</p>
          {active.audio_url && <AudioPlayer src={active.audio_url} />}
        </article>
      )}
      
      <footer className="attribution">
        Powered by <a href="https://unleeshed.ai">Unleeshed</a>
      </footer>
    </div>
  );
}
```

## Audio Player

For commentaries with audio:

```jsx theme={null}
function AudioPlayer({ src, personaName }) {
  const [isPlaying, setIsPlaying] = useState(false);
  const audioRef = useRef(null);
  
  const togglePlay = () => {
    if (isPlaying) {
      audioRef.current.pause();
    } else {
      audioRef.current.play();
    }
    setIsPlaying(!isPlaying);
  };
  
  return (
    <div className="audio-player">
      <audio 
        ref={audioRef} 
        src={src}
        onEnded={() => setIsPlaying(false)}
      />
      <button onClick={togglePlay} aria-label={isPlaying ? 'Pause' : 'Play'}>
        {isPlaying ? <PauseIcon /> : <PlayIcon />}
      </button>
      <span>Listen to {personaName}</span>
    </div>
  );
}
```

## Styling Guidelines

### Typography

* Use readable font sizes (16px+ for body text)
* Maintain good contrast (4.5:1 minimum)
* Quote styling for commentary content

### Persona Avatars

* Minimum size: 48x48px
* Use `border-radius` for circular avatars
* Include alt text for accessibility

### Responsive Design

```css theme={null}
.commentary-card {
  max-width: 600px;
  padding: 1.5rem;
}

@media (max-width: 640px) {
  .commentary-card {
    padding: 1rem;
  }
  
  .persona-avatar {
    width: 40px;
    height: 40px;
  }
}
```

## Loading States

Show meaningful loading states during generation:

```jsx theme={null}
function CommentaryLoader({ personas, status, summary }) {
  return (
    <div className="loading-state">
      <div className="progress">
        <div 
          className="progress-bar"
          style={{ width: `${(summary.completed / summary.total_personas) * 100}%` }}
        />
      </div>
      <p>
        Generating commentary... 
        ({summary.completed}/{summary.total_personas} ready)
      </p>
      <div className="persona-status">
        {personas.map(p => (
          <div key={p.id} className={`persona ${p.status}`}>
            <img src={p.image_url} alt="" />
            {p.status === 'completed' && <CheckIcon />}
            {p.status === 'pending' && <Spinner />}
          </div>
        ))}
      </div>
    </div>
  );
}
```

## Accessibility

<AccordionGroup>
  <Accordion title="Screen readers">
    * Use semantic HTML (`article`, `header`, `blockquote`)
    * Include `alt` text for all images
    * Announce dynamic content changes
  </Accordion>

  <Accordion title="Keyboard navigation">
    * All interactive elements focusable
    * Logical tab order
    * Visible focus indicators
  </Accordion>

  <Accordion title="Audio content">
    * Provide text transcript (commentary text)
    * Keyboard-accessible controls
    * Visual playback indicators
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Error Handling" icon="triangle-exclamation" href="/guides/error-handling">
    Handle failures gracefully.
  </Card>

  <Card title="Rate Limits" icon="gauge" href="/resources/rate-limits">
    Understand API limits.
  </Card>
</CardGroup>
