Shared UI component library (@rstack-dev/doc-ui) for Rstack websites (rspack.rs, rsbuild.rs, rspress.rs).
- React 18 + TypeScript 5.9 (strict mode)
- Build: Rslib (based on Rsbuild)
- Styling: SCSS Modules (
.module.scss) - Animation: Framer Motion, Lottie
- UI: Ant Design 6
- Dev: Storybook 10
# Development
pnpm dev # Start Storybook (port 6006)
# Build
pnpm build # Build with Rslib
pnpm build:watch # Watch mode
# Lint (prefer file-scoped)
pnpm lint # Check all with Biome
pnpm lint:fix # Auto-fix
# Single file commands
npx tsc --noEmit 'path/to/file.tsx'
npx prettier --write 'path/to/file.tsx'
npx biome check --write 'path/to/file.tsx'No test framework - visual testing via Storybook.
src/
├── announcement/ # Announcement banner
├── antd/ # Ant Design wrappers
├── background-image/ # Background component
├── benchmark/ # Benchmark display
├── built-with-rspack/ # Showcase component
├── fully-featured/ # Feature list
├── hero/ # Hero section
├── nav-icon/ # Nav icon with popover
├── section-style/ # Section utilities
├── tool-stack/ # Tool stack display
├── why-rspack/ # Feature cards
├── env.d.ts # Type declarations
└── shared.tsx # Shared utilities
stories/ # Storybook stories
- Single quotes, trailing commas (
all), no parens for single arrow params
noExplicitAny: off,noArrayIndexKey: off- File naming:
camelCase,PascalCase, or export name
- Strict mode, use
typeimports:import type { FC } from 'react' - Path alias:
@/*→./src/*
- External packages (react, antd, framer-motion)
- Internal components (relative)
- Styles (
.module.scss) - Assets (JSON, images)
import type { FC } from 'react';
import { useState } from 'react';
import { useInView } from 'react-intersection-observer';
import { ProgressBar } from './ProgressBar';
import styles from './index.module.scss';- Components:
PascalCase(Hero,NavIcon) - Hooks:
useCamelCase(useMouseMove) - Props:
ComponentNameProps(HeroProps) - Styles:
index.module.scssper component
import type { FC } from 'react';
import styles from './index.module.scss';
export type MyComponentProps = {
title: string;
active?: boolean;
};
export const MyComponent: FC<MyComponentProps> = ({
title,
active = false,
}) => {
return <div className={styles.root}>{title}</div>;
};- SCSS Modules only (
.module.scss) - Compose:
${styles.btn} ${styles.btnPrimary} - Root class:
styles.root
- Functional components with hooks
- SCSS Modules for styling
- Export types with components
- Semantic HTML (
section,button) - Default values for optional props
memo()for expensive components
- Inline styles (except dynamic values)
- CSS-in-JS (emotion, styled-components)
- Hard-coded colors
- Class components
- Heavy deps without consideration
// stories/Hero.stories.tsx
import { Hero } from '@rstack-dev/doc-ui/hero';
import './index.scss';
export const HeroStory = () => <Hero onClickGetStarted={() => {}} />;
export default { title: 'Hero' };Pre-commit via simple-git-hooks + nano-staged:
- Biome lint on JS/TS
- Prettier format on all files
- Create
src/component-name/index.tsx - Add
src/component-name/index.module.scss - Add entry in
rslib.config.ts - Add export in
package.jsonexports - Create
stories/ComponentName.stories.tsx
Externalized (consumers provide): react, react-dom, framer-motion
Peer deps: antd, lottie-web, react-intersection-observer