Skip to content

Add @appica/ui-svelte (Svelte 5 port of @appica/ui-react) #6

Description

@triadmoko

Summary

Add @appica/ui-svelte as a sibling package to @appica/ui-react in this monorepo: same design tokens and public component set, implemented in Svelte 5 (runes) instead of React.

CONTRIBUTING.md already treats extra framework packages as expected (pnpm dev "as more packages land"), but the documented workflow, playground, and repo layout are still React-only. This issue is the discussion point before any implementation PR, per the contributing guide.

Motivation

  • The root AGENTS.md / monorepo layout already describe packages/svelte as the next integration after React.
  • Svelte 5 apps cannot consume @appica/ui-react without a React runtime. A first-party port keeps one visual language across frameworks.
  • Tokens, variants, and accessibility expectations should stay aligned with React so docs and Figma remain a single source of truth.

Proposed stack

Concern React today Svelte proposal
Framework React 19 Svelte ^5.33 (runes only)
Headless primitives Base UI (@base-ui/react) Native HTML in Wave 1; bits-ui in Wave 2
Motion Motion CSS + svelte/transition (no Motion)
Styling Tailwind v4 + CSS variables Same tokens (var(--color-…), var(--radius-…), var(--transition-…))
Tests vitest + Testing Library + vitest-axe Same, with @testing-library/svelte
Playground pnpm playgroundplaygrounds/react pnpm playground:svelteplaygrounds/svelte

Do not add bits-ui until a component needs a headless primitive. Polymorphism is href / el (no public child snippet, no React render / cloneElement).

Scope

Port every public @appica/ui-react export (components, hooks, providers, color lib). Track progress in packages/svelte/COMPONENT-STATUS.md.

Wave 1 (native HTML): presentational primitives such as Alert, Avatar, Badge, ButtonGroup, Card, Input, Textarea, Table, Tabs chrome that does not need a primitive, ThemeProvider, and related hooks.

Wave 2 (bits-ui or custom): overlays and form controls that React wraps with Base UI: Accordion, Dialog, Select, Combobox, DateField / DatePicker / TimeField / Calendar, Menu family, Slider, Toast, and so on. Color controls and Carousel have no bits-ui analog and stay custom (Embla for Carousel, same plugins as React).

Svelte-specific API notes to keep in the port (not 1:1 React copies):

  • $props() / $bindable() for fields; snippets (children, start, end) instead of slots.
  • createContext / setContext instead of cloneElement.
  • Overlay triggers take class={buttonVariants(...)} directly; links that look like a button use buttonVariants on <a>, not href on Button.
  • Date values stay @internationalized/date DateValue / TimeValue, not native Date.
  • src/index.ts and the package.json exports map are generated (pnpm --filter @appica/ui-svelte sync-exports).

Docs and contributing workflow

When the package lands, update the contributor-facing docs that today only mention React:

  • CONTRIBUTING.md: Node floor (repo engines is >=22), pnpm --filter @appica/ui-svelte …, pnpm playground:svelte, playgrounds/svelte/src/app.svelte as the Svelte scratch page, and a packages/svelte layout paragraph pointing at packages/svelte/AGENTS.md.
  • Root README.md: add @appica/ui-svelte to the package table once it is published.
  • Root scripts: pack:svelte (and release if Svelte should publish with the same flow).

Docs site work (appica.dev /ui) lives in the separate appica-dev repo and is out of scope here, other than packing the tarball (pnpm pack:svelte) so that site can consume it.

Acceptance criteria

  • packages/svelte publishes @appica/ui-svelte with the same component coverage as @appica/ui-react (checklist in COMPONENT-STATUS.md).
  • Co-located tests pass: pnpm --filter @appica/ui-svelte test.
  • pnpm --filter @appica/ui-svelte typecheck and build succeed; build emits .js + .d.ts (+ .svelte) for each public export.
  • Vite playground at playgrounds/svelte hot-reloads against library source (pnpm playground:svelte).
  • Theme via CSS variables only; shared tokens with React.
  • CONTRIBUTING.md and root README document the Svelte package the same way they document React.
  • Prop JSDoc follows the published API-reference rules (declared props only, @default when there is a default, American English, hyphen not a typographic dash).

Out of scope

  • Vue / HTML packages.
  • Changing the React public API to match Svelte.
  • appica.dev docs pages (separate repo), aside from making the packed tarball consumable.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions