Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .cursor/skills/nutui-proportional-scaling/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
name: nutui-proportional-scaling
description: >-
NutUI React proportional scaling on branch feat_resize: runtime --nut-scale-f /
--nut-scale-font / --nut-scale-icon from scale-f.ts (H5) and scale-f.taro.ts
(Taro), Sass helpers scale-px / scale-font-px / scale-icon-px and theme font
tokens in variables.scss & theme-*.scss; profiles standard / large / elderly;
commit-backed rules e.g. never scale 0px. Use when implementing 多尺寸适配,
等比适配, 大字版, 老年版, scale-px, viewport or native bridge scaling, or
editing component SCSS for resize.
---

# NutUI React 等比适配

## 1. 运行时:谁在写 CSS 变量

- **H5**:`src/utils/scale-f.ts`
- `initScaleF(profile?)`:首次计算缩放、`resize` 时 `refreshScaleF`。
- `getScaleF`:优先 `jmfe.callNative('DongScreenAdapterPlugin','getScale')`,失败用视口规则。
- 视口回退要点:`innerWidth >= 600` 视作 pad,基准乘 `1.2`;`375–600` 间按 `375` 比例,**上限 1.17**(与源码常量一致)。
- **Taro 侧复用同一套契约**:`src/utils/scale-f.taro.ts`,并从 `src/utils/index.taro.ts` 导出。
- 写入 `:root` 的变量(与 `variables.scss` 一致):
- `--nut-scale-f`:布局/通用 `scale-px`
- `--nut-scale-font`:`scale-font-px`、主题 `--nutui-font-size-*`
- `--nut-scale-icon`:`scale-icon-px`、图标相关

**档位 `ScaleProfile`**:`standard` | `large` | `elderly`(仅后两者生效额外倍率)。
**场景倍率**(与 `getSceneRatio` 一致):老年对 `font` / `icon` / `lego` × `1.3`;大字仅对 `font` × `1.15`。

**JS 里算像素**:`calcByProfile(baseValue, { scene, profile?, scale?, device? })` — 用于组件内联样式、画布尺寸等,与 Sass 的 `calc(...* var(--nut-scale-*))` 同一套语义。

---

## 2. 样式层:`variables.scss` 中的函数

```scss
// 根上默认值见 variables.scss :root
@function scale-px($size) {
@return calc(#{$size} * var(--nut-scale-f, 1));
}
@function scale-font-px($size) {
@return calc(#{$size} * var(--nut-scale-font, var(--nut-scale-f, 1)));
}
@function scale-icon-px($size) {
@return calc(#{$size} * var(--nut-scale-icon, var(--nut-scale-f, 1)));
}
```

**主题字号档**(`theme-default.scss` / `theme-dark.scss`):`--nutui-font-size-*` 使用 `calc(Npx * var(--nut-scale-font, var(--nut-scale-f, 1)))`,与 **大字/老年** 档位对齐。

---

## 3. 提交里固化的规范(务必遵守)

### 3.1 「0px 不转换」(`1a35d9b8`)

凡应为 **数值 0** 的尺寸,**不要**写 `scale-px(0px)`,一律 **`0`** 或 **`0px`**(如 padding 某一维、`box-shadow` 偏移、border 为 0、`margin: 0`)。
否则会得到 `calc(0px * var(--nut-scale-f))`,在部分浏览器或亚像素场景下与纯 `0` 表现不一致。

### 3.2 `line-height`

- **比例行高**(如 `line-height: 1`):不随系数变,用于挤压行盒、图标对齐等 — 与「等比 px」不同维度。
- **与设计稿 px 绑定的行高**:用与字号一致的档位,通常为 **`scale-font-px`**,或与同一变量体系。
- 参考历史修复:弹层标题等曾去掉不恰当的固定 `line-height` 以免与大字模式冲突 — 新增时不要给标题随意写死 `line-height: 20px` 类样式,除非走缩放函数或主题变量。

### 3.3 组件 SCSS 迁移模式(`dc4f1e28` / 后续 style 提交)

