This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
playhtml is a collaborative, interactive HTML library that allows elements to be magically transformed with simple data attributes. The project consists of three main packages in a monorepo structure:
- packages/playhtml: Core library that adds interactive capabilities to HTML elements
- packages/react: React wrapper components for playhtml functionality
- packages/common: Shared TypeScript types and interfaces
- partykit/: Real-time sync server using PartyKit and Yjs for collaborative state
- website/: Demo site showcasing playhtml capabilities and the home page for the library. Any test pages should go in here.
bun dev: Start the development server for the websitebun dev-server: Start the PartyKit development server for real-time syncbun build-site: Build the website for productionbun build-packages: Build all packages in the monorepo
- use bun install to install dependencies for all packages
Each package (playhtml, react, common) has its own build process:
cd packages/[package-name] && bun build: Build individual packagecd packages/react && bun run test: Run React package tests
cd packages/react && bun run test: Run tests for React componentscd packages/react && bun run test:watch: Run tests in watch mode
The playhtml library revolves around the concept of "capabilities" - interactive behaviors that can be added to HTML elements via data attributes:
-
Element Capabilities: Defined in
packages/playhtml/src/elements.tsandpackages/common/src/index.tscan-move: Draggable elements with 2D translationcan-spin: Rotatable elementscan-toggle: Toggle on/off state with CSS classescan-grow: Scalable elements with click/alt-clickcan-duplicate: Clone elements dynamicallycan-mirror: Sync all element changes automaticallycan-play: Fully customizable capability framework
-
State Management: Uses Yjs for real-time collaborative state sync
- Global shared state:
globalData(Y.Map) - Element handlers:
elementHandlers(Map of ElementHandler instances) - Awareness (user presence):
yprovider.awareness
- Global shared state:
-
Element Handler System: Each interactive element gets an
ElementHandlerinstance that manages:- Data persistence and sync
- Event handling (click, drag, mount)
- Element updates and awareness
- Reset shortcuts and debouncing
packages/playhtml/src/index.ts: Core initialization and setup logicpackages/playhtml/src/elements.ts: ElementHandler class and capability definitionspackages/common/src/index.ts: TypeScript interfaces and typespartykit/party.ts: Real-time server with Supabase persistencepackages/react/src/elements.tsx: React wrapper components
The React package provides declarative components that wrap the core playhtml functionality:
<PlayProvider>: Context provider for playhtml initialization<CanMove>,<CanSpin>,<CanToggle>etc.: Component wrappers for each capability- Custom hooks for accessing playhtml state and events
- Define the capability interface in
packages/common/src/index.ts(data types, TagType enum) - Implement the capability in
packages/playhtml/src/elements.ts(add to TagTypeToElement) - Create React wrapper component in
packages/react/src/elements.tsx - Add examples in
website/directory - Follow contributing guidelines in
CONTRIBUTING.md
Bun automatically handles workspace linking. When making changes across packages, they're immediately available without manual linking. Just run bun install at the root to set up workspace dependencies.
- Use the website/ directory to test new capabilities
- Run the React test suite for regression testing
- Test real-time sync with multiple browser windows
- Verify TypeScript compilation across all packages
- Elements must have unique
idattributes - Capabilities are detected via data attributes (e.g.,
can-move,can-toggle) - Custom elements use the
can-playattribute with JavaScript setup
- Use
setData()for persistent, synced state changes - Use
setLocalData()for temporary, local-only state - Use
setMyAwareness()for user presence/cursor data - For React, generally always try to use
withSharedStaterather than the internalCanPlayElement
- onClick, onDrag, onDragStart are the main interaction patterns
- Reset shortcuts use modifier keys (shift, ctrl, alt, meta)
- Custom event listeners can be added in
onMount
docs/: Public developer-facing and user-facing documentation only.internal-docs/: Internal planning and decision records (gitignored, not committed). Specs go ininternal-docs/specs/, plans go ininternal-docs/plans/. Date-prefix files (e.g.,2026-03-13-feature-name.md).
- The project uses Bun as the package manager and task runner
- Vite is used for bundling with TypeScript support
- Real-time sync is powered by PartyKit + Yjs with Supabase persistence
- CSS styles are in
packages/playhtml/src/style.scss - The website uses a multi-page app (MPA) structure with glob-based HTML discovery
- Workspace dependencies are automatically linked by Bun
- Examples and demos are critical for showcasing capabilities
- When making any changes to the core packages (playhtml, common, react), you should also make sure to add a changeset in .changeset/