# Conversation Scene

Canonical: https://socra.design/landing/visuals/conversation-scene

A Channels exchange rendered with the real message components.

Show people and AI teammates working in Channels with the product itself as the picture. The page passes the channel, the participants, the messages and the label that states what the scene is; the scene renders the real message list, mentions and attachments, plays once and holds still under reduced motion.

Status: ready. Shared conversation scene (landing.ConversationScene — the Web UI Pane, AppBar, MessageList, MessageEntry, MessageProse, MemberMention and Attachment inside the shared HeroMediaFrame; each message carries the identity card and an AI teammate its chip; a CSS reveal from the first paint, once, with no script and no motion when the visitor asks for less; an inert interface described in one accessible label; the required label beneath states illustrative or recorded) lives in @socra/ui-web-landing.

Library: @socra/ui-web-landing

## Anatomy

- Intent, states, feedback, hierarchy, and access requirements are independent of any renderer.
- Every shared visual relationship resolves from semantic design roles without changing their meaning between platforms.
- Each platform uses its native mechanics and provides live conformance evidence without changing the guideline.

## States

- **Rest:** Shows the stable, enabled component without implied activity.
- **Hover:** Every enabled clickable target shows a pointing hand. Keep the shared visual hover treatment independent, without moving content or changing meaning.
- **Focus:** Shows the shared visible focus treatment without depending on hover.
- **Pressed:** Acknowledges active input immediately and returns cleanly on release.
- **Selected:** Uses a neutral surface plus native selected semantics when selection applies.
- **Disabled:** Remains legible, unavailable, and absent from misleading interaction feedback.
- **Loading:** Preserves context while honestly identifying work that has not completed.
- **Error:** Places the failure and recovery path beside the action or content that failed.

## Motion

Stable state changes respond immediately. Appearance, expansion, and morphing begin at the triggering origin and use an interruptible spring that can retarget from its live position and velocity. When spatial motion is not meaningful, the component changes without decorative travel.

## Usage

- Use the owning platform implementation first. Each renderer improves once and every product on that platform inherits the fix.
- Name the user job before the component. A component earns a place only when it helps the user decide, act, recover, or understand.
- Promote missing behavior into the owning platform domain. Product-local UI creates drift; platform contracts preserve shared meaning and native quality.
- Raise luminance to bring a surface forward. Depth reads as light. A surface that comes toward the reader catches more of it, so a raised panel, a selected tab, a menu, a popover resolves LIGHTER than what it sits on — in dark mode away from the canvas, in light mode toward the white it cannot exceed. The ladder runs page, resting card, elevated, and never doubles back.

## Avoid

- Do not build a product-local replacement. A local implementation splits behavior, access requirements, and future migrations.
- Do not copy theme values. Semantic values are shared system roles and are consumed by meaning.
- Do not add decoration as a substitute for structure. If the hierarchy is weak, fix the hierarchy.
- Do not darken a surface to lift it. Darkening in a dark theme pushes a surface INTO the page — it reads as a hole cut in the canvas rather than a panel resting on it. Sinking a surface to make it stand out is the most common way a dark interface loses its depth.

## Tokens

- `designTokens.color[mode].color.brand.primary`: Primary action role.
- `designTokens.color[mode].color.surface.secondary`: Working surface role.
- `designTokens.color[mode].color.content.primary`: Primary content role.
- `designTokens.spacing.role.contentGap`: Spacing relationship.
- `designTokens.shape.role.control`: Shape relationship.

## Properties

