diff --git a/CLAUDE.md b/CLAUDE.md
index 0f72a01f..d0e32f2a 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -174,6 +174,8 @@ import { quickInsert } from '../../../services/canvas-operations';
2. Hover 延迟操作需要正确的计时器清理
3. 第三方窗口需用 `createPortal` 保持 React 事件流
4. 图标组件使用 `React.FC`,支持 `size` 属性
+5. 传递组件作为 prop 时必须实例化:`icon={}` 而非 `icon={Icon}`
+6. 内联 style 的 `undefined` 值会覆盖 CSS 类,需要 CSS 类生效时传 `style={undefined}`
### 缓存规则
diff --git a/docs/CODING_RULES.md b/docs/CODING_RULES.md
index b681abe6..619340be 100644
--- a/docs/CODING_RULES.md
+++ b/docs/CODING_RULES.md
@@ -526,6 +526,64 @@ const openDialog = (dialogType: DialogType) => {
- 函数式更新 `setState(prev => ...)` 保证 `prev` 始终是最新状态
- 这个问题在多个弹窗/抽屉同时打开时特别容易出现
+#### 传递 React 组件作为 prop 时必须实例化
+
+**场景**: 将 React 组件作为 `icon` 或其他 prop 传递给子组件时
+
+❌ **错误示例**:
+```typescript
+// 错误:传递组件函数本身,而不是 JSX 实例
+import { BackgroundColorIcon } from './icons';
+
+const icon = !hexColor ? BackgroundColorIcon : undefined;
+
+// 子组件中渲染时:
+// {icon} → React 警告 "Functions are not valid as a React child"
+```
+
+✅ **正确示例**:
+```typescript
+// 正确:传递 JSX 实例
+import { BackgroundColorIcon } from './icons';
+
+const icon = !hexColor ? : undefined;
+
+// 子组件中渲染时:
+// {icon} → 正常渲染
+```
+
+**原因**: React 组件本质上是函数,直接将函数作为子元素传递会导致 React 警告。需要调用组件(``)生成 JSX 元素后再传递。
+
+#### 内联 style 的 undefined 值会覆盖 CSS 类样式
+
+**场景**: 当需要条件性地应用内联样式,同时使用 CSS 类作为备选样式时
+
+❌ **错误示例**:
+```typescript
+// 错误:style 对象中的 undefined 值会覆盖 CSS 类的 background
+
+```
+
+✅ **正确示例**:
+```typescript
+// 正确:当需要 CSS 类生效时,不传递 style 对象
+
+```
+
+**原因**: React 的内联 style 优先级高于 CSS 类。即使 `background: undefined`,React 仍会在元素上设置空的 style 属性,这可能干扰 CSS 类的样式应用。当需要 CSS 类完全控制样式时,应该不传递 style 对象(`style={undefined}`)。
+
#### 使用 ResizeObserver 实现组件级别的响应式布局
**场景**: 当组件位于可调整大小的侧边栏、抽屉或面板中时,使用基于视口宽度的媒体查询 (`@media`) 无法准确反映组件的实际可用空间。
diff --git a/packages/drawnix/src/components/toolbar/popup-toolbar/fill-button.tsx b/packages/drawnix/src/components/toolbar/popup-toolbar/fill-button.tsx
index 26d9b10d..8ad312c7 100644
--- a/packages/drawnix/src/components/toolbar/popup-toolbar/fill-button.tsx
+++ b/packages/drawnix/src/components/toolbar/popup-toolbar/fill-button.tsx
@@ -92,7 +92,7 @@ export const PopupFillButton: React.FC = ({
}, [currentColor]);
const icon =
- !hexColor || isFullyTransparent(opacity) ? BackgroundColorIcon : undefined;
+ !hexColor || isFullyTransparent(opacity) ? : undefined;
// 处理纯色变更
const handleSolidChange = useCallback(
diff --git a/packages/drawnix/src/components/toolbar/popup-toolbar/popup-toolbar.scss b/packages/drawnix/src/components/toolbar/popup-toolbar/popup-toolbar.scss
index 4f2051e8..b302e9d1 100644
--- a/packages/drawnix/src/components/toolbar/popup-toolbar/popup-toolbar.scss
+++ b/packages/drawnix/src/components/toolbar/popup-toolbar/popup-toolbar.scss
@@ -19,15 +19,19 @@
border: 1px solid #c0c0c0;
}
&.color-mixed {
- background: conic-gradient(
- from 0deg,
- #f87171 0deg 60deg,
- #fbbf24 60deg 120deg,
- #34d399 120deg 180deg,
- #60a5fa 180deg 240deg,
- #a78bfa 240deg 300deg,
- #f472b6 300deg 360deg
- );
+ // 使用环形渐变效果,与线条杂色表示方式一致
+ border: var(--border-radius-sm) solid transparent;
+ background:
+ linear-gradient(var(--color-surface, #fff), var(--color-surface, #fff)) padding-box,
+ conic-gradient(
+ from 0deg,
+ #f87171 0deg 60deg,
+ #fbbf24 60deg 120deg,
+ #34d399 120deg 180deg,
+ #60a5fa 180deg 240deg,
+ #a78bfa 240deg 300deg,
+ #f472b6 300deg 360deg
+ ) border-box;
}
}
.stroke-label {
diff --git a/packages/drawnix/src/components/toolbar/popup-toolbar/popup-toolbar.tsx b/packages/drawnix/src/components/toolbar/popup-toolbar/popup-toolbar.tsx
index bf856d95..4bc197b1 100644
--- a/packages/drawnix/src/components/toolbar/popup-toolbar/popup-toolbar.tsx
+++ b/packages/drawnix/src/components/toolbar/popup-toolbar/popup-toolbar.tsx
@@ -501,20 +501,25 @@ export const PopupToolbar = () => {
'color-gradient': state.fill && isFillConfig(state.fill) && state.fill.type === 'gradient',
'color-image': state.fill && isFillConfig(state.fill) && state.fill.type === 'image',
})}
- style={{
- // 统一使用 background 属性,避免 backgroundColor 和 background 冲突
- background: state.fill && isSolidFill(state.fill)
- ? state.fill
- : state.fill && isFillConfig(state.fill)
- ? state.fill.type === 'gradient' && state.fill.gradient
- ? gradientToCSS(state.fill.gradient)
- : state.fill.type === 'solid' && state.fill.solid
- ? state.fill.solid.color
- : state.fill.type === 'image' && state.fill.image?.imageUrl
- ? `url(${state.fill.image.imageUrl}) center/cover no-repeat`
- : undefined
- : undefined,
- }}
+ style={
+ // 当 fill 为 undefined 时(杂色状态),不设置内联 background,让 CSS 类生效
+ state.fill === undefined
+ ? undefined
+ : {
+ // 统一使用 background 属性,避免 backgroundColor 和 background 冲突
+ background: isSolidFill(state.fill)
+ ? state.fill
+ : isFillConfig(state.fill)
+ ? state.fill.type === 'gradient' && state.fill.gradient
+ ? gradientToCSS(state.fill.gradient)
+ : state.fill.type === 'solid' && state.fill.solid
+ ? state.fill.solid.color
+ : state.fill.type === 'image' && state.fill.image?.imageUrl
+ ? `url(${state.fill.image.imageUrl}) center/cover no-repeat`
+ : undefined
+ : undefined,
+ }
+ }
>
)}