<user-persona>
Displays user personas in an Agile-friendly visual format with avatar, quote, goals, frustrations, and behaviors.
Overview
A web component for displaying user personas in a structured, visual card format. Includes an auto-generated avatar with initials and a deterministic color derived from the name hash, a quote block, and slotted sections for biography, goals, frustrations, and behaviors. Ideal for design documentation, sprint planning boards, and stakeholder presentations.
<user-persona role="Product Manager" age="34" location="San Francisco, CA"> <h2 slot="name">Sarah Chen</h2> <p slot="quote">I need tools that help me stay organized across multiple projects.</p> <p slot="bio">Sarah has 8 years of experience in product management at mid-size SaaS companies.</p> <ul slot="goals"> <li>Streamline team communication</li> <li>Track project progress efficiently</li> </ul> <ul slot="frustrations"> <li>Too many disconnected tools</li> <li>Difficulty getting stakeholder buy-in</li> </ul> <ul slot="behaviors"> <li>Checks dashboards first thing each morning</li> <li>Prefers async communication over meetings</li> </ul></user-persona>
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
role |
string | — | Job title or role description |
age |
string | — | Age displayed as "[value] years old" |
location |
string | — | Geographic location shown with a pin icon |
avatar |
string (URL) | — | Custom avatar image URL. When omitted, initials are generated from the name. |
compact |
boolean | — | Reduces padding, font sizes, and avatar size for denser layouts |
src |
string (URL) | — | URL to JSON file containing persona data. Loads and populates all fields. |
Slots
| Slot | Expected Content | Description |
|---|---|---|
name |
<h2> or text |
Full name of the persona. Defaults to "Unnamed Persona" when omitted. |
quote |
<p> or text |
Representative quote displayed in an italic block with a decorative quotation mark. |
bio |
<p> or text |
Background information about the persona |
goals |
<ul> or <ol> |
What the persona wants to achieve. Displayed with a green accent icon. |
frustrations |
<ul> or <ol> |
Pain points and frustrations. Displayed with a red accent icon. |
behaviors |
<ul> or <ol> |
Behavioral patterns and habits. Displayed with a purple accent icon. |
Content values (name, quote) are provided as named slots. State attributes (role, age, location, avatar) remain on the element.
Avatar Behavior
When no avatar URL is provided, the component generates an avatar automatically:
- Initials — extracted from the first letter of each word in the name, capped at two characters (e.g., "Sarah Chen" becomes "SC")
- Background color — a deterministic HSL hue is computed from a hash of the name string, ensuring the same name always produces the same color
To use a custom image instead, set the avatar attribute to any image URL. The image renders as a circular background-image with center/cover sizing.
<user-persona role="Product Manager" avatar="https://example.com/team/sarah.jpg"> <h2 slot="name">Sarah Chen</h2> <p slot="bio">Uses a custom photo instead of generated initials.</p></user-persona>
Compact Variant
Add the compact attribute for a denser layout with smaller padding, reduced font sizes, and a 56px avatar (down from 80px). Useful when displaying multiple personas side by side or in a dashboard grid.
<user-persona role="Frontend Developer" age="28" location="Austin, TX" compact> <h2 slot="name">Alex Rivera</h2> <p slot="quote">I want clean APIs and minimal boilerplate.</p> <ul slot="goals"> <li>Ship features faster</li> <li>Reduce build complexity</li> </ul> <ul slot="frustrations"> <li>Breaking changes in dependencies</li> </ul></user-persona>
JSON Loading
Load persona data from a JSON file using the src attribute. All fields are mapped to attributes or cached for rendering.
<user-persona src="/api/personas/sarah.json"></user-persona>
{ "name": "Sarah Chen", "role": "Senior Product Manager", "age": "34", "location": "San Francisco, CA", "avatar": "https://example.com/sarah.jpg", "quote": "I need tools that help me stay organized.", "bio": "Sarah has 8 years of experience in product management.", "goals": ["Streamline communication", "Track progress efficiently"], "frustrations": ["Too many disconnected tools", "Unclear ownership"], "behaviors": ["Checks Slack 30+ times per day", "Prefers async communication"]}
Mock Data
Add data-mock to auto-generate a complete persona with a portrait photo, demographics, quote, and slotted sections. Use data-seed for deterministic results.
<!-- Everything auto-generated --><user-persona data-mock></user-persona> <!-- Deterministic — same seed, same persona --><user-persona data-mock data-seed="PM-1"></user-persona> <!-- Partial override — set what you know, mock the rest --><user-persona data-mock data-seed="eng-1" role="VP of Engineering"> <h2 slot="name">Jordan Rivera</h2></user-persona>
The portrait images come from the faker-js portrait assets (CC0 licensed). The face is deterministic — the same name always shows the same person.
Story Traceability
Add data-list-stories and the persona card auto-renders a Stories section listing every <user-story> on the page whose persona-id matches this persona's id. The section updates live as stories are added, removed, or relabeled.
<user-persona id="alex" role="Product Manager" data-list-stories> <h2 slot="name">Alex Chen</h2></user-persona> <user-story persona-id="alex" priority="high" status="in-progress" story-id="ONB-1"> <span slot="action">a one-page onboarding flow</span> <span slot="benefit">customers don't bounce before linking a bank account</span></user-story>
Each entry links to the matching story by id when available, alongside its priority and status badges. The persona reads stories from the document scope (or its root node), so it works inside any layout that keeps personas and stories on the same page.
Slot-Based Authoring
Content values — name and quote — are provided as named slots. State attributes like role, age, and location remain on the element. This separation keeps content in the DOM and metadata in attributes.
<user-persona role="Senior Product Manager" age="34" location="San Francisco, CA"> <h2 slot="name">Sarah Chen</h2> <p slot="quote">I need tools that help me stay organized.</p> <p slot="bio">Sarah has 8 years of experience in product management.</p> <ul slot="goals"> <li>Streamline communication</li> <li>Track progress efficiently</li> </ul> <ul slot="frustrations"> <li>Too many disconnected tools</li> </ul></user-persona>
CSS Custom Properties
| Variable | Default | Description |
|---|---|---|
--user-persona-bg |
#ffffff / #1e1e1e |
Card background color |
--user-persona-text |
#1a1a1a / #e8e8e8 |
Primary text color |
--user-persona-muted |
#666666 / #888888 |
Secondary/muted text color |
--user-persona-border |
#e0e0e0 / #333333 |
Card and section border color |
--user-persona-accent |
#0066cc / #6b9fff |
Accent color for role text and bio icon |
--user-persona-card-bg |
#f8f9fa / #252525 |
Section card background (quote block, content sections) |
--user-persona-radius |
16px |
Outer card border radius |
--user-persona-avatar-size |
80px |
Avatar width and height (56px in compact mode) |
--user-persona-goals |
#22c55e / #4ade80 |
Goals section accent color |
--user-persona-frustrations |
#ef4444 / #f87171 |
Frustrations section accent color |
--user-persona-behaviors |
#8b5cf6 |
Behaviors section accent color |
--user-persona-stories |
#3b82f6 |
Stories section accent color (when data-list-stories is set) |
Events
| Event | Detail | When |
|---|---|---|
persona-ready |
{ name, role, age, location } |
Fires after the component renders for the first time |
user-persona:upgraded |
— | Once after first connect. Wait for this before assigning .data from a framework. |
user-persona:data-changed |
{ data, source: 'property' } |
After .data assignment. |
JavaScript API
<user-persona> follows the
HTML-first + JS-first dual-mode contract.
Reactive consumers (Montane, Lit, Solid, etc.) should wait for
user-persona:upgraded before assigning .data.
| Property | Type | Description |
|---|---|---|
.data |
{ name?, role?, age?, location?, avatar?, quote?, bio?, goals?, frustrations?, behaviors? } |
Whole-record assignment. Sets state attributes and creates slotted children for any text fields not already present. Idempotent. |
const persona = document.querySelector('user-persona');await new Promise(r => persona.addEventListener('user-persona:upgraded', r, { once: true }));persona.data = { name: 'Sarah Chen', role: 'Product Manager', age: 34, location: 'Portland, OR', quote: 'I want to ship faster without breaking things.', goals: ['Ship weekly', 'Reduce churn'], frustrations: ['Slow CI', 'Unclear specs'],};
Accessibility
- The outer card uses
role="article"with anaria-labelof "User persona: [name]" - The avatar element has
role="img"witharia-label="Avatar for [name]" - The decorative quotation mark uses
aria-hidden="true"so screen readers skip it - Each content section has a visible heading for screen reader navigation
- Respects
prefers-reduced-motion: reduceby disabling all transitions - Responsive layout stacks to a single column at narrow viewports (<600px)
Related
<user-story>— Agile user story cards referencing personas<user-journey>— Journey maps that link back to persona definitions<empathy-map>— Empathy maps linked viapersona-id<impact-effort>— Prioritization matrix for triaging stories<story-map>— Horizontal story mapping for sprint planning- UX Planning Pack — loads all six UX components together