feat(playground): add Sandpack component playground#349
Conversation
bntvllnt
left a comment
There was a problem hiding this comment.
Review — 1 finding (blocking, published as COMMENT)
BLOCKING
C1 — Sandpack preview omits the VLLNT Tailwind contract- Evidence:
apps/registry/components/playground/sandpack-playground.tsx:92-100generates a sandbox Tailwind config with only./index.htmland./src/**/*.{ts,tsx}content,theme.extend: {}, and no@vllnt/ui/tailwind-preset/equivalent token theme. The sandbox examples import@vllnt/ui/styles.cssand@vllnt/ui/themes/default.css, and both/src/main.tsxplus the fallback demo use token utilities such asbg-background,text-foreground,bg-card, andtext-card-foreground. Those utilities are defined by the VLLNT Tailwind preset/theme, not Tailwind defaults. - Why it matters: the new playground is meant to render real
@vllnt/uiexamples, but Tailwind will not generate the design-token utilities or scan the package internals. Published components and the default fallback demo can render unstyled or partially styled in the preview even while the docs page itself looks correct. - Fix: make the generated sandbox Tailwind config consume the exported
@vllnt/ui/tailwind-presetor inline the same token theme/plugin contract, and include the published package/dist/source paths needed for component-internal class scanning.
- Evidence:
WARN
- None.
VERIFIED CLEAN
- Re-fetched PR #349 immediately before publishing; head is still
8c4961fd7bdb900848908515345c799252a5f6a0onfeat/256-sandpack-playground. - Re-checked the previously found blocker against the live diff and package contract:
@vllnt/uiexports./tailwind-preset, while the generated sandbox config still omits it. - Existing GitHub checks are green at this head.
VALIDATION
- Ran:
gh pr view 349 --repo vllnt/ui --json ...before review and before publish. - Ran:
gh pr checks 349 --repo vllnt/ui(all listed checks passing). - Inspected:
apps/registry/components/playground/sandpack-playground.tsx,apps/registry/lib/playground.ts,packages/ui/package.json,packages/ui/src/tailwind-preset.ts, and registry/package Tailwind configs. - Not run: live browser Sandpack smoke; this publication reconciles the already-local blocker against the current live head.
Note: GitHub rejected REQUEST_CHANGES from the current authenticated account because it is the PR author, so this formal COMMENT review carries the blocking review gate instead of a request-changes verdict.
| "/tailwind.config.js": `/** @type {import("tailwindcss").Config} */ | ||
| export default { | ||
| darkMode: ["class"], | ||
| content: ["./index.html", "./src/**/*.{ts,tsx}"], |
There was a problem hiding this comment.
Blocking: this generated Tailwind config does not load the VLLNT Tailwind preset/theme or scan @vllnt/ui internals. The sandbox imports @vllnt/ui/styles.css / themes/default.css, and the examples use token utilities such as bg-background, text-foreground, bg-card, and text-card-foreground, but those utilities are not Tailwind defaults. Without @vllnt/ui/tailwind-preset (or the equivalent token theme/plugin contract plus package content scanning), the playground can render real components/default examples unstyled or partially styled even though the docs page itself is styled. Please wire the generated sandbox Tailwind config to the exported VLLNT preset and include the package paths needed for component class scanning.
|
Preview ready · Updated 2026-05-20T14:42:40Z
Inspect
|
- Pin vite to ^7.3.2 and @vitejs/plugin-react to ^5.0.0 in sandbox-generated package.json (was "latest")
- Replace defaultValue+key remount pattern with controlled value+onValueChange on Tabs; extend Tabs/TabsTrigger/TabsContent props to support value, tabIndex, aria-hidden
- Add aria-hidden="true" tabIndex={-1} to Playground TabsTrigger and aria-hidden to TabsContent panel (hidden on mobile)
- getRegistryPackageVersion now throws instead of silently returning "latest" when version is undefined
916ea92 to
dbf081d
Compare
bntvllnt
left a comment
There was a problem hiding this comment.
Review — 1 finding (blocking, published as COMMENT)
BLOCKING
A11Y1 — Desktop Playground tab is visible but hidden from assistive tech- Evidence:
apps/registry/components/playground/preview-playground-tabs.tsx:54-58renders the Playground tab trigger witharia-hidden="true"andtabIndex={-1}whileclassName="hidden md:inline-flex"makes it visible on desktop. The matching panel at:76-80is also rendered witharia-hidden="true"whilemd:blockmakes it visible on desktop. - Why it matters: desktop keyboard and screen-reader users can see (or be told visually about) the new tab, but it is removed from the accessibility tree and skipped by tab navigation. If
#playgroundactivates the tab, the visible editor panel is also hidden from assistive technologies. - Fix: only hide the mobile-only duplicate path from the accessibility tree. The desktop
TabsTrigger/TabsContentshould not carry unconditionalaria-hidden, and the trigger should remain keyboard reachable when it is visible.
- Evidence:
WARN
- None.
VERIFIED CLEAN
- Re-fetched PR #349 immediately before publishing; current head is
dbf081d498863c1d330b277ac2f6cb6022adbd8donfeat/256-sandpack-playground. - Reviewed all 11 changed files: component page integration, dedicated playground route, playground shell/barrel, preview/playground tabs, Sandpack setup, playground utilities, registry package manifest, both Tabs implementations, and lockfile Sandpack entries.
- The previous Tailwind sandbox blocker is resolved in this head: the generated sandbox now imports
@vllnt/ui/tailwind-presetand scans local plus@vllnt/uipackage paths. - Viewed-state marked for all 11 changed files.
VALIDATION
- Ran:
pnpm install --frozen-lockfile— passed. - Ran:
pnpm -F @vllnt/ui lint— passed. - Ran:
pnpm -F @vllnt/ui exec tsc --noEmit --project tsconfig.build.json— passed. - Ran:
pnpm -F ui-registry exec tsc --noEmit— passed. - Ran:
pnpm -F @vllnt/ui build— passed. - Ran:
pnpm -F ui-registry build— passed; Next generated/components/[slug]/playgroundfor the registry components. - Ran: touched registry-file ESLint command — passed for changed lintable files, with one ignored generated registry default file warning.
- Gap:
pnpm -F ui-registry lintcrashes ineslint-plugin-jsx-a11yon untouchedapps/registry/app/report/report-bug-form.tsx(TypeError: (0 , _minimatch.default) is not a function), so the full registry lint gate is not authoritative for this PR head.
| <div className="flex items-center justify-between gap-4 border-b"> | ||
| <TabsList className="border-b-0"> | ||
| <TabsTrigger value="preview">Preview</TabsTrigger> | ||
| <TabsTrigger |
There was a problem hiding this comment.
Blocking: this tab is only hidden visually on mobile (hidden md:inline-flex), but aria-hidden="true" and tabIndex={-1} apply at every breakpoint. On desktop the Playground tab is visible while being removed from the accessibility tree and keyboard tab order; the matching panel below is also rendered with aria-hidden="true" while visible at md:block. Please keep the desktop tab/panel accessible and only hide the mobile alternate path from assistive tech.
Closes #256
Summary
/components/[slug]/playgroundroute generated for every registry component.@vllnt/ui, and track opens with Vercel Analytics.@vllnt/ui/tailwind-presetand scan local sandbox files plus@vllnt/uipackagedist/srcpaths for token and component-internal classes.Dependency tradeoff
@codesandbox/sandpack-reacttoapps/registryonly. This brings Sandpack and CodeMirror editor dependencies for the live-editing experience, but it is route/tab scoped and not part of the package library build.Fixes in this revision (HEAD
dbf081d498863c1d330b277ac2f6cb6022adbd8d)@vitejs/plugin-reactandvitechanged from"latest"to"^5.0.0"/"^7.3.2"in the Sandpack-generatedpackage.json.defaultValue={activeTab} key={activeTab}(full remount on hash change) with controlledvalue={activeTab} onValueChange={setActiveTab}; extendedTabsProps,TabsTriggerProps, andTabsContentPropsin@vllnt/uiand the registry copy to acceptvalue,tabIndex, andaria-hidden.TabsTrigger(hidden on mobile) now hasaria-hidden="true"andtabIndex={-1}; correspondingTabsContentpanel also hasaria-hidden="true".getRegistryPackageVersionnow throws instead of silently returning"latest"whenversionis undefined.Verification at HEAD
dbf081d498863c1d330b277ac2f6cb6022adbd8deslint components/playground/sandpack-playground.tsx components/playground/preview-playground-tabs.tsx lib/playground.ts— passedtsc --noEmit(registry) — passed (0 errors)pnpm -F @vllnt/ui build— passed (ESM + DTS)pnpm -F @vllnt/ui-registry build— passedvitest run src/components/tabs/tabs.test.tsx— passed (5 tests)pnpm -F @vllnt/ui lint src/components/tabs/— passedMerge note
67d3640. Lockfile conflict resolved with--theirs+pnpm install --no-frozen-lockfile.apps/registry/package.jsonandpnpm-lock.yaml; rebase may be needed if another registry dependency PR lands first.