[Complete public API, including inherited properties and event signatures](https://socra.design/landing/visuals/conversation-scene/api.md)

### Setup

- Use the host-selected compatible Socra theme provider and published component styles. Keep one compatible provider instance across shared packages.

### ConversationScene

Import: `import { ConversationScene } from '@socra/ui-web-landing';`

A Channels exchange rendered with the real Web UI message components inside the shared media frame, labelled by its truth.

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `channel` | `string` | Required input | yes | The channel’s name without its #, shown in the pane’s title. | Direct API |
| `label` | `string` | Required input | yes | What the scene is, stated beneath the frame and leading its accessible description: An illustrative conversation, or Recorded with the date. Required; never present an invented exchange as recorded. Rendered as the figure’s caption and as the start of the role="img" label that reads the whole exchange aloud. | Direct API |
| `messages` | `readonly ConversationSceneMessage[]` | Required input | yes | The exchange in reading order: a stable id, the author id, the time label, the text and an optional delivered file. Keep each message short enough to read at phone width. The attachment takes a file name and one short line. Each message renders as the Web UI MessageEntry with the author’s squircle Avatar, name, time and prose; a file renders as the Web UI Attachment with a file glyph. | Direct API |
| `participants` | `readonly ConversationSceneParticipant[]` | Required input | yes | Everyone the messages name as an author: a stable id, a name, a kind of human or ai-teammate and an optional portrait URL. Every message’s author must match a participant id; an unknown author renders its id as the name without a portrait. An AI teammate’s messages carry the secondary sm AI teammate chip in the message header; a person’s carry none. A participant’s name, written as @Name inside any message, renders as the real MemberMention. | Direct API |
| `portraitLoading` | `"eager"`, `"lazy"` | `"lazy"` | no | How the participants’ portraits load. Pass eager only for a scene on the first screen, such as a page hero. lazy sets loading="lazy" on each portrait so nothing below the first screen is preloaded; eager sets fetchPriority="high" so a hero’s portraits lead the page. | Direct API |
| `reveal` | `false`, `true` | `true` | no | Whether the messages arrive one at a time when the page loads. The reveal is a CSS animation from the first paint, so the exchange is never shown and then hidden and plays once without script; prefers-reduced-motion shows the complete exchange at once. false renders the settled frame. | Play once |

### Band

Import: `import { Band } from '@socra/ui-web-landing';`

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `ariaLabelledby` | `string \| undefined` | Omitted; no declared default | no | id of the band's heading — keeps the <section> named for screen readers. | Direct API |
| `bleed` | `false`, `true` | `false` | no | Band owns its full width (carousels): children render edge-to-edge with no content column. The child then re-creates the column where it needs one. | Direct API |
| `children` | `ReactNode` | Required input | yes |  | Direct API |
| `colorMode` | `"dark"`, `"ambient"` | `"ambient"` | no | whether the band is fixed dark or follows the ambient page theme. | Direct API |
| `contentMaxWidth` | `string \| undefined` | `"1120px"` | no | content column width; the production landing column. | Direct API |
| `divider` | `false`, `true` | `false` | no | The divider at a standalone band's leading edge. Inside a marketing page the band sequence puts the divider on every boundary, so pages do not set it. | Direct API |
| `id` | `string \| undefined` | Omitted; no declared default | no | anchor id for in-page navigation. | Direct API |
| `spacing` | `"none"`, `"compact"`, `"screen"`, `"regular"` | `"regular"` | no | the band's vertical rhythm. | Direct API |
| `tone` | `"base"`, `"surface"`, `"raised"` | `"base"` | no | A standalone band's surface step. Inside a marketing page the band sequence alternates base and surface by position, so pages do not set it. | Direct API |

### BandIntro

Import: `import { BandIntro } from '@socra/ui-web-landing';`

| Property | Type or permitted values | Default / omission | Required | Meaning and behavior | Inspector |
| --- | --- | --- | --- | --- | --- |
| `align` | `"center"`, `"responsive"` | `"responsive"` | no | whether the column centers at every width or only from \`md\`. | Direct API |
| `children` | `ReactNode` | Omitted; no declared default | no | extra intro content below the description (a second paragraph, a CTA). | Direct API |
| `description` | `string \| undefined` | Omitted; no declared default | no | optional standfirst under the heading (\`subhead\` role). | Direct API |
| `headingScale` | `"section"`, `"statement"` | `'section'` | no | the heading scale. | Direct API |
| `headingTreatment` | `"solid"`, `"gradient"` | `'gradient'` | no | the heading paint treatment. | Direct API |
| `id` | `string \| undefined` | Omitted; no declared default | no | heading id — the target of the owning band's \`ariaLabelledby\`. | Direct API |
| `level` | `"h1"`, `"h2"` | `'h2'` | no | the heading level; the page's first band passes 'h1'. | Direct API |
| `title` | `ReactNode` | Required input | yes | the band heading, rendered through <BandHeader/>. | Direct API |

### Behavior and constraints

- ConversationSceneParticipant, ConversationSceneMessage, ConversationSceneAttachment and ConversationSceneProps are public type exports.
- The interface inside the frame is inert and hidden from assistive technology; the figure exposes one role="img" whose label states the scene’s truth, the channel and every message with its author, kind and time. The component owns no actions: nothing in it can be pressed, selected or focused.
- The fixture is a neutral fictional exchange between a person and an AI teammate, both shown by their initials: a real portrait appears only under its own name. Product hosts pass approved content and portraits and label a real exchange only after it is approved for publication.

### Inspector mappings

| Setting | Initial preview value | Consumed code |
| --- | --- | --- |
| Label | `An illustrative conversation` | composition: Passes String(configuration.label ?? 'An illustrative conversation').trim() \|\| that default to ConversationScene.label, so a blank inspector value retains a truthful label. The public label itself remains required without a default. |
| Play once | `true` | ConversationScene.reveal |
