diff --git a/packages/nutui-taro-demo/config/index.js b/packages/nutui-taro-demo/config/index.js
index 47df7565b0..d58f4ca7ae 100644
--- a/packages/nutui-taro-demo/config/index.js
+++ b/packages/nutui-taro-demo/config/index.js
@@ -131,7 +131,6 @@ const config = {
sass: {
resource: [
path.resolve(__dirname, '../../../', fileStr),
- path.resolve(__dirname, '../../../', themeStr),
],
},
defineConstants: {},
diff --git a/packages/nutui-taro-demo/src/app.scss b/packages/nutui-taro-demo/src/app.scss
index 9049abed34..f007aeeeee 100644
--- a/packages/nutui-taro-demo/src/app.scss
+++ b/packages/nutui-taro-demo/src/app.scss
@@ -1,4 +1,4 @@
-@import '../../../src/styles/jd-font.scss';
+@import '../../../src/styles/theme-default.scss';
body {
font-size: 14px;
diff --git a/scripts/build-taro.mjs b/scripts/build-taro.mjs
index bf7446d930..3dde7b6a7b 100644
--- a/scripts/build-taro.mjs
+++ b/scripts/build-taro.mjs
@@ -21,6 +21,16 @@ const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const require = createRequire(import.meta.url)
const pxToScalePxInComponentScss = require('./px-to-scale-px-in-component-scss.cjs')
+
+// PostCSS plugin: remove .nut-rtl selectors for mini-program builds
+const stripRtlPlugin = {
+ postcssPlugin: 'strip-rtl',
+ Rule(rule) {
+ if (rule.selector && rule.selector.includes('.nut-rtl')) {
+ rule.remove()
+ }
+ },
+}
const dist = 'release/taro/dist'
const filePath = resolve(__dirname, '../package.json')
const packageJson = JSON.parse(readFileSync(filePath, 'utf8'))
@@ -289,6 +299,11 @@ async function buildAllCSS(themeName = '') {
}
},
})
+
+ // generate mini-program variant without RTL rules
+ const fullCss = await readFile(join(__dirname, `../${dist}/${themeStylePath}.css`), { encoding: 'utf8' })
+ const miniResult = await postcss([stripRtlPlugin]).process(fullCss, { from: undefined })
+ await dest(`${dist}/${themeStylePath}.mini.css`, miniResult.css)
}
async function buildThemeCSS() {
@@ -400,29 +415,41 @@ async function buildCSS(themeName = '') {
await dest(join(`${dist}/es`, cssPath, `${themeDir}/${base}`), postcssRes.css)
await dest(join(`${dist}/cjs`, cssPath, `${themeDir}/${base}`), postcssRes.css)
- const code = sass.compileString(variables + '\n' + postcssRes.css.replaceAll('../../../../', '../../'), {
+ // strip CSS entity blocks (:root/page {}) from variables before per-component compile
+ // these blocks belong in the global style.css entry, not repeated in each component
+ const variablesSassOnly = variables.toString().replace(/:root\s*,?\s*\npage\s*\{[^}]*\}\n?/g, '')
+ const code = sass.compileString(variablesSassOnly + '\n' + postcssRes.css.replaceAll('../../../../', '../../'), {
loadPaths: [loadPath],
})
await dest(join(`${dist}/es`, cssPath, `${themeDir}/style.css`), code.css)
await dest(join(`${dist}/cjs`, cssPath, `${themeDir}/style.css`), code.css)
+ // strip .nut-rtl rules for mini-program — no RTL needed in weapp/jd/tt etc.
+ const miniCssResult = await postcss([stripRtlPlugin]).process(code.css, { from: undefined })
+ await dest(join(`${dist}/es`, cssPath, `${themeDir}/style.mini.css`), miniCssResult.css)
+ await dest(join(`${dist}/cjs`, cssPath, `${themeDir}/style.mini.css`), miniCssResult.css)
+
const jsContent = []
const cssContent = []
+ const miniContent = []
atRules.forEach((rule) => {
rule = rule.replaceAll('\'', '')
if (rule.indexOf('../styles/') > -1) {
const ext = extname(rule)
jsContent.push(`import '../../${rule}${ext ? '' : '.scss'}';`)
+ miniContent.push(`import '../../${rule}${ext ? '' : '.scss'}';`)
} else if (rule.startsWith('../') || rule.startsWith('./')) {
const base = basename(rule)
const ext = extname(base)
const name = base.replace(ext, '')
jsContent.push(`import '../../${name}/${themeDir}';`)
cssContent.push(`import '../../${name}/${themeDir}/css';`)
+ miniContent.push(`import '../../${name}/${themeDir}/mini';`)
}
})
jsContent.push(`import './${base}';`)
cssContent.push(`import './style.css';`)
+ miniContent.push(`import './style.mini.css';`)
await dest(
join(`${dist}/cjs`, cssPath, `${themeDir}/index.js`),
@@ -436,6 +463,13 @@ async function buildCSS(themeName = '') {
join(`${dist}/cjs`, cssPath, `${themeDir}/css.js`),
cssContent.join('\n'),
)
+
+ // 写 mini 文件(小程序专用,无 RTL)
+ await dest(join(`${dist}/es`, cssPath, `${themeDir}/mini.js`), miniContent.join('\n'))
+ await dest(
+ join(`${dist}/cjs`, cssPath, `${themeDir}/mini.js`),
+ miniContent.join('\n'),
+ )
}
}
@@ -489,8 +523,10 @@ async function buildHarmonyCSS(themeName = '') {
return result
})
const themeDir = themeName ? `style-${themeName}` : 'style'
+ // strip CSS entity blocks from variables — same as buildCSS
+ const variablesSassOnly = variables.toString().replace(/:root\s*,?\s*\npage\s*\{[^}]*\}\n?/g, '')
const code = sass.compileString(
- variables + '\n' + postcssRes.css.replaceAll('../../../../', '../../'),
+ variablesSassOnly + '\n' + postcssRes.css.replaceAll('../../../../', '../../'),
{
loadPaths: [loadPath],
},
diff --git a/src/sites/sites-react/doc/docs/taro/start-react.en-US.md b/src/sites/sites-react/doc/docs/taro/start-react.en-US.md
index 6b306a0105..8849eca58a 100644
--- a/src/sites/sites-react/doc/docs/taro/start-react.en-US.md
+++ b/src/sites/sites-react/doc/docs/taro/start-react.en-US.md
@@ -200,6 +200,8 @@ module.exports = {
customStyleName: (name) =>
`@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}/style`,
// customStyleName: (name) => `@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}/style/css`
+ // mini-program only: removes RTL styles for smaller bundle
+ // customStyleName: (name) => `@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}/style/mini`
},
'nutui-react',
],
@@ -212,11 +214,14 @@ Taro config/index.js
```js
{
sass: {
- data: '@import "@nutui/nutui-react-taro/dist/styles/variables.scss";'
- // JMAPP Theme
- // data: `@import '@nutui/nutui-react-taro/dist/styles/variables-jmapp.scss';`
- // JRKF Theme
- // data: `@import '@nutui/nutui-react-taro/dist/styles/variables-jrkf.scss';`
+ // resource only injects pure Sass variables/functions, no CSS entity output
+ resource: [
+ path.resolve(__dirname, 'node_modules/@nutui/nutui-react-taro/dist/styles/variables.scss'),
+ // JMAPP Theme
+ // path.resolve(__dirname, 'node_modules/@nutui/nutui-react-taro/dist/styles/variables-jmapp.scss'),
+ // JRKF Theme
+ // path.resolve(__dirname, 'node_modules/@nutui/nutui-react-taro/dist/styles/variables-jrkf.scss'),
+ ],
}
}
```
diff --git a/src/sites/sites-react/doc/docs/taro/start-react.md b/src/sites/sites-react/doc/docs/taro/start-react.md
index d7e58c23dc..19ec70d837 100644
--- a/src/sites/sites-react/doc/docs/taro/start-react.md
+++ b/src/sites/sites-react/doc/docs/taro/start-react.md
@@ -209,6 +209,8 @@ module.exports = {
`@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}/style`,
// 自动加载 css 样式文件
// customStyleName: (name) => `@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}/style/css`
+ // 自动加载 css 样式文件(小程序专用,已移除 RTL 样式,体积更小)
+ // customStyleName: (name) => `@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}/style/mini`
// JMAPP 主题
// 自动加载 scss 样式文件
@@ -233,11 +235,14 @@ module.exports = {
```js
{
sass: {
- data: '@import "@nutui/nutui-react-taro/dist/styles/variables.scss";'
- // JMAPP 主题
- // data: `@import '@nutui/nutui-react-taro/dist/styles/variables-jmapp.scss';`
- // JRKF 主题
- // data: `@import '@nutui/nutui-react-taro/dist/styles/variables-jrkf.scss';`
+ // resource 仅注入纯 Sass 变量/函数,不含实体 CSS 输出
+ resource: [
+ path.resolve(__dirname, 'node_modules/@nutui/nutui-react-taro/dist/styles/variables.scss'),
+ // JMAPP 主题
+ // path.resolve(__dirname, 'node_modules/@nutui/nutui-react-taro/dist/styles/variables-jmapp.scss'),
+ // JRKF 主题
+ // path.resolve(__dirname, 'node_modules/@nutui/nutui-react-taro/dist/styles/variables-jrkf.scss'),
+ ],
}
}
```
diff --git a/src/sites/sites-react/doc/docs/taro/theme-react.md b/src/sites/sites-react/doc/docs/taro/theme-react.md
index 0a6b733c82..1a69e202e3 100644
--- a/src/sites/sites-react/doc/docs/taro/theme-react.md
+++ b/src/sites/sites-react/doc/docs/taro/theme-react.md
@@ -51,7 +51,7 @@ export default defineConfig({
#### Webpack 方式
-```javascript
+````javascript
{
test: /\.(sa|sc)ss$/,
use: [
@@ -64,4 +64,163 @@ export default defineConfig({
}
]
}
+
+### Taro 项目:使用 sass.resource 的性能说明
+
+在 Taro 项目中,通常会通过 `config/index.js` 的 `sass.resource` 来注入全局 SCSS 变量:
+
+```javascript
+// config/index.js
+const config = {
+ sass: {
+ resource: [
+ path.resolve(__dirname, '../src/styles/variables.scss'),
+ ],
+ },
+}
+````
+
+**注意**:`sass.resource` 会将文件内容注入到项目中**每一个** SCSS 文件的头部参与编译。因此,应**只将包含纯 Sass 变量/函数定义的文件**放入 `sass.resource`——即只包含 `$variable`、`@mixin`、`@function` 等编译期定义,不含任何实体 CSS 输出(如 `:root {}` 块或 `@font-face` 声明)。
+
+`theme-default.scss`(主题 CSS 变量文件)全部内容为 `:root, page {}` CSS 自定义属性声明,属于**运行时 CSS**,无需在每个组件编译时重复注入。应将其移至全局入口样式(如 `app.scss`)中一次性引入:
+
+```scss
+/* app.scss —— 全局引入一次,而非通过 sass.resource 重复注入 */
+@import '../../src/styles/theme-default.scss';
+```
+
+```javascript
+// config/index.js —— sass.resource 只保留纯 Sass 变量/函数文件
+const config = {
+ sass: {
+ resource: [
+ path.resolve(__dirname, '../src/styles/variables.scss'),
+ // 不要在这里加入 theme-default.scss
+ ],
+ },
+}
+```
+
+这样可以避免 `theme-default.scss` 的 380+ 行 CSS 被重复编译到每个 SCSS 文件中,显著减少编译产物体积。
+
+## 暗黑模式
+
+NutUI-React (Taro 版) 原生支持暗黑模式。组件库在暗黑模式下使用一套独立调优的色彩变量。
+
+### 方案 A:跟随 APP 主动切换(推荐)
+
+如果你的小程序/应用需要由用户在设置中手动切换亮色/暗黑模式(不强制跟随系统),可以使用**类名切换方案**。
+
+#### 1. 引入样式
+
+首先,在项目入口或全局样式文件中引入组件库的默认样式和暗黑样式。你可以根据项目配置选择引入 CSS 或 SCSS:
+
+##### 引入 CSS 样式(适用于普通 JS/TS 项目):
+
+参考方式二。
+
+##### 引入 SCSS 样式(适用于配置了 Sass 预处理器的项目):
+
+参考方式二。
+
+#### 2. 挂载类名
+
+在小程序的根页面容器(如 Taro 的 `View` 标签或全局包裹页面组件)上挂载 `.nut-theme-dark` 类名:
+
+`isDark` 的值,需要桥接 APP 的实现。
+
+```tsx
+import React, { useState } from 'react'
+import { View, Button } from '@tarojs/components'
+
+function App() {
+ const [isDark, setIsDark] = useState(false)
+
+ return (
+
+
+ {/* 你的其他组件 */}
+
+ )
+}
+```
+
+#### 3. 配合 ConfigProvider 进行定制
+
+如果你需要更灵活地在 Taro/React 逻辑中控制暗黑模式,或者对暗黑色彩变量进行定制,可以结合 `ConfigProvider` 使用:
+
+```tsx
+import React, { useState } from 'react'
+import { View, Button } from '@tarojs/components'
+import { ConfigProvider } from '@nutui/nutui-react-taro'
+
+// 自定义暗黑模式下的主色调
+const darkTheme = {
+ nutuiColorPrimary: '#ff0f23',
+ nutuiColorBackgroundOverlay: '#1f2226',
+}
+
+function App() {
+ const [isDark, setIsDark] = useState(false)
+
+ return (
+
+
+
+ {/* 组件库在暗黑模式下会自动识别 .nut-theme-dark,且通过 theme 属性定制的变量会自动覆盖 */}
+
+
+ )
+}
+```
+
+---
+
+### 方案 B:跟随系统媒体查询
+
+如果你的应用希望**完全自动跟随手机操作系统**的亮暗模式设置(例如微信小程序的深色模式),无需手动提供开关,可使用**系统媒体查询方案**。
+
+#### 1. 编写全局 SCSS
+
+在全局 SCSS 样式文件中利用媒体查询动态注入暗黑变量。对于小程序环境,样式需要作用在 `page` 节点上以覆盖整页背景:
+
+```scss
+@media (prefers-color-scheme: dark) {
+ page {
+ // 导入组件库自带的暗黑变量映射
+ @import '@nutui/nutui-react-taro/dist/styles/theme-dark.scss';
+ }
+}
+```
+
+#### 2. 引入配置
+
+确保你的小程序(以微信小程序为例)在 `app.json` 或 `app.config.ts` 中配置支持暗黑模式:
+
+```json
+{
+ "darkmode": true
+}
+```
+
+---
+
+### 方案对比与选用建议
+
+| 方案 | 适用场景 | 实现原理 | 优点 |
+| --- | --- | --- | --- |
+| **方案 A (跟随 APP)** | 应用内需要提供“深色/浅色”的手动切换开关,不强绑定系统。 | 挂载类名 `.nut-theme-dark` 或结合 `ConfigProvider` | 灵活度最高,支持局部容器应用暗黑,可通过 JS/TS 灵活控制。 |
+| **方案 B (跟随系统)** | 应用不需要手动开关,纯粹期望手机系统是深色模式时,小程序自动呈现深色。 | `@media (prefers-color-scheme: dark)` | 无需 JS 介入,完全由宿主小程序容器自动触发渲染。 |
+
+```
+
+
```