3.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
@modernsk/ui — a tactile, dark-first React component library built on Radix primitives. Distributed as a git-installable / publishable package; only dist/ ships. Consumers get built ESM + CJS, .d.ts types, and a single styles.css.
Commands
npm run dev # Rsbuild playground at http://localhost:3000 (renders src/App.tsx — every component on one page)
npm run build # build publishable package: tsup (JS/types) + build:css (bundled stylesheet) into dist/
npm run lint # rslint (rslint.config.ts)
npm run format # prettier --write .
No test suite exists. npm run build runs automatically on install via the prepare script — consumers build the package themselves.
The build is two steps that must both run (the build script chains them):
tsupbundlessrc/index.ts→ ESM/CJS + types.react/react-domare externalized (peer deps);radix-ui+@phosphor-icons/reactare bundled.build:cssruns esbuild onsrc/styles/index.csswith--loader:.ttf=dataurl, inlining the self-hosted Anta font so no asset hosting is needed.
Architecture
Two parallel surfaces share the same source but never mix at publish time:
- Library (shipped):
src/index.tsis the public entry. It re-exports everything fromsrc/components/ui.tsx, plusThemeProvider/useThemefromsrc/components/theme.tsx, and exposesTooltipProvider(Radix'sTooltip.Provider). The shipped stylesheet entry issrc/styles/index.css. - Playground (dev-only, never published):
src/index.tsxmountssrc/App.tsx, and usessrc/styles/global.css. These are the Rsbuild dev target only.
src/styles/index.css (shipped) vs src/styles/global.css (dev) is a deliberate split: index.css imports only tokens.css + components.css and applies box-sizing at zero specificity via :where([class^='msk-']) so it never touches consumer elements. global.css adds a global reset and kitchen-sink layout helpers — those must stay out of the shipped bundle.
Components (src/components/ui.tsx)
All components live in one file. Pattern: Radix provides logic/accessibility, every visual comes from CSS. Components are thin wrappers that attach msk-* classes and spread props. The cx() helper joins class names (no classnames dependency). Components forward refs where they wrap a DOM element. There is no inline styling and no CSS-in-JS — all appearance is driven by msk-* classes resolving against CSS custom properties.
Styling system (src/styles/)
tokens.css— single source of truth: color/type CSS custom properties,@font-face, Google Fonts import. Every component reads from here.components.css— themsk-*class definitions.- Dark/light is driven by
data-themeon<html>, set byThemeProvider(persisted tolocalStorageunder keymsk-theme, defaultdark).
When adding or changing a component: add the wrapper in ui.tsx, define its msk-* styles in components.css, and pull any new color/spacing value from a token in tokens.css rather than hardcoding.
Consumer contract
Consumers import @modernsk/ui/styles.css once at app root, wrap their tree in ThemeProvider, and wrap any tooltip-using subtree in TooltipProvider. Keep these provider requirements intact when refactoring exports.
Conventions
- React 19, React Compiler is enabled (
babel-plugin-react-compilerin the Rsbuild dev pipeline). - TypeScript is
noEmit+verbatimModuleSyntax: useimport typefor type-only imports.noUnusedLocals/noUnusedParametersare on. - ESM-only package (
"type": "module").