UNPKG

@rogieking/figui3

Version:

A lightweight web components library for building Figma plugin and widget UIs with native look and feel

255 lines (195 loc) 11.9 kB
--- 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 ```json !`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 1. **Preserve native Web Components patterns.** Keep components framework-agnostic and rooted in custom elements. 2. **Prefer existing `fig-*` components over one-off markup.** Compose from current primitives before inventing new ones. 3. **Keep Figma UI3 visual consistency.** Use existing CSS variables and spacing/radius conventions. 4. **Honor interaction semantics.** Emit `input` while interacting and `change` on committed value changes. 5. **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: ```tsx import "@rogieking/figui3/fig.css"; const bootstrap = async () => { // Prevent production tree-shaking from dropping registration side effects. await import("@rogieking/figui3/fig.js"); createRoot(document.getElementById("app")!).render(<App />); }; bootstrap(); ``` ### Vite config guidance - Standard React Vite config is usually enough: ```ts 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 ```html <!-- Modal/dialog content container --> <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> <!-- Anchored popup surface --> <dialog is="fig-popup" anchor="#trigger" position="bottom left" offset="8 8"> <div>Popup content</div> </dialog> ``` ```js // Event contract pattern: continuous + committed updates. this.dispatchEvent(new CustomEvent("input", { detail, bubbles: true })); this.dispatchEvent(new CustomEvent("change", { detail, bubbles: true })); // Attribute-driven updates. static get observedAttributes() { return ["value", "disabled"]; } attributeChangedCallback(name, oldValue, newValue) { if (oldValue === newValue) return; // sync internal UI state } ``` ```txt 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 ``` ```html <!-- Typical field composition --> <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 1. **Read existing implementation first.** Check `fig.js`, `components.css`, and related demo usage before editing. 2. **Confirm API surface impact.** Identify affected attributes, events, and slots. 3. **Implement with compatibility in mind.** Preserve defaults and old usage unless explicitly changed. 4. **Update docs/demo in same pass.** Keep examples and behavior synchronized. 5. **Run project checks.** Use `bun dev` for interactive verification and `bun build` for output sanity. 6. **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 ```bash # Start docs/demo server bun dev # Build distributable files 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