关于 mantine-react-component-dev
mantine-react-component-dev 是用于开发和维护符合 Mantine 规范的 React 组件的技能,涵盖组件模式、样式、无障碍、测试和文档,并遵循所述 monorepo 结构。
主要功能
- 01Mantine 多态工厂模式
- 02Styles API 与 CSS 变量
- 03上下文和复合组件模式
- 04受控与非受控状态指南
- 05无障碍和键盘导航
- 06测试与文档规范
适用场景
- 01构建 Mantine 风格的 React 组件
- 02通过 Styles API 添加样式定制
- 03实现共享状态和复合组件
- 04编写组件测试、演示和文档
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gfazioli/ mantine-select-stepper --skill mantine-react-component-dev安装后在 Claude Code 中输入/mantine-react-component-dev确认已加载。
详细介绍
Mantine React 组件开发技能
使用此技能的时机
在以下情况下应用此技能:
- 组件开发:基于 Mantine 的工厂模式创建或编辑 React 组件
- TypeScript 模式:实现多态组件、类型安全的属性和 CSS 变量
- Styles API:通过 Mantine 的 Styles API 系统(选择器、变量、修饰符)配置组件样式
- 组件组合:构建带有静态子组件的复合组件(例如,
Component.Target) - 上下文管理:实现安全的上下文模式以共享组件状态
- 无障碍性:确保符合 ARIA 规范、支持键盘导航并管理焦点
- 代码审查:确保符合既定模式和约定
- 文档:编写组件演示、MDX 文档和 API 参考
项目结构
组件采用单体仓库工作区结构组织:
-
/package/src/:组件主要源代码ComponentName.tsx:组件主要实现ComponentName.module.css:组件作用域样式ComponentName.context.ts:上下文提供程序(如有需要)ComponentName.errors.ts:错误消息ComponentName.test.tsx:Jest + Testing Library 测试ComponentName.story.tsx:Storybook 故事SubComponent/SubComponent.tsx:位于独立文件夹中的子组件index.ts:公共导出
-
/docs/:Next.js 文档站点demos/ComponentName.demo.*.tsx:交互式演示styles-api/ComponentName.styles-api.ts:Styles API 元数据docs.mdx:主要文档页面
请参阅 ./package/src/ 中的现有组件,了解实现示例。
TypeScript 模式
组件工厂模式
所有组件都使用 Mantine 的多态工厂模式。
import { polymorphicFactory, PolymorphicFactory, useProps, useStyles, createVarsResolver } from '@mantine/core';
export type ComponentStylesNames = 'root' | 'element';
export type ComponentCssVariables = {
root: '--custom-var' | '--another-var';
};
export interface ComponentProps extends BoxProps, StylesApiProps<ComponentFactory> {
/** Prop description */
customProp?: string;
}
export type ComponentFactory = PolymorphicFactory<{
props: ComponentProps;
defaultComponent: 'div';
defaultRef: HTMLDivElement;
stylesNames: ComponentStylesNames;
vars: ComponentCssVariables;
staticComponents: {
SubComponent: typeof SubComponent;
};
}>;
const defaultProps: Partial<ComponentProps> = {
customProp: 'default',
};
const varsResolver = createVarsResolver<ComponentFactory>((_, { customProp }) => ({
root: {
'--custom-var': customProp,
},
}));
export const Component = polymorphicFactory<ComponentFactory>((_props, ref) => {
const props = useProps('Component', defaultProps, _props);
const { classNames, className, style, styles, unstyled, vars, customProp, ...others } = props;
const getStyles = useStyles<ComponentFactory>({
name: 'Component',
classes,
props,
className,
style,
classNames,
styles,
unstyled,
vars,
varsResolver,
});
return (
<Box ref={ref} {...getStyles('root')} {...others}>
{/* component content */}
</Box>
);
});
Component.displayName = '@your-scope/Component';
Component.SubComponent = SubComponent;
主要要求:
- 对不受约束的泛型使用
unknown;通过类型守卫缩小类型范围 - 避免使用
any;子元素使用React.ReactNode - 为样式名称和 CSS 变量定义显式类型别名
- 使用
usePropsHook 合并默认属性 - 实现
varsResolver以处理 CSS 自定义属性
上下文模式
对于需要共享状态的组件,请使用 Mantine 的安全上下文模式。
import { createSafeContext } from '@mantine/core';
import { COMPONENT_ERRORS } from './Component.errors';
interface ComponentContext {
state: boolean;
setState: (value: boolean) => void;
}
export const [ComponentContextProvider, useComponentContext] = createSafeContext<ComponentContext>(
COMPONENT_ERRORS.context
);
Component.errors.ts 中的错误定义:
export const COMPONENT_ERRORS = {
context: 'Component was not found in the tree',
validation: 'Specific validation message',
};
子组件模式
子组件通过上下文访问父组件,并强制执行约束。
import { forwardRef, useProps, isElement, createEventHandler } from '@mantine/core';
import { useComponentContext } from '../Component.context';
export interface SubComponentProps {
children: React.ReactNode;
refProp?: string;
}
export const SubComponent = forwardRef<HTMLElement, SubComponentProps>((props, ref) => {
const { children, ...others } = useProps('SubComponent', {}, props);
if (!isElement(children)) {
throw new Error(COMPONENT_ERRORS.children);
}
const ctx = useComponentContext();
const onClick = createEventHandler(children.props.onClick, () => ctx.toggle());
return cloneElement(children, { onClick, ref });
});
Styles API
每个组件都提供 Styles API 以供自定义。在 ./docs/styles-api/Component.styles-api.ts 中定义:
import type { ComponentFactory } from '@your-scope/component';
import type { StylesApiData } from '../components/styles-api.types';
export const ComponentStylesApi: StylesApiData<ComponentFactory> = {
selectors: {
root: 'Root element',
element: 'Specific child element',
},
vars: {
root: {
'--custom-var': 'Controls custom behavior',
'--another-var': 'Controls another aspect',
},
},
modifiers: [
{ modifier: 'data-active', selector: 'root', condition: '`active` prop is set' }
],
};
CSS 模块(Component.module.css):
.root {
/* Use CSS custom properties from varsResolver */
property: var(--custom-var, fallback);
}
.element {
/* Scoped class name */
}
/* Data attribute modifiers */
.root[data-active] {
/* Active state */
}
组件指南
受控与非受控状态
使用 @mantine/hooks 中的 useUncontrolled 支持双模式状态:
import { useUncontrolled } from '@mantine/hooks';
const [value, setValue] = useUncontrolled({
value: props.value,
defaultValue: props.defaultValue,
finalValue: undefined,
onChange: props.onChange,
});
生命周期 Hook
使用 @mantine/hooks 中的 useDidUpdate 实现更新时触发副作用的模式:
import { useDidUpdate } from '@mantine/hooks';
useDidUpdate(() => {
props.onStateChange?.(state);
}, [state]);
属性验证
当类型系统不足以确保正确性时,在运行时验证属性:
if (React.Children.count(children) !== 2) {
throw new Error('Component requires exactly two children');
}
Ref 转发
始终支持 Ref 转发,以便组合组件:
export const Component = polymorphicFactory<ComponentFactory>((_props, ref) => {
return <Box ref={ref} {...others} />;
});
编码标准
导入顺序
Prettier 会根据 ./.prettierrc.mjs 自动排序导入:
- CSS 导入
- React
- Next.js(如适用)
- 内置模块
- 第三方模块
@mantine/*包- 本地导入(先父级,再同级)
- CSS 模块置于最后
格式
- 打印宽度:100 个字符
- 引号:单引号
- 尾随逗号:ES5 风格
- MDX:打印宽度为 70 个字符
提交前运行 npm run prettier:write。
代码检查
ESLint 配置继承自 eslint-config-mantine:
import mantine from 'eslint-config-mantine';
import tseslint from 'typescript-eslint';
export default tseslint.config(...mantine, {
ignores: ['**/.next/**', '**/*.{mjs,cjs,js,d.ts,d.mts}']
});
运行 npm run lint 检查所有规则。
TypeScript 配置
主要配置(./tsconfig.json):
- 目标:ES2015
- 模块:ESNext,使用 Node 解析
- JSX:React(经典运行时)
- 严格模式:已启用(通过 mantine 配置)
- 跳过库检查:true(加快构建速度)
构建配置(tsconfig.build.json)用于隔离编译范围。
测试
使用 @mantine-tests/core 渲染器和 Testing Library。
import React from 'react';
import { render } from '@mantine-tests/core';
import { Component } from './Component';
describe('Component', () => {
it('renders without crashing', () => {
const { container } = render(<Component>Content</Component>);
expect(container).toBeTruthy();
});
it('applies custom className', () => {
const { container } = render(<Component className="custom" />);
expect(container.querySelector('.custom')).toBeTruthy();
});
it('forwards ref', () => {
const ref = React.createRef<HTMLDivElement>();
render(<Component ref={ref} />);
expect(ref.current).toBeTruthy();
});
});
使用 npm run jest 运行测试。
文档
演示
在 ./docs/demos/ 中创建交互式演示:
// Component.demo.basic.tsx
import { Component } from '@your-scope/component';
import { MantineDemo } from '@docs/components';
const code = `
import { Component } from '@your-scope/component';
function Demo() {
return <Component>Content</Component>;
}
`;
export const basic: MantineDemo = {
type: 'code',
component: Demo,
code,
};
配置器演示使用 MantineDemo 类型 'configurator' 和 controls 对象。
MDX 页面
主要文档页面位于 ./docs/docs.mdx:
import { InstallScript } from './components/InstallScript/InstallScript';
import * as demos from './demos';
## Installation
<InstallScript packages="@your-scope/component" />
## Usage
<Demo data={demos.basic} />
## Props
<PropsTable component="Component" />
## Styles API
<StylesApiTable component="Component" />
请参照 /docs 中现有的文档结构,保持一致。
无障碍
ARIA 模式
- 优先使用语义化 HTML 元素(
<button>、<nav>等),而不是带有角色的<div> - 仅在语义化 HTML 不足时添加 ARIA 属性
- 参考 MDN ARIA 实践:https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA
键盘导航
- 确保所有交互元素都可通过键盘访问
- 使用
tabIndex={-1}进行程序化焦点管理 - 为复合控件实现方向键导航
焦点管理
import { useFocusTrap } from '@mantine/hooks';
const focusTrapRef = useFocusTrap(active);
用于状态的数据属性
通过数据属性公开组件状态,以供 CSS 和屏幕阅读器使用:
<div data-active={isActive} data-disabled={disabled} />
安全与维护
禁止硬编码机密信息
- 切勿提交 API 密钥、令牌或凭据
- 使用环境变量配置敏感信息
- 在
.gitignore中排除.env文件
依赖项管理
- 保持
@mantine/core和@mantine/hooks版本同步(参见./package/package.json) - 对 React 和 Mantine 包使用
peerDependencies - 运行
npm run syncpack验证版本一致性
最小权限
- 尽量缩小 React Context 提供程序的作用范围
- 避免使用全局状态;优先使用组件级状态
- 使用
createSafeContext强制执行提供程序边界
边缘情况与故障排除
模块解析
- 确保本地模块的所有导入都使用相对路径
- 检查
package.json的exports字段,确保入口点正确 - 使用别名时,检查
tsconfig.json中的paths
CSS Modules
- 类名通过
hash-css-selector自动限定作用域(参见./rollup.config.mjs) - 使用
import classes from './Component.module.css'导入 CSS 模块 - 使用
getStyles辅助函数合并 className
多态组件
- 默认组件为
div;可通过component属性覆盖 - Ref 类型必须与工厂定义中的
defaultRef匹配 - 属性会合并:
BoxProps & ComponentProps & { component?: any }
Testing Library 查询
- 优先使用
getByRole,而不是getByTestId - 全局查询使用
screen,限定范围的查询使用container - 需要时模拟
window.matchMedia(参见./jsdom.mocks.cjs)
Storybook 集成
- Story 文件与组件放在一起,命名为
Component.story.tsx - 使用包含
Meta和StoryObj类型的 CSF3 格式 - Story 会根据 TypeScript 类型自动生成文档
文件组织
为每个组件保持一致的文件结构:
package/src/
├── Component.tsx # Main component
├── Component.module.css # Scoped styles
├── Component.context.ts # Context (if needed)
├── Component.errors.ts # Error constants
├── Component.test.tsx # Tests
├── Component.story.tsx # Storybook
├── SubComponent/
│ └── SubComponent.tsx # Sub-component
└── index.ts # Public API
公共 API(index.ts):
export { Component } from './Component';
export type { ComponentProps, ComponentFactory } from './Component';
提交规范
- 提交前运行
npm run test(包括 prettier、typecheck、lint、jest) - 编写清晰的提交信息:
fix: resolve prop merging issue或feat: add keyboard navigation - 避免提交生成的文件(
dist/、.next/、out/) - 使用
.gitignore排除构建产物
参考资料
请参阅 ./references/ 中索引的文档指引。