| name | figui3 |
| description | Guides development and maintenance of the FigUI3 web components library for Figma-style plugin UIs. Applies when adding or modifying `fig-*` custom elements, updating docs/demo pages, adjusting theme tokens, improving accessibility, or debugging component behavior in `fig.js`, `components.css`, `index.html`, and `README.md`. |
| user-invocable | false |
FigUI3
A lightweight web components library for Figma UI3-style plugin and widget interfaces.
IMPORTANT: Prefer the project's native scripts and structure. Use bun dev for local docs/demo work and bun build for production output.
Current Project Context
!`node -e "const p=require('./package.json'); console.log(JSON.stringify({name:p.name,version:p.version,scripts:p.scripts,exports:p.exports},null,2))" 2>/dev/null || echo '{"error":"package.json not found"}'`
The JSON above is the source of truth for package name, build commands, and exported files.
Principles
- Preserve native Web Components patterns. Keep components framework-agnostic and rooted in custom elements.
- Prefer existing
fig-* components over one-off markup. Compose from current primitives before inventing new ones.
- Keep Figma UI3 visual consistency. Use existing CSS variables and spacing/radius conventions.
- Honor interaction semantics. Emit
input while interacting and change on committed value changes.
- Treat accessibility as required behavior. Preserve labels, keyboard support, ARIA attributes, and disabled states.
React + Vite Integration
Install and bootstrap in React
- Install package:
npm i @rogieking/figui3 (or pnpm add / bun add).
- Import CSS once in app entry (
main.tsx / main.jsx): import "@rogieking/figui3/fig.css";
- Register custom elements before first render. In Vite/React, prefer an explicit bootstrap:
import "@rogieking/figui3/fig.css";
const bootstrap = async () => {
await import("@rogieking/figui3/fig.js");
createRoot(document.getElementById("app")!).render(<App />);
};
bootstrap();
Vite config guidance
- Standard React Vite config is usually enough:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
});
- Keep FigUI3 registration import at the top-level app bootstrap (not inside leaf components).
- If a production build appears to tree-shake element registration, use the explicit dynamic import pattern above.
React usage rules for web components
- Use DOM attrs on custom elements (
<fig-slider text="true" />) and read values from e.target / e.detail.
- In React, use
class (not className) for all FigUI3 web components (fig-* and <dialog is="fig-...">) to keep attribute behavior consistent.
- Prefer refs +
addEventListener when wiring complex input/change behavior.
React + color picker modes (fig-input-color / fig-fill-picker)
fig-fill-picker is optional. Import fig-editor.js and fig-editor.css when full picker behavior is needed.
- Do not use
picker or picker-anchor on fig-input-color; components auto-detect fig-fill-picker at interaction time.
picker-* attrs on fig-input-color are forwarded to fig-fill-picker only when the optional picker is registered.
- Example:
picker-dialog-position, picker-experimental, etc.
- For React custom modes, use
fig-fill-picker + slot API:
- Add a child with
slot="mode-<name>" (and optional label).
- Include
<name> in the mode attribute (e.g. mode="solid,react-demo").
- Listen for
modeready and render into e.detail.container.
- Do not reparent React-owned DOM into the picker after render; use the provided
modeready container as mount target.
- Keep React lifecycle cleanup explicit for custom mode mounts:
- keep one
root per mode container
- call
root.unmount() when the host component unmounts
- remove
modeready listeners in cleanup to avoid duplicate mounts
- Custom mode content must dispatch
input / change with detail payload so picker can store mode data and propagate events.
- Preserve value shape expectations:
fig-input-color expects solid color data (detail.color, optional detail.alpha) from the picker.
fig-fill-picker custom modes use JSON with type set to mode name and remaining data in payload.
Experimental Attribute Guidance
- Use
experimental as a feature-flag string for opt-in behavior. Treat it as progressive enhancement, not guaranteed baseline behavior.
- Prefer
experimental="modern" when enabling modern customizable select/picker UI behavior.
- Keep usage explicit on the component that needs it (for example
fig-dropdown, fig-fill-picker, fig-input-fill, fig-input-color).
- Preserve pass-through behavior:
fig-input-color and fig-input-fill forward experimental-related picker settings into internal fig-fill-picker usage.
- Avoid adding hidden implicit defaults that enable experimental behavior globally.
- Backward-compat rule:
- Do not reintroduce the old
neue variant name for dropdown experimental behavior.
- Use
variant="classic" only when a slider needs the previous visual style.
- Documentation rule: any new experimental token must be documented with activation syntax, intended scope, and fallback behavior in demos + README + changelog.
Critical Rules
Overlay Components (fig-dialog, fig-popup)
- Choose the overlay primitive intentionally:
<dialog is="fig-dialog"> for modal/light-dismiss dialog workflows.
<dialog is="fig-popup"> for anchored floating surfaces (menus, contextual panels, nested popups).
- Keep overlay semantics stable:
fig-dialog should remain dialog-first (title/header/footer patterns, modal semantics).
fig-popup should remain anchor/position-first (offset, collision handling, viewport margins).
- Preserve drag and positioning behavior on both
fig-dialog and fig-popup; do not regress manual placement rules.
- For popup chains, maintain containment and dismissal logic across descendant popups.
- Document any overlay behavior change in demos and changelog with a concrete before/after note.
Component Architecture
- Extend
HTMLElement and implement lifecycle cleanup in disconnectedCallback.
- Use
observedAttributes + attributeChangedCallback for attribute-driven reactivity.
- Keep attribute names and behavior backward-compatible unless explicitly doing a breaking change.
- Support
disabled behavior wherever interaction is possible.
- Avoid introducing framework-specific assumptions in component internals.
Events and Data Contracts
- Emit standard
input and change events for form-like controls.
- Put rich payloads in
event.detail when needed; keep names stable.
- Do not silently change event payload shape for existing components.
- When adding new events, document trigger timing and payload fields.
Styling and Theming
- Reuse established design tokens and CSS variables before adding new ones.
- Keep light/dark compatibility working with
color-scheme and current token strategy.
- Avoid ad-hoc hardcoded colors when semantic tokens already exist.
- Preserve current sizing, spacing, and radius rhythm unless intentionally refactoring system-wide.
Documentation and Demos
- Update
README.md component docs when public API or behavior changes.
- Update demo surfaces (
index.html and playground/ routes where relevant) for visible behavior changes.
- Prefer realistic examples that mirror plugin/property panel usage.
- If introducing an experimental feature, document activation and fallback behavior clearly.
Compatibility and Safety
- Keep browser support expectations aligned with current README claims.
- Use progressive enhancement for bleeding-edge CSS features.
- Avoid regressions in existing attributes, defaults, and emitted events.
Color Picker Mode Extensibility
- Treat custom modes as a
fig-fill-picker concern, not a standalone fig-input-color concern.
- When adding a new mode, update demos/docs with both:
- vanilla slot usage (
slot="mode-*")
- React
modeready usage
- Do not emit
input from programmatic attribute writes (value updates); preserve current loop-avoidance behavior for React.
Key Patterns
<dialog is="fig-dialog" drag="true" handle="fig-header">
<fig-header>
Dialog Title
<fig-button variant="ghost" icon close-dialog aria-label="Close dialog">
<fig-icon name="close"></fig-icon>
</fig-button>
</fig-header>
<div>Dialog body</div>
</dialog>
<dialog is="fig-popup" anchor="#trigger" position="bottom left" offset="8 8">
<div>Popup content</div>
</dialog>
this.dispatchEvent(new CustomEvent("input", { detail, bubbles: true }));
this.dispatchEvent(new CustomEvent("change", { detail, bubbles: true }));
static get observedAttributes() { return ["value", "disabled"]; }
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) return;
}
Event contract quick map:
- fig-slider: input/change -> current value on e.target.value
- fig-input-color: input/change -> value on e.target.value, structured color via e.detail (when available)
- fig-input-fill / fig-fill-picker: input/change -> fill payload in e.detail
<fig-field direction="horizontal">
<label>Opacity</label>
<fig-slider value="75" min="0" max="100" text="true" units="%"></fig-slider>
</fig-field>
fig-popup vs fig-dialog
- Use
fig-dialog when the UI is a dialog.
- Best for modal or primary task flows.
- Works well with explicit dialog structure and close policies.
- Use
fig-popup when you need low-level floating control.
- Best for anchored contextual surfaces and advanced positioning behavior.
- Prefer this when you need explicit anchor/position/offset/viewport tuning.
Rule of thumb: fig-dialog = dialog UX, fig-popup = popup primitive.
Workflow
- Read existing implementation first. Check
fig.js, components.css, and related demo usage before editing.
- Confirm API surface impact. Identify affected attributes, events, and slots.
- Implement with compatibility in mind. Preserve defaults and old usage unless explicitly changed.
- Update docs/demo in same pass. Keep examples and behavior synchronized.
- Run project checks. Use
bun dev for interactive verification and bun build for output sanity.
- Verify accessibility and theming. Check keyboard flow, labels, disabled states, and both light/dark appearance.
Release-Ready Checklist
- Validate in a production build (
bun build) and confirm custom elements are registered at runtime.
- Verify
input vs change behavior for touched controls in both vanilla usage and React integration.
- Verify light/dark themes and keyboard navigation for any changed component.
- Verify overlay behavior (
fig-dialog, fig-popup) including close/dismiss and drag behavior when applicable.
- Update
README.md, demos, and CHANGELOG.md for any public API or behavior change.
Quick Reference
bun dev
bun build
Primary Files
fig.js - component implementations and behavior
components.css - component-level styling and states
base.css - foundational styles and variables
index.html - main interactive docs/demo
README.md - public API and usage documentation
CHANGELOG.md - release history and migration notes