diff --git a/frontend/packages/ui/.babelrc b/frontend/packages/ui/.babelrc deleted file mode 100644 index ab1c311509..0000000000 --- a/frontend/packages/ui/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": [], - "plugins": [] -} diff --git a/frontend/packages/ui/AGENTS.md b/frontend/packages/ui/AGENTS.md new file mode 100644 index 0000000000..b019c47ec4 --- /dev/null +++ b/frontend/packages/ui/AGENTS.md @@ -0,0 +1,266 @@ +# @penpot/ui – Agent Instructions + +TypeScript + React component library that forms the Penpot design system (DS). +Components are built in TypeScript/TSX, styled with CSS Modules (SCSS), tested +with Vitest + Testing Library, and documented with Storybook. + +This package lives under `frontend/packages/ui/` and is published as the +`@penpot/ui` internal package consumed by the main `frontend/` ClojureScript +application. + +## Architecture + +``` +frontend/packages/ui/ +├── src/ +│ ├── index.ts # Barrel – all public exports +│ └── lib/ +│ ├── _ds/ # Shared SCSS foundations (mixins, tokens) +│ │ ├── _borders.scss # Border-radius and border-width tokens +│ │ ├── _sizes.scss # Size tokens ($sz-*) +│ │ ├── _utils.scss # px2rem() helper +│ │ └── typography.scss # use-typography() mixin + font styles +│ ├── buttons/ # Button components +│ │ ├── _buttons.scss # Shared button placeholder/variant styles +│ │ ├── Button.tsx +│ │ ├── Button.module.scss +│ │ ├── Button.stories.tsx +│ │ └── Button.spec.tsx +│ ├── example/ # Example component (reference) +│ ├── foundations/ +│ │ ├── assets/ # Icon component +│ │ │ ├── Icon.tsx +│ │ │ ├── Icon.module.scss +│ │ │ ├── Icon.stories.tsx +│ │ │ └── Icon.spec.tsx +│ │ └── typography/ # Text, Heading components + shared utilities +│ └── product/ # Product-level components (e.g. Cta) +├── eslint.config.mjs # ESLint 9 flat config (TypeScript + React) +├── stylelint.config.mjs # Stylelint config (mirrors frontend/) +├── vite.config.mts # Vite lib build + Vitest config +├── tsconfig.json +├── tsconfig.lib.json +├── tsconfig.spec.json +└── tsconfig.storybook.json +``` + +Components are organised to mirror the CLJS source tree +`frontend/src/app/main/ui/ds/`: + +| CLJS path | TS path | +|-----------|---------| +| `ds/foundations/typography/text.cljs` | `src/lib/foundations/typography/Text.tsx` | +| `ds/foundations/typography/heading.cljs` | `src/lib/foundations/typography/Heading.tsx` | +| `ds/foundations/assets/icon.cljs` | `src/lib/foundations/assets/Icon.tsx` | +| `ds/product/cta.cljs` | `src/lib/product/Cta.tsx` | +| `ds/buttons/button.cljs` | `src/lib/buttons/Button.tsx` | + +### Known Tooling Notes + +- **No `.babelrc` in this package.** The `react-docgen` plugin used by + Storybook calls `@babel/core`'s `loadPartialConfig`. If a `.babelrc` is + present with empty `presets: []` it disables the default `typescript` Babel + plugin, causing `import type` to fail in story files. Keep the `.babelrc` + deleted. +- **`@vitejs/plugin-react` v6** removed the `babel` option. Use + `reactCompilerPreset()` from the same package instead of passing + `babel: { plugins: ['babel-plugin-react-compiler'] }`. + +Every migrated component must have: +- `ComponentName.tsx` – the React component +- `ComponentName.module.scss` – CSS Module styles +- `ComponentName.stories.tsx` – Storybook stories +- `ComponentName.spec.tsx` – Vitest unit tests + +## Development Commands + +All commands must be run from `frontend/packages/ui/`. + +```bash +# Build the library (outputs to dist/) +pnpm run build + +# Watch mode (rebuilds on file changes) +pnpm run watch + +# Type-check all tsconfig projects +pnpm run typecheck + +# Run unit tests (Vitest, single pass) +pnpm run test + +# Run tests in watch mode +pnpm run test:watch + +# Launch Storybook dev server +pnpm run storybook + +# Build Storybook static site +pnpm run build:storybook + +# Lint TypeScript/TSX (ESLint) +pnpm run lint:ts + +# Lint SCSS (Stylelint) +pnpm run lint:scss + +# Lint all (TS/TSX + SCSS) +pnpm run lint + +# Auto-fix formatting – TS/TSX only +pnpm run fmt:ts + +# Auto-fix formatting – SCSS only +pnpm run fmt:scss + +# Auto-fix formatting – all (TS/TSX + SCSS) +pnpm run fmt + +# Check formatting – TS/TSX only +pnpm run check-fmt:ts + +# Check formatting – SCSS only +pnpm run check-fmt:scss + +# Check formatting – all (TS/TSX + SCSS) +pnpm run check-fmt +``` + +Always run the following checks after making changes and before committing: + +```bash +pnpm run lint +pnpm run check-fmt +pnpm run typecheck +pnpm run test +``` + +## Component Conventions + +### Naming + +- PascalCase filenames: `MyComponent.tsx`, `MyComponent.module.scss` +- Named exports only — no default exports for components +- Export from `src/index.ts` (both the component and its props type) + +### Component Structure + +```tsx +import { type ComponentPropsWithRef, memo } from "react"; +import clsx from "clsx"; +import styles from "./MyComponent.module.scss"; + +export interface MyComponentProps extends ComponentPropsWithRef<"div"> { + /** Required prop description */ + label: string; +} + +function MyComponentInner({ label, className, children, ...rest }: MyComponentProps) { + return ( +