feat: Voz a texto components + Storybook on GitHub Pages#3
Conversation
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>
Reviewer's GuideAdds 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
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 4 issues, and left some high level feedback:
- The
Optionrow is implemented as a clickabledivplus abutton, but only the button is keyboard-accessible; consider giving the row proper button semantics (e.g.role="button",tabIndex={0}, andonKeyDownfor Enter/Space) or using a<button>to make selection accessible via keyboard and screen readers. AvatarPillis used as an interactive control inSidePanel(onClickfrom the parent) but is rendered as aspanwithcursor: default; consider switching the root to abutton-like element (or addingrole="button",tabIndex, pointer cursor, and keyboard handlers) so it behaves like an accessible, obviously clickable chip.- Now that
AvatarColorincludes the new speaker palette (violet,green,red, etc.), you might want to expose those in Storybook controls (e.g.AvatarPillandTranscriptBlockargTypes) 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| const pillRoot = cva({ | ||
| base: { | ||
| display: "inline-flex", | ||
| alignItems: "center", | ||
| gap: "2", // 8px | ||
| p: "2", // 8px | ||
| rounded: "xl", // 24px | ||
| cursor: "default", |
There was a problem hiding this comment.
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.
| {people.map((person, index) => ( | ||
| <AvatarPill | ||
| key={`${person.initials}-${person.name}`} |
There was a problem hiding this comment.
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.
| const optionRoot = cva({ | ||
| base: { | ||
| display: "flex", | ||
| alignItems: "center", | ||
| justifyContent: "space-between", | ||
| gap: "2", | ||
| w: "full", | ||
| h: "[32px]", | ||
| px: "2", // 8px | ||
| py: "[6px]", |
There was a problem hiding this comment.
suggestion (bug_risk): Option root is visually clickable but not keyboard-accessible or semantically interactive.
The root is a <div> styled with `cursor:
| // 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 | ||
| }, |
There was a problem hiding this comment.
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.
| // 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 | |
| }, |
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):
AvatarPill40002313:53080Avatar+ nombre; estados default/selectedOption40001295:56887XCirclede cierre;selected/hoverCategoryItem40001297:49803SwitchTranscriptBlock40002318:32859SidePanel40002322:53113Tokens
bg/category/*del Figma apreset.ts(category.{violet,green,red,yellow,pink,orange,green-light}).Avatargana esos colores como variantes.Íconos
ArrowsMerge(ArrowsMergeIcon.tsx) porquephosphor-react@1.4.1es anterior a ese ícono.Plus/Trash/XCirclesí salen de phosphor-react.CI / Pages
.github/workflows/pages.yml: buildea Storybook y lo publica en GitHub Pages en cada push amain.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:
Enhancements:
CI:
Tests: