@obra-studio/figui3-svelte
v1.0.2
Published
Figma UI3 design system components, authored in Svelte 5.
Maintainers
Readme
@obra-studio/figui3-svelte
Figma UI3 design system components, authored in native Svelte 5.
This is a from-scratch Svelte 5 rewrite of FigUI3 (a vanilla Web Components library). It is a deliberate breaking change from the upstream project: there is no Custom Element output here, and this package is not a drop-in replacement for
@rogieking/figui3. A subset of the full component set is ported so far — see below.
Install
npm install @obra-studio/figui3-svelteRequires Svelte 5.56+.
Usage
<script lang="ts">
import { Button, TextInput } from '@obra-studio/figui3-svelte';
import '@obra-studio/figui3-svelte/styles.css';
let name = $state('');
</script>
<TextInput bind:value={name} placeholder="Your name" />
<Button onclick={() => alert(`Hi, ${name}`)}>Say hi</Button>Import @obra-studio/figui3-svelte/styles.css once (e.g. in your root layout) for design tokens and base element styles. Individual pieces are also available as @obra-studio/figui3-svelte/styles/tokens.css and @obra-studio/figui3-svelte/styles/base.css if you want to compose them yourself.
Components ported so far
31 components. Those marked (unofficial) have no counterpart in the original library — they are additions, not ports:
Core — Button, Checkbox, Radio, Switch, Chip, Icon, Spinner, Banner Inputs — TextInput, NumberInput, Slider, Dropdown, Listbox, Field Layout — Header, Footer, Content, Tabs, SegmentedControl, Group (unofficial) Overlays — Popup, Menu, Tooltip Color — ColorInput (unofficial), FillInput (unofficial), FillPicker (unofficial) Spatial — Joystick (unofficial), OriginGrid (unofficial), ThreeDRotate (unofficial), EasingCurve (unofficial), Handle (unofficial)
Still missing from the original library: Dialog, Media, Layer, Toast and others.
Why this exists
The original figui3 is a well-designed, native-look Figma plugin/widget UI kit — the API surface and visual design are unchanged here. This fork exists purely to replace its hand-rolled Custom Element internals with idiomatic Svelte 5 (runes, snippets, attachments) for a better authoring and consumption experience in Svelte-based plugins. Credit and design origin: FigUI3 by Rogie King.
Repo layout
This package is a workspace in the figui3-svelte repo, and it is two things at once — the standard SvelteKit library layout:
| Path | What it is |
| --- | --- |
| src/lib/ | The published library. svelte-package compiles this to dist/, and dist is the only thing in the npm tarball. |
| src/routes/ + src/playground/ | The playground, which is the site at figui3.obra.studio. Prerendered to static HTML by adapter-static. |
Nothing under src/routes/ or src/playground/ ships to npm.
Development
bun install # from the repo root — the workspace uses bun
npm run dev # playground at localhost:5173, hot-reloading the library
npm run check # svelte-check; must be 0 errors before releasingReleasing to npm
The version, the git tag, and the published package are kept in lockstep. Run these in order:
# 1. bump the version in package.json (e.g. 1.0.0 -> 1.1.0), then:
git commit -am "Release 1.1.0"
git tag -a v1.1.0 -m 1.1.0
git push origin main --follow-tags
# 2. publish
npm run releasenpm run release refuses to publish unless all of these hold — you don't
have to remember them, the guards do:
| Guard | Blocks a release when |
| --- | --- |
| guard:clean | the working tree is dirty, so a published version always matches a commit |
| guard:pushed | there are unpushed commits, so the tag people fetch actually exists |
| guard:tagged | there is no v<version> tag for the version being published |
| guard:unpublished | that exact version is already on npm (npm would reject it anyway, later and more confusingly) |
It then runs check and a full build before the tarball is made.
Prereleases: a version like
1.1.0-beta.1needs an explicit dist-tag, or npm refuses it:npm run release -- --tag beta. Never publish a prerelease aslatest— that is howlatestended up pointing at an old alpha before 1.0.0.
2FA: publishing prompts for a one-time password. Pass it directly if the prompt is awkward:
npm run release -- --otp=123456.
Deploying the site
figui3.obra.studio is a Cloudflare Pages project (figui3-svelte) with no
Git integration — deploys are explicit, from a machine with wrangler logged in:
npm run deployThat rebuilds the site from source and uploads it. predeploy runs
guard:clean, guard:pushed and check first, so what is live always
corresponds to a pushed commit rather than to whatever happened to be in
build/ locally.
Releasing to npm and deploying the site are independent: a library fix needs
npm run release, a playground change needs npm run deploy, and a change to
both needs both.
The @obra-studio scope gotcha
If a global ~/.npmrc maps the @obra-studio scope to another registry (e.g.
GitHub Packages), it wins over --registry on the command line, and installs
404 while publishes silently aim at the wrong host. The scope-specific override
is the only reliable form:
--@obra-studio:registry=https://registry.npmjs.orgnpm run release and guard:unpublished already pass it. Consumers should pin
the scope in their own project .npmrc.
License
MIT
