Skip to content

feat: Voz a texto components + Storybook on GitHub Pages#3

Merged
lionelchamorro merged 1 commit into
mainfrom
feat/voz-a-texto-components
Jun 20, 2026
Merged

feat: Voz a texto components + Storybook on GitHub Pages#3
lionelchamorro merged 1 commit into
mainfrom
feat/voz-a-texto-components

Conversation

@lionelchamorro

@lionelchamorro lionelchamorro commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Qué incluye

Componentes nuevos de la feature Voz a texto (speech-to-text), extraídos pixel-exacto del Figma UI Library vía MCP y traducidos a recipes Panda (strictTokens, semantic tokens):

Componente Figma node Notas
AvatarPill 40002313:53080 Reusa Avatar + nombre; estados default/selected
Option 40001295:56887 Fila de menú con XCircle de cierre; selected/hover
CategoryItem 40001297:49803 Fila label + reusa Switch
TranscriptBlock 40002318:32859 Turno de transcripción; 4 estados (default/selected/hover/typed)
SidePanel 40002322:53113 Composite: turno + personas sugeridas + marca de tiempo + acciones

Tokens

  • Se promueve la paleta de hablantes bg/category/* del Figma a preset.ts (category.{violet,green,red,yellow,pink,orange,green-light}).
  • Avatar gana esos colores como variantes.

Íconos

  • Se vendoriza el glifo Phosphor ArrowsMerge (ArrowsMergeIcon.tsx) porque phosphor-react@1.4.1 es anterior a ese ícono. Plus/Trash/XCircle sí salen de phosphor-react.

CI / Pages

  • .github/workflows/pages.yml: buildea Storybook y lo publica en GitHub Pages en cada push a main.
  • Requiere activar Settings → Pages → Source: GitHub Actions (se intenta por API).
  • URL esperada: https://aymurai.github.io/ui-components/

Verificación

  • pnpm validate (biome + tsc) ✅
  • pnpm build (vite + panda cssgen) ✅
  • pnpm build-storybook ✅ (todos los stories compilan)

🤖 Generated with Claude Code

Summary by Sourcery

Add new Voz a texto UI components and color tokens, and publish Storybook to GitHub Pages on pushes to main.

New Features:

  • Introduce AvatarPill component for speaker pills with selectable states.
  • Add Option component for selectable list rows with removable items.
  • Add CategoryItem component for labelled rows with toggle switches.
  • Add TranscriptBlock component to represent individual transcript turns with multiple visual states.
  • Add SidePanel composite component for editing a selected transcript turn, managing suggested speakers, timestamps, and actions.
  • Expose new Voz a texto components from the public library index.

Enhancements:

  • Extend avatar color variants with a dedicated speaker/category color palette.
  • Define new category.* color tokens in the design preset for speaker differentiation.
  • Document the new category color palette and Voz a texto components in the component authoring guide.

CI:

  • Add a GitHub Actions workflow to build Storybook and deploy it to GitHub Pages on pushes to main.

Tests:

  • Add Storybook stories for AvatarPill, Option, CategoryItem, TranscriptBlock, and SidePanel components to document and visually test their states.

Adds the speech-to-text (Voz a texto) family extracted pixel-exact from the
Figma UI Library:

- AvatarPill (40002313:53080) — Avatar circle + name pill (default/selected)
- Option (40001295:56887) — menu row with dismiss XCircle
- CategoryItem (40001297:49803) — label row reusing Switch
- TranscriptBlock (40002318:32859) — turn block, 4 states
- SidePanel (40002322:53113) — composite assembling the above + TextField/Button

Tokens: promote the Figma "bg/category/*" speaker palette to preset.ts and
extend Avatar colours (violet/green/red/yellow/pink/orange/green-light).
Vendor the Phosphor ArrowsMerge glyph (phosphor-react@1.4.1 predates it).

CI: add pages.yml to build Storybook and deploy to GitHub Pages on push to main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds the Voz a texto (speech-to-text) UI component set (AvatarPill, Option, CategoryItem, TranscriptBlock, SidePanel) wired to Panda recipes and Storybook, extends the design tokens with a speaker/category color palette, vendors a missing Phosphor ArrowsMerge icon, and configures CI to build and deploy Storybook to GitHub Pages on pushes to main.

File-Level Changes

Change Details Files
Extend design tokens and Avatar to support speaker/category color palette used by Voz a texto components.
  • Add bg.category.* speaker palette colors to the Panda preset with pixel-exact hex values from Figma.
  • Expose category.* tokens in component-authoring documentation so authors know they are available.
  • Add new Avatar color variants (violet, green, red, yellow, pink, orange, green-light) wired to the category.* tokens and extend AvatarColor union type.
src/preset.ts
src/components/avatar/Avatar.tsx
docs/component-authoring.md
Introduce reusable Voz a texto primitives (AvatarPill, Option, CategoryItem, TranscriptBlock, SidePanel) and export them from the public entrypoint.
  • Implement AvatarPill as an Avatar + name pill with selected/default variants and AvatarColor passthrough.
  • Implement Option as a selectable list row with XCircle remove button, hover/selected background, and controlled via a selected variant.
  • Implement CategoryItem as a labelled row that wraps the existing Switch component and forwards basic switch props.
  • Implement TranscriptBlock as a cva-based component with default/selected/hover/typed variants, avatar header, timestamp, and styled transcript body.
  • Implement SidePanel as a composite turn editor assembling AvatarPill, TextField, and Button into sections (selected turn, suggested people, timestamp, actions) with callbacks for all actions.
  • Create Storybook stories for each new component, including variant demos and composite examples, wired to corresponding Figma nodes.
  • Export all new components and their types via their own index files and the root src/index.ts public barrel.
src/components/avatar-pill/AvatarPill.tsx
src/components/avatar-pill/AvatarPill.stories.tsx
src/components/avatar-pill/index.ts
src/components/option/Option.tsx
src/components/option/Option.stories.tsx
src/components/option/index.ts
src/components/category-item/CategoryItem.tsx
src/components/category-item/CategoryItem.stories.tsx
src/components/category-item/index.ts
src/components/transcript-block/TranscriptBlock.tsx
src/components/transcript-block/TranscriptBlock.stories.tsx
src/components/transcript-block/index.ts
src/components/side-panel/SidePanel.tsx
src/components/side-panel/SidePanel.stories.tsx
src/components/side-panel/ArrowsMergeIcon.tsx
src/components/side-panel/index.ts
src/index.ts
Vendor the Phosphor ArrowsMerge icon locally to match the Figma glyph while staying on phosphor-react 1.4.1.
  • Add ArrowsMergeIcon React component implementing the phosphor arrows-merge SVG path and a size prop, using currentColor for fill.
  • Use ArrowsMerge in SidePanel action buttons, rotating it 180° for the "merge with previous" action to match Figma orientation.
src/components/side-panel/ArrowsMergeIcon.tsx
src/components/side-panel/SidePanel.tsx
Enable Storybook deployment to GitHub Pages via GitHub Actions.
  • Add a pages.yml workflow that installs dependencies with pnpm, runs Panda codegen, builds Storybook, uploads it as a Pages artifact, and deploys it using actions/deploy-pages.
  • Configure workflow triggers on pushes to main and manual workflow_dispatch, with appropriate permissions and concurrency settings.
.github/workflows/pages.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@lionelchamorro lionelchamorro merged commit 8bc1647 into main Jun 20, 2026
2 checks passed
@lionelchamorro lionelchamorro mentioned this pull request Jun 20, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • The Option row is implemented as a clickable div plus a button, but only the button is keyboard-accessible; consider giving the row proper button semantics (e.g. role="button", tabIndex={0}, and onKeyDown for Enter/Space) or using a <button> to make selection accessible via keyboard and screen readers.
  • AvatarPill is used as an interactive control in SidePanel (onClick from the parent) but is rendered as a span with cursor: default; consider switching the root to a button-like element (or adding role="button", tabIndex, pointer cursor, and keyboard handlers) so it behaves like an accessible, obviously clickable chip.
  • Now that AvatarColor includes the new speaker palette (violet, green, red, etc.), you might want to expose those in Storybook controls (e.g. AvatarPill and TranscriptBlock argTypes) so consumers can visually exercise the full set of category colors from the UI library.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `Option` row is implemented as a clickable `div` plus a `button`, but only the button is keyboard-accessible; consider giving the row proper button semantics (e.g. `role="button"`, `tabIndex={0}`, and `onKeyDown` for Enter/Space) or using a `<button>` to make selection accessible via keyboard and screen readers.
- `AvatarPill` is used as an interactive control in `SidePanel` (`onClick` from the parent) but is rendered as a `span` with `cursor: default`; consider switching the root to a `button`-like element (or adding `role="button"`, `tabIndex`, pointer cursor, and keyboard handlers) so it behaves like an accessible, obviously clickable chip.
- Now that `AvatarColor` includes the new speaker palette (`violet`, `green`, `red`, etc.), you might want to expose those in Storybook controls (e.g. `AvatarPill` and `TranscriptBlock` argTypes) so consumers can visually exercise the full set of category colors from the UI library.

## Individual Comments

### Comment 1
<location path="src/components/avatar-pill/AvatarPill.tsx" line_range="13-20" />
<code_context>
+ * Two states: Default (white bg, lighter name) and Selected (primary-alternative
+ * bg, default name). The avatar circle colour is per-speaker (passthrough).
+ */
+const pillRoot = cva({
+  base: {
+    display: "inline-flex",
+    alignItems: "center",
+    gap: "2", // 8px
+    p: "2", // 8px
+    rounded: "xl", // 24px
+    cursor: "default",
+    userSelect: "none",
+  },
</code_context>
<issue_to_address>
**issue (bug_risk):** AvatarPill is used as a clickable control but rendered as a non-interactive span with a default cursor.

In `SidePanel` this pill is wired with `onClick`/`selected` but still lacks keyboard affordances and a pointer cursor, so it’s effectively inaccessible to keyboard users. Please either render a semantic `<button>` (or a `<div role="button" tabIndex={0}>` with `cursor: "pointer"`) when clickable, or add a prop (e.g. `interactive` / `as="button"`) that switches to an interactive element for these cases.
</issue_to_address>

### Comment 2
<location path="src/components/side-panel/SidePanel.tsx" line_range="148-150" />
<code_context>
+      {/* Suggested people */}
+      <Section heading="Personas sugeridas">
+        <div className={pills}>
+          {people.map((person, index) => (
+            <AvatarPill
+              key={`${person.initials}-${person.name}`}
+              initials={person.initials}
+              name={person.name}
</code_context>
<issue_to_address>
**issue (bug_risk):** The key for suggested people can easily collide when there are repeated names/initials.

This will generate duplicate React keys for entries like multiple "Defensor" personas, leading to unstable list rendering and console warnings. If there’s no stable unique ID in the data, consider using the array index here or another guaranteed-unique value instead.
</issue_to_address>

### Comment 3
<location path="src/components/option/Option.tsx" line_range="13-22" />
<code_context>
+ * dismiss XCircle on the right. Highlighted (selected/hover) uses
+ * bg.primary-alternative (#E5E8FF).
+ */
+const optionRoot = cva({
+  base: {
+    display: "flex",
+    alignItems: "center",
+    justifyContent: "space-between",
+    gap: "2",
+    w: "full",
+    h: "[32px]",
+    px: "2", // 8px
+    py: "[6px]",
+    rounded: "sm", // 4px
+    textStyle: "label.md.default",
+    color: "text.default",
+    cursor: "pointer",
+    userSelect: "none",
+    bg: "[transparent]",
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Option root is visually clickable but not keyboard-accessible or semantically interactive.

The root is a `<div>` styled with `cursor:
</issue_to_address>

### Comment 4
<location path="src/preset.ts" line_range="134-144" />
<code_context>
           },
+          // Speaker/category palette (Figma "bg/category/*") — distinguishes
+          // people in the Voz a texto flows (avatar fills, pills).
+          category: {
+            violet: color("#C5CAFF"), // bg/category/violet-ligth
+            green: color("#94FFC8"), // bg/category/green
+            red: color("#F69FA6"), // bg/category/red
+            yellow: color("#FDE27B"), // bg/category/yellow
+            pink: color("#F8AEEF"), // bg/category/pink
+            orange: color("#FFE2C4"), // bg/category/orange-ligth
+            "green-light": color("#D1F4E2"), // bg/category/green-ligth
+          },
           system: {
</code_context>
<issue_to_address>
**nitpick (typo):** There are a couple of typos in the palette comments that might cause confusion with the Figma names.

In the comments for `violet`, `orange`, and `green-light`, `light` is misspelled as `ligth` (e.g. `violet-ligth`, `green-ligth`). Since these mirror Figma token names, please correct the spelling to keep them aligned with the design file.

```suggestion
          // Speaker/category palette (Figma "bg/category/*") — distinguishes
          // people in the Voz a texto flows (avatar fills, pills).
          category: {
            violet: color("#C5CAFF"), // bg/category/violet-light
            green: color("#94FFC8"), // bg/category/green
            red: color("#F69FA6"), // bg/category/red
            yellow: color("#FDE27B"), // bg/category/yellow
            pink: color("#F8AEEF"), // bg/category/pink
            orange: color("#FFE2C4"), // bg/category/orange-light
            "green-light": color("#D1F4E2"), // bg/category/green-light
          },
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +13 to +20
const pillRoot = cva({
base: {
display: "inline-flex",
alignItems: "center",
gap: "2", // 8px
p: "2", // 8px
rounded: "xl", // 24px
cursor: "default",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): AvatarPill is used as a clickable control but rendered as a non-interactive span with a default cursor.

In SidePanel this pill is wired with onClick/selected but still lacks keyboard affordances and a pointer cursor, so it’s effectively inaccessible to keyboard users. Please either render a semantic <button> (or a <div role="button" tabIndex={0}> with cursor: "pointer") when clickable, or add a prop (e.g. interactive / as="button") that switches to an interactive element for these cases.

Comment on lines +148 to +150
{people.map((person, index) => (
<AvatarPill
key={`${person.initials}-${person.name}`}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The key for suggested people can easily collide when there are repeated names/initials.

This will generate duplicate React keys for entries like multiple "Defensor" personas, leading to unstable list rendering and console warnings. If there’s no stable unique ID in the data, consider using the array index here or another guaranteed-unique value instead.

Comment on lines +13 to +22
const optionRoot = cva({
base: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: "2",
w: "full",
h: "[32px]",
px: "2", // 8px
py: "[6px]",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Option root is visually clickable but not keyboard-accessible or semantically interactive.

The root is a <div> styled with `cursor:

Comment thread src/preset.ts
Comment on lines +134 to +144
// Speaker/category palette (Figma "bg/category/*") — distinguishes
// people in the Voz a texto flows (avatar fills, pills).
category: {
violet: color("#C5CAFF"), // bg/category/violet-ligth
green: color("#94FFC8"), // bg/category/green
red: color("#F69FA6"), // bg/category/red
yellow: color("#FDE27B"), // bg/category/yellow
pink: color("#F8AEEF"), // bg/category/pink
orange: color("#FFE2C4"), // bg/category/orange-ligth
"green-light": color("#D1F4E2"), // bg/category/green-ligth
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): There are a couple of typos in the palette comments that might cause confusion with the Figma names.

In the comments for violet, orange, and green-light, light is misspelled as ligth (e.g. violet-ligth, green-ligth). Since these mirror Figma token names, please correct the spelling to keep them aligned with the design file.

Suggested change
// Speaker/category palette (Figma "bg/category/*") — distinguishes
// people in the Voz a texto flows (avatar fills, pills).
category: {
violet: color("#C5CAFF"), // bg/category/violet-ligth
green: color("#94FFC8"), // bg/category/green
red: color("#F69FA6"), // bg/category/red
yellow: color("#FDE27B"), // bg/category/yellow
pink: color("#F8AEEF"), // bg/category/pink
orange: color("#FFE2C4"), // bg/category/orange-ligth
"green-light": color("#D1F4E2"), // bg/category/green-ligth
},
// Speaker/category palette (Figma "bg/category/*") — distinguishes
// people in the Voz a texto flows (avatar fills, pills).
category: {
violet: color("#C5CAFF"), // bg/category/violet-light
green: color("#94FFC8"), // bg/category/green
red: color("#F69FA6"), // bg/category/red
yellow: color("#FDE27B"), // bg/category/yellow
pink: color("#F8AEEF"), // bg/category/pink
orange: color("#FFE2C4"), // bg/category/orange-light
"green-light": color("#D1F4E2"), // bg/category/green-light
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant