diff --git a/packages/twenty-ui/README.md b/packages/twenty-ui/README.md index c21b3a3cd2..2eac78b28d 100644 --- a/packages/twenty-ui/README.md +++ b/packages/twenty-ui/README.md @@ -1,385 +1,75 @@ # twenty-ui -> **Status:** Cut-over complete (June 2026). All 192 components and all 70 stories were -> migrated from the legacy library with full public-API parity (export-identifier diff: -> 0 missing / 0 extra across all 13 subpath modules), `twenty-front` was switched over to -> `twenty-ui`, and the legacy `twenty-ui-deprecated` package was removed. Gates green: -> typecheck, lint, jest, build, storybook:build, -> storybook:test (225 stories incl. play functions + live axe gate), size. -> Remaining: the Argos visual-parity triage, the a11y fix pass (119 stories carry -> inherited-violation `a11y: 'todo'` overrides), the CI diff-table workflow, the -> `modules/ui` triage (Phase 4), and the publish pipeline (Phase 5). -> The sections below remain the design document for the full effort; see -> [Migration outcomes](#migration-outcomes-june-2026) for decisions taken during the port. +Twenty's open-source React UI component library: components, icons, and design tokens built on a zero-runtime, CSS-variable styling layer. -`twenty-ui` is Twenty's UI library, the successor to the legacy `twenty-ui-deprecated` package -(now removed). It is built on a headless component library and a zero-runtime, CSS-variable styling layer. +# Installation -## Goals - -1. Publish as a standalone, versioned **npm package**. -2. Replace `twenty-ui` in `twenty-front` with **no visual change** (same design, token for token). -3. **Migrate every component** currently exported by `twenty-ui-deprecated`. *(Done — June 2026.)* -4. **Absorb the generic, reusable UI** currently living in `twenty-front/src/modules/ui` (dropdowns, modals, tab lists, side panels, navigation, field inputs/displays, etc.), decoupling it from application concerns so it ships from the library. -5. Enforce a **quality bar in CI**: bundle size, render/load time, and accessibility, measured against the old library. - -## Current state (`twenty-ui-deprecated`) - -| Aspect | Today | -| --- | --- | -| Exports | ~180 components across 13 subpath entry points (`display`, `input`, `layout`, `navigation`, `feedback`, `components`, `theme`, `theme-constants`, `utilities`, `accessibility`, `assets`, `json-visualizer`, `testing`) + 3 CSS files | -| Styling | Linaria (`@linaria/react`) compiled via `@wyw-in-js/vite`; theming via generated `--t-*` CSS variables | -| Behavior | Hand-rolled (modals, menus, tooltips, selects, etc.); `react-tooltip` for tooltips | -| Build | Vite library mode, dual ESM/CJS, `vite-plugin-dts`, auto-generated barrels | -| Icons | `@tabler/icons-react` re-exports + custom icons + Jotai-backed `IconsProvider` | -| Consumption | ~1,730 files in `twenty-front` import it (mostly `display` and `theme-constants`), plus `twenty-front-component-renderer` and `twenty-sdk`; imported by package name | -| Published | No (`private: true`) | - -`twenty-front/src/modules/ui/` (application-level UI) consumes `twenty-ui-deprecated` today. Its **generic, reusable** -components are now **in scope** — they migrate into `twenty-ui` (see [Application-level UI migration](#application-level-ui-migration-twenty-frontsrcmodulesui)). - -## Decision 1 — Headless library: Base UI - -Adopt **Base UI** ([`mui/base-ui`](https://github.com/mui/base-ui), published to npm as -[`@base-ui/react`](https://base-ui.com), MIT) as the behavioral foundation; build Twenty's visual -design on top of it. - -| | Base UI | shadcn/ui | Radix | -| --- | --- | --- | --- | -| Distribution | npm package | copy-paste source | npm package | -| Styling | bring your own | Tailwind | bring your own | -| State styling | `data-*` + `className`-as-function | (underlying primitive) | `data-*` | -| Maintenance | MUI team, full-time; frequent stable releases | community | WorkOS; slower cadence, creators departed | - -**Rationale** - -- **Publishable and unstyled** — Base UI ships as an npm dependency and imposes no styling, so consumers apply their own tokens. shadcn is copy-paste source (not installable) and Tailwind-coupled; it is suitable only as a scaffolding/reference tool, not the foundation. -- **Active long-term investment** — Base UI is maintained by the team behind Radix, Floating UI, and Material UI. Radix is a viable, still-maintained fallback, but its core authors now work on Base UI and its stable-release cadence has slowed. -- **Modern, broad primitives** — Combobox/Autocomplete with built-in search, Select, Number Field, Navigation Menu, Toast, etc., several of which replace hand-rolled or single-purpose dependencies in `twenty-ui`. -- **`className`-as-function-of-state** pairs cleanly with CSS/SCSS Modules. -- **Small, tree-shakeable dependency tree**; peer-compatible with the repo's React 18. - -Pin the latest **stable** release at implementation time; isolate Base UI behind the package's own -component APIs so upgrades stay localized. - -## Decision 2 — Styling: SCSS Modules (drop Linaria) - -Use **SCSS Modules** (`*.module.scss`) over the existing CSS-variable theme. Drop Linaria. - -| Approach | Runtime | Build complexity | Scoping | Verdict | -| --- | --- | --- | --- | --- | -| Linaria (today) | zero | high (Babel + wyw-in-js) | auto | overkill | -| Plain global CSS | zero | none | none (collision risk) | unsafe for a library | -| CSS Modules | zero | none (native Vite) | auto | strong baseline | -| **SCSS Modules** | zero | low (`sass` only) | auto | **recommended** | -| vanilla-extract | zero | medium (TS compile) | auto | viable alternative (typed tokens) | - -**Rationale** - -- Theming is already CSS variables and component state comes from Base UI as `data-*` attributes, so the two features Linaria provides (JS theming and prop interpolation) are not needed. -- SCSS Modules are zero-runtime and auto-scoped, native to Vite (no Babel/`wyw-in-js`), and faster to build. -- Sass mixins, maps, and `@each` cover variant/size generation and responsive breakpoints. -- Type-safe class names via generated `*.module.scss.d.ts` (`vite-plugin-sass-dts`). - -**Conventions:** one `Component.module.scss` per component; tokens only via `var(--t-*)`; state via -`data-*` selectors; multi-variant composition via `clsx` (or `cva` for a typed variants API); -shared `mixins.scss` / `breakpoints.scss`; global unscoped CSS only for theme variables, reset, and keyframes. - -## Architecture - -``` -packages/twenty-ui/ -├── package.json # public exports mirror twenty-ui's subpath map -├── project.json # Nx targets: build, lint, test, storybook, size -├── vite.config.ts # library mode, no wyw-in-js -├── vitest.config.ts # storybook component tests -├── .storybook/ -├── .size-limit.json # per-entry bundle budgets -├── scripts/ # generateBarrels.ts -└── src/ - ├── styles/ # global: reset, theme vars, mixins, breakpoints - ├── theme/ theme-constants/ - ├── display/ input/ layout/ navigation/ feedback/ components/ - ├── accessibility/ utilities/ json-visualizer/ assets/ testing/ +```bash +npm install twenty-ui ``` -**Public API parity.** Keep the same subpath exports, component names, and prop signatures as -`twenty-ui-deprecated` so the final swap is a codemod + dependency rename, not a rewrite of ~1,730 files. -Keep auto-generated barrels and dual ESM/CJS + `dts` output. *(Achieved: the export-identifier sets of -all 13 module barrels are identical between the two packages.)* +`react`, `react-dom`, and `monaco-editor` are peer dependencies (install them in your app). `monaco-editor` is only required if you use the code editor components. -**Internal changes vs `twenty-ui-deprecated`:** Linaria → SCSS Modules; hand-rolled behavior + `react-tooltip` -→ Base UI; prefer Base UI/CSS transitions over `framer-motion` where possible; keep the icon system -as-is. The internal path alias is `@ui/*` (same convention as the deprecated package, so ported files -diff cleanly against their sources). +# Usage -## Theming +Import the base styles once, pick a theme stylesheet, and wrap your app in `ThemeProvider`: -`theme-constants` has ~943 importers and must be a drop-in replacement. +```tsx +import { ThemeProvider } from 'twenty-ui/theme-constants'; +import { Button } from 'twenty-ui/input'; -- Keep the public API identical: `ThemeProvider`, `ThemeContext`, `useTheme`, the `themeCssVariables` shape, `ThemeType`, color helpers, and the `theme-light.css` / `theme-dark.css` exports. -- Reuse `twenty-ui-deprecated`'s token values verbatim to guarantee identical design. -- Tokens live in `src/theme/` (`THEME_LIGHT` / `THEME_DARK`); the `--t-*` CSS variables and the `themeCssVariables` accessor are static files mirrored token-for-token from `twenty-ui-deprecated` (matching its own static-CSS approach). -- A theme parity test asserts the theme CSS and `themeCssVariables` stay identical to `twenty-ui-deprecated`'s `--t-*` values. +import 'twenty-ui/style.css'; +import 'twenty-ui/theme-light.css'; -### Consuming the theme +export const App = () => ( + +