# Token-Styled Select Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Replace the four raw ``, so the affected tests are rewritten from `userEvent.selectOptions` to open-trigger + click-option. **Tech Stack:** React 19 + TS + pnpm, Base UI (`@base-ui/react/select`, namespace `Select`), react-hook-form (object-form), lucide-react (Chevron/Check), Vitest + RTL + Storybook. Test runner: `pnpm test` (single pass). **Conventions:** pnpm; **no `any`/`eslint-disable`/`@ts-ignore`**; no codename; en/sv parity (no new keys expected); **ui/ files = no-semicolon** (match `ui/combobox.tsx`/`ui/menu.tsx`); app source = double-quote+semicolon; stories single-quote/no-semicolon; token classes only; this repo enforces `react-hooks/refs` + `react-refresh/only-export-components` — refactor cleanly, never disable. **Spec:** `docs/superpowers/specs/2026-06-08-token-select-design.md` **Key facts:** - Base UI: `import { Select as SelectPrimitive } from "@base-ui/react/select"` — parts `Root, Trigger, Value, Icon, Portal, Positioner, Popup, List, Item, ItemIndicator, ItemText`. Mirror `ui/combobox.tsx`/`ui/menu.tsx` wrapper style. **Novel → validate by running.** - Input className to match (`ui/input.tsx`): `h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 … focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:… aria-invalid:border-destructive dark:bg-input/30`. - `object-form.tsx`: visibility ` Apple Pear Plum ) } const meta = { component: Select, tags: ['ai-generated'], render: () => } satisfies Meta export default meta type Story = StoryObj export const Default: Story = { play: async ({ canvas, userEvent }) => { await userEvent.click(canvas.getByRole('combobox', { name: 'Fruit' })) await userEvent.click(await within(document.body).findByRole('option', { name: 'Pear' })) await expect(canvas.getByRole('combobox', { name: 'Fruit' })).toHaveTextContent('Pear') }, } ``` (If the Base UI Select trigger isn't role `combobox`, adjust the query to what it actually is — discover by running. If `onValueChange`/`value` prop names differ, fix. The story passing IS the validation; report the final working API.) - [ ] **Step 4: Validate by running (vitest ONCE):** `cd web && pnpm vitest run src/components/ui/select.stories.tsx && pnpm typecheck && pnpm lint` Iterate the wrapper until the story play test passes. Report the FINAL working API (exports, the trigger role/accessible-name mechanism, value/onValueChange names, placeholder mechanism) — Tasks 2/3 depend on it. - [ ] **Step 5: Commit** ```bash git add web/src/components/ui/select.tsx web/src/components/ui/select.stories.tsx git commit -m "feat(web): ui/select Base UI Select wrapper matching Input + story (#51)" ``` --- # Task 2: object-form visibility → ui/Select **Files:** `web/src/objects/object-form.tsx`, `web/src/objects/object-form.test.tsx`. - [ ] **Step 1: Replace the visibility ` {t("form.draft")} {t("form.internal")} )} /> )} ``` (Use the exact value/onValueChange/trigger-id API confirmed in Task 1. `form.control` is available from `useForm`; destructure `control` or use `form.control`. The default value stays `"draft"` from `defaultValues`.) Keep `