- 间距、圆角、`border` 粗细、固定宽高(非纯文字):优先 **`scale-px`**。
- 纯字体大小:`scale-font-px` 或主题已有 `--nutui-font-size-*`。
- 图标占位:**`scale-icon-px`** 或已有 `--nut-icon-*`。
- 保持与 **无障碍/大屏** 相关提交协同:同一文件改尺度时,勿回退 `dialog` 等对大字兼容的改动。

---

## 4. 业务接入清单

1. **入口调用**:在应用入口(仅浏览器环境)调用 `initScaleF(可选档位)`;档位可随业务切换并依赖内部 `setScaleProfile` 刷新变量。
2. **Taro**:使用 `scale-f.taro` 导出,保证 H5 与小程序 WebView 行为一致(仍以仓库实现为准)。
3. **覆写主题**:通过 `--nutui-*` 或 `--nut-scale-*` 覆盖时,保持 `calc` 与变量回退链完整。
4. **验收**:切换标准/大字/老年、收窄与放宽视口、(如有)站内容器走原生 `getScale`,检查布局与字号是否同比变化且无「0px 被 scale」问题。

---

## 5. Agent 自检(改完缩放权相关代码时)

- [ ] 新增 **0** 尺寸未误用 `scale-px(0px)`。
- [ ] 字体/图标是否应走 **`scale-font-px` / `scale-icon-px`** 而非误用 `scale-px`。
- [ ] TS 侧改 `scale-f*` 已同步考虑 **Taro** 文件。
- [ ] 修改 `formatScaleValue` / 断点时,已通读 **视口与平板常量** 是否仍与文档、设计一致。

若与上游分支分歧,以**当前分支 `feat_resize` 最新提交**及 `src/utils/scale-f*.ts`、`src/styles/variables.scss` 为准。
12 changes: 12 additions & 0 deletions packages/nutui-taro-demo/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component } from 'react'
import { initScaleF } from '@/utils/scale-f'
import('@/sites/assets/styles/reset.scss')
import('@/packages/nutui.react.scss.taro')
import('@nutui/touch-emulator')
Expand All @@ -7,6 +8,17 @@ import './app.scss'

// console.log(NutUI)
class App extends Component {
private disposeScale?: () => void

componentDidMount() {
// 写入 :root 的 --nut-scale-f / --nut-scale-font / --nut-scale-icon,与组件内 scale-* 一致
this.disposeScale = initScaleF('elderly')
}

componentWillUnmount() {
this.disposeScale?.()
}

render() {
return this.props.children
}
Expand Down
2 changes: 2 additions & 0 deletions packages/nutui-taro-demo/src/pages/index/index.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export default definePageConfig({
navigationBarTitleText: '首页',
// App设计规范适配版本
// designAppVersion: 16
})
8 changes: 4 additions & 4 deletions src/packages/actionsheet/actionsheet.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
}

.nut-popup-title {
border-bottom: 1px solid $actionsheet-border-color;
border-bottom: scale-px(1px) solid $actionsheet-border-color;
}

&-list {
Expand All @@ -23,7 +23,7 @@
&-cancel,
&-item {
display: block;
padding: 10px;
padding: scale-px(10px);
text-align: $actionsheet-item-text-align;
line-height: $actionsheet-item-line-height;
font-size: $font-size-base;
Expand Down Expand Up @@ -54,8 +54,8 @@
}

&-cancel {
margin-top: 5px;
border-top: 1px solid $actionsheet-border-color;
margin-top: scale-px(5px);
border-top: scale-px(1px) solid $actionsheet-border-color;
border-radius: $actionsheet-border-radius;
}

Expand Down
84 changes: 42 additions & 42 deletions src/packages/address/address.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
.nut-address {
&-exist {
display: block;
padding: 15px 20px 0;
height: 279px;
padding: scale-px(15px) scale-px(20px) 0;
height: scale-px(279px);
overflow-y: auto;
box-sizing: border-box;

&-item {
display: flex;
align-items: center;
margin-bottom: 20px;
margin-bottom: scale-px(20px);
font-size: $font-size-s;
line-height: $font-size-base;
color: $color-title;
Expand All @@ -22,56 +22,56 @@
}

&-info {
margin-left: 9px;
margin-left: scale-px(9px);
}
}
}

&-footer {
width: 100%;
height: 54px;
padding: 6px 0px 0;
border-top: 1px solid $color-border;
height: scale-px(54px);
padding: scale-px(6px) 0px 0;
border-top: scale-px(1px) solid $color-border;

&-btn {
width: 90%;
height: 42px;
line-height: 42px;
height: scale-px(42px);
line-height: scale-px(42px);
margin: auto;
text-align: center;
background: $button-primary-background-color;
border-radius: 21px;
font-size: 15px;
border-radius: scale-px(21px);
font-size: $font-size-l;
color: $color-primary-text;
}
}
}

.nut-address {
&-title {
font-size: 14px;
font-size: $font-size-base;
font-weight: 500;
padding: 16px 16px 12px 16px;
padding: scale-px(16px) scale-px(16px) scale-px(12px) scale-px(16px);
}
&-hotlist {
padding: 0 16px;
padding: 0 scale-px(16px);
display: flex;
flex-wrap: wrap;
align-items: flex-start;
&-item {
display: flex;
justify-content: center;
align-items: center;
width: 63px;
height: 28px;
font-size: 12px;
border-radius: 4px;
margin-bottom: 7px;
width: scale-px(63px);
height: scale-px(28px);
font-size: $font-size-s;
border-radius: scale-px(4px);
margin-bottom: scale-px(7px);
/* #ifdef harmony dynamic*/
margin-right: 6px;
margin-right: scale-px(6px);
/* #endif */
/* #ifndef harmony dynamic*/
margin-right: 7px;
margin-right: scale-px(7px);
/* #endif */
background-color: $color-background-sunken;
color: $color-title;
Expand All @@ -83,36 +83,36 @@
&.hotlist-more {
.nut-address-hotlist-item {
width: auto;
padding: 0 16px;
margin-right: 7px;
padding: 0 scale-px(16px);
margin-right: scale-px(7px);
}
}
}

&-selected {
width: 100%;
height: 60px;
padding: 0 16px;
height: scale-px(60px);
padding: 0 scale-px(16px);
display: flex;
align-items: center;
border-bottom: 1px solid $color-border;
border-bottom: scale-px(1px) solid $color-border;
&-item {
font-size: 12px;
font-size: $font-size-s;
display: inline-block;
height: 28px;
line-height: 28px;
padding: 0 12px;
border-radius: 4px;
height: scale-px(28px);
line-height: scale-px(28px);
padding: 0 scale-px(12px);
border-radius: scale-px(4px);
background-color: $color-background-sunken;

&.active {
border: 1px solid $color-primary;
border: scale-px(1px) solid $color-primary;
background-color: $color-primary-light-pressed;
color: $color-primary;
}
}
&-border {
margin: 0 2px;
margin: 0 scale-px(2px);
color: $color-text-disabled;
}
}
Expand All @@ -122,15 +122,15 @@
.nut-elevator-list {
&-item {
position: relative;
padding-left: 20px;
padding-left: scale-px(20px);
}
&-item-code {
display: inline;
position: absolute;
left: 0;
top: 0;
height: 30px;
line-height: 30px;
height: scale-px(30px);
line-height: scale-px(30px);
border-bottom: 0;
color: $color-text-help;
font-weight: 500;
Expand All @@ -145,11 +145,11 @@
display: flex;
justify-content: center;
align-items: center;
width: 16px;
height: 16px;
font-size: 10px;
border-radius: 16px;
margin-bottom: 2px;
width: scale-px(16px);
height: scale-px(16px);
font-size: $font-size-xxs;
border-radius: scale-px(16px);
margin-bottom: scale-px(2px);
color: $color-text-help;
&-active {
background-color: $color-primary;
Expand All @@ -167,7 +167,7 @@
&-item {
&-info {
margin-left: 0;
margin-right: 9px;
margin-right: scale-px(9px);
}
}
}
Expand Down
Loading
Loading