create-shopify-scss-autofill 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/create-scss-kit.mjs
CHANGED
|
@@ -79,18 +79,7 @@ function parseArgs(argv) {
|
|
|
79
79
|
return out
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
function makeLowRangeCoefficients() {
|
|
83
|
-
const out = {}
|
|
84
|
-
for (let i = 0; i <= 50; i += 5) {
|
|
85
|
-
const key = `scale-${String(i).padStart(2, '0')}`
|
|
86
|
-
out[key] = Number((i / 100).toFixed(2))
|
|
87
|
-
}
|
|
88
|
-
return out
|
|
89
|
-
}
|
|
90
|
-
|
|
91
82
|
function makeDefaultConfig() {
|
|
92
|
-
const lowRange = makeLowRangeCoefficients()
|
|
93
|
-
|
|
94
83
|
return {
|
|
95
84
|
$schema: './tools/scss-kit/schema.json',
|
|
96
85
|
design: {
|
|
@@ -104,6 +93,7 @@ function makeDefaultConfig() {
|
|
|
104
93
|
},
|
|
105
94
|
autofill: {
|
|
106
95
|
function: 'r.resp',
|
|
96
|
+
vwFunction: 'r.vw',
|
|
107
97
|
mobileMax: 850,
|
|
108
98
|
scanDirs: ['src/styles'],
|
|
109
99
|
output: 'src/styles/_responsive-autofill.generated.scss',
|
|
@@ -126,30 +116,82 @@ function makeDefaultConfig() {
|
|
|
126
116
|
},
|
|
127
117
|
coefficients: {
|
|
128
118
|
mobile: {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
'button-text':
|
|
139
|
-
|
|
119
|
+
h1: 0.52,
|
|
120
|
+
h2: 0.55,
|
|
121
|
+
h3: 0.55,
|
|
122
|
+
h4: 0.6,
|
|
123
|
+
h5: 0.6,
|
|
124
|
+
h6: 0.6,
|
|
125
|
+
body: 0.6,
|
|
126
|
+
small: 0.65,
|
|
127
|
+
quote: 0.55,
|
|
128
|
+
'button-text': 0.6,
|
|
129
|
+
'nav-link': 0.6,
|
|
130
|
+
'form-label': 0.6,
|
|
131
|
+
'form-input': 0.6,
|
|
132
|
+
'price-large': 0.5,
|
|
133
|
+
'price-small': 0.6,
|
|
134
|
+
badge: 0.65,
|
|
135
|
+
breadcrumb: 0.65,
|
|
136
|
+
},
|
|
137
|
+
desktop: {
|
|
138
|
+
h1: 0.58,
|
|
139
|
+
h2: 0.6,
|
|
140
|
+
h3: 0.6,
|
|
141
|
+
h4: 0.6,
|
|
142
|
+
h5: 0.65,
|
|
143
|
+
h6: 0.65,
|
|
144
|
+
body: 0.65,
|
|
145
|
+
small: 0.65,
|
|
146
|
+
quote: 0.6,
|
|
147
|
+
'button-text': 0.65,
|
|
148
|
+
'nav-link': 0.65,
|
|
149
|
+
'form-label': 0.65,
|
|
150
|
+
'form-input': 0.65,
|
|
151
|
+
'price-large': 0.55,
|
|
152
|
+
'price-small': 0.65,
|
|
153
|
+
badge: 0.7,
|
|
154
|
+
breadcrumb: 0.7,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
floors: {
|
|
158
|
+
mobile: {
|
|
159
|
+
h1: '24px',
|
|
160
|
+
h2: '20px',
|
|
161
|
+
h3: '18px',
|
|
162
|
+
h4: '16px',
|
|
163
|
+
h5: '14px',
|
|
164
|
+
h6: '14px',
|
|
165
|
+
body: '14px',
|
|
166
|
+
small: '12px',
|
|
167
|
+
quote: '14px',
|
|
168
|
+
'button-text': '14px',
|
|
169
|
+
'nav-link': '14px',
|
|
170
|
+
'form-label': '14px',
|
|
171
|
+
'form-input': '16px',
|
|
172
|
+
'price-large': '22px',
|
|
173
|
+
'price-small': '14px',
|
|
174
|
+
badge: '11px',
|
|
175
|
+
breadcrumb: '11px',
|
|
140
176
|
},
|
|
141
177
|
desktop: {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
'
|
|
149
|
-
'
|
|
150
|
-
'
|
|
151
|
-
'button-text':
|
|
152
|
-
|
|
178
|
+
h1: '28px',
|
|
179
|
+
h2: '22px',
|
|
180
|
+
h3: '18px',
|
|
181
|
+
h4: '16px',
|
|
182
|
+
h5: '14px',
|
|
183
|
+
h6: '14px',
|
|
184
|
+
body: '14px',
|
|
185
|
+
small: '12px',
|
|
186
|
+
quote: '16px',
|
|
187
|
+
'button-text': '14px',
|
|
188
|
+
'nav-link': '14px',
|
|
189
|
+
'form-label': '14px',
|
|
190
|
+
'form-input': '14px',
|
|
191
|
+
'price-large': '24px',
|
|
192
|
+
'price-small': '14px',
|
|
193
|
+
badge: '12px',
|
|
194
|
+
breadcrumb: '12px',
|
|
153
195
|
},
|
|
154
196
|
},
|
|
155
197
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# scss-kit(本仓库内置工具)
|
|
2
2
|
|
|
3
|
-
目的:在 Shopify ThemeKit 开发里实现“写 SCSS(src/styles)→ 编译 CSS(assets)→ theme watch 上传”,并提供一套基于 `r.resp(pc, mobile, desktopType[, mobileType])` 的移动端覆盖自动生成能力。
|
|
3
|
+
目的:在 Shopify ThemeKit 开发里实现“写 SCSS(src/styles)→ 编译 CSS(assets)→ theme watch 上传”,并提供一套基于 `r.resp(pc, mobile, desktopType[, mobileType])` 与 `r.vw(pc, mobile)` 的移动端覆盖自动生成能力。
|
|
4
4
|
|
|
5
5
|
## 拷贝接入(新项目)
|
|
6
6
|
|
|
@@ -57,6 +57,72 @@
|
|
|
57
57
|
- 顶部 `@use "./_responsive-autofill.<page>.generated" as auto;`
|
|
58
58
|
- 底部 `@include auto.responsive_autofill_overrides();`(确保覆盖顺序)
|
|
59
59
|
|
|
60
|
+
推荐约定:
|
|
61
|
+
|
|
62
|
+
- 字体/需要下限兜底的值:使用 `r.resp(pc, mobile, desktopType[, mobileType])`
|
|
63
|
+
- 大多数间距(padding/margin/gap):使用 `r.vw(pc, mobile)`
|
|
64
|
+
- 少量间距若确实需要最小值:改用 `r.resp(...)`(不要给 `r.vw` 追加参数)
|
|
65
|
+
|
|
66
|
+
`r.vw(pc, mobile)` 的行为:
|
|
67
|
+
|
|
68
|
+
- PC 输出 `min(vw, px)`,例如 `r.vw(40px, 24px)` → `min(2.0833vw, 40px)`
|
|
69
|
+
- 自动生成的移动端覆盖直接输出 `vw`,例如 `3.2vw`
|
|
70
|
+
|
|
71
|
+
## Responsive 函数目录(完整)
|
|
72
|
+
|
|
73
|
+
以下函数都定义在 `src/styles/_responsive.scss`(由 `scss-kit` 生成):
|
|
74
|
+
|
|
75
|
+
### 业务侧常用(推荐直接调用)
|
|
76
|
+
|
|
77
|
+
- `r.resp($pc, $mobile, $type, $mobile-type: null)`
|
|
78
|
+
- 用途:用于“字体/需要下限兜底”的场景。
|
|
79
|
+
- PC 输出:`clamp_pc($pc, min_px($pc, $type, desktop))`。
|
|
80
|
+
- Mobile 输出:由自动生成器扫描 `r.resp(...)` 后,在 `@media` 中生成 `clamp_mb(...)` 覆盖。
|
|
81
|
+
- 第 3/4 参数:第 3 个是 PC type;第 4 个是 mobile type(可省略,省略时沿用第 3 个)。
|
|
82
|
+
- 示例:`font-size: r.resp(40px, 24px, h1, h2);`
|
|
83
|
+
|
|
84
|
+
- `r.vw($pc, $mobile)`
|
|
85
|
+
- 用途:用于“间距/尺寸优先流式”的场景(如 padding/margin/gap/宽高)。
|
|
86
|
+
- PC 输出:`min(vw, px)`(防止超大屏继续放大)。
|
|
87
|
+
- Mobile 输出:由自动生成器扫描 `r.vw(...)` 后,在 `@media` 中生成纯 `vw` 覆盖。
|
|
88
|
+
- 说明:`r.vw` 仅保留两个参数;若你需要最小值下限,请使用 `r.resp(...)`。
|
|
89
|
+
- 示例:`margin-top: r.vw(40px, 24px);`
|
|
90
|
+
|
|
91
|
+
### 由 `r.resp` / `r.vw` 间接使用(一般不直接写)
|
|
92
|
+
|
|
93
|
+
- `r.min_px($value, $type, $range: mobile, $override-coef: null, $override-floor: null)`
|
|
94
|
+
- 用途:计算 clamp 最小值。
|
|
95
|
+
- 规则:
|
|
96
|
+
- 命名 type:按 `max(designPx * coef(type), floor(type))` 计算(floor 来自 `floors.mobile/desktop`)。
|
|
97
|
+
- 数字 type:可直接传系数(例如 `0.6`),此时跳过 type 表查询;可选叠加 `override-floor`。
|
|
98
|
+
|
|
99
|
+
- `r.coef($type, $range: mobile)`
|
|
100
|
+
- 用途:读取 `coefficients.mobile/desktop` 中对应 type 的系数。
|
|
101
|
+
|
|
102
|
+
- `r._floor($type, $range: mobile)`
|
|
103
|
+
- 用途:读取 `floors.mobile/desktop` 中对应 type 的绝对兜底值。
|
|
104
|
+
|
|
105
|
+
- `r.clamp_pc($pc, $min)`
|
|
106
|
+
- 用途:生成 PC clamp。
|
|
107
|
+
- 结构:`clamp($min, calc($pc * var(--px-to-vw)), $pc)`。
|
|
108
|
+
|
|
109
|
+
- `r.clamp_mb($mobile, $min)`
|
|
110
|
+
- 用途:生成移动端 clamp。
|
|
111
|
+
- 结构:`clamp($min, calc($mobile * var(--px-to-vw-mb)), $mobile)`。
|
|
112
|
+
|
|
113
|
+
- `r.vw_pc($pc)`
|
|
114
|
+
- 用途:生成 PC 端 `vw` 并带上限。
|
|
115
|
+
- 结构:`min(calc($pc * var(--px-to-vw)), $pc)`。
|
|
116
|
+
|
|
117
|
+
- `r.vw_mb($mobile)`
|
|
118
|
+
- 用途:生成移动端纯 `vw`。
|
|
119
|
+
- 结构:`calc($mobile * var(--px-to-vw-mb))`。
|
|
120
|
+
|
|
121
|
+
### 内部工具函数(不建议业务直接使用)
|
|
122
|
+
|
|
123
|
+
- `_to-px($value)`:把无单位数字转为 `px`。
|
|
124
|
+
- `_to-num($value)`:把 `px` 或无单位数字转为纯数字(其他单位会报错)。
|
|
125
|
+
|
|
60
126
|
## CSS 编译(safe mode)
|
|
61
127
|
|
|
62
128
|
`npm run css:watch` 默认走 safe mode:
|
|
@@ -67,6 +67,7 @@ function getMobileMax(cfg) {
|
|
|
67
67
|
|
|
68
68
|
function getAutofill(cfg) {
|
|
69
69
|
const fn = cfg?.autofill?.function ?? 'r.resp'
|
|
70
|
+
const vwFn = cfg?.autofill?.vwFunction
|
|
70
71
|
const mobileMax = getMobileMax(cfg)
|
|
71
72
|
const scanDirs = cfg?.autofill?.scanDirs ?? [
|
|
72
73
|
cfg?.paths?.scssSrcDir ?? 'src/styles',
|
|
@@ -82,9 +83,27 @@ function getAutofill(cfg) {
|
|
|
82
83
|
}
|
|
83
84
|
const ns = parts[0]
|
|
84
85
|
const name = parts[1]
|
|
86
|
+
|
|
87
|
+
const defaultVwFn = `${ns}.vw`
|
|
88
|
+
const vwParts = String(vwFn ?? defaultVwFn).split('.')
|
|
89
|
+
if (vwParts.length !== 2 || !vwParts[0] || !vwParts[1]) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Invalid autofill.vwFunction: ${String(
|
|
92
|
+
vwFn
|
|
93
|
+
)}. Expected format: <ns>.<name> (e.g. ${defaultVwFn})`
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
if (vwParts[0] !== ns) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`autofill.vwFunction namespace must match autofill.function namespace (${ns})`
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
const vwName = vwParts[1]
|
|
102
|
+
|
|
85
103
|
return {
|
|
86
104
|
ns,
|
|
87
105
|
name,
|
|
106
|
+
vwName,
|
|
88
107
|
mobileMax,
|
|
89
108
|
scanDirs,
|
|
90
109
|
outputAbs: path.join(ROOT, output),
|
|
@@ -139,8 +158,7 @@ function splitTopLevelArgs(argsStr) {
|
|
|
139
158
|
return args
|
|
140
159
|
}
|
|
141
160
|
|
|
142
|
-
function
|
|
143
|
-
const token = `${ns}.${name}(`
|
|
161
|
+
function replaceFunctionCalls(value, token, mapArgsToReplacement) {
|
|
144
162
|
let out = ''
|
|
145
163
|
let idx = 0
|
|
146
164
|
let changed = false
|
|
@@ -183,11 +201,9 @@ function replaceRespCalls(value, { ns, name }) {
|
|
|
183
201
|
|
|
184
202
|
const argsStr = value.slice(argsStart, i)
|
|
185
203
|
const args = splitTopLevelArgs(argsStr)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const mobileType = args.length >= 4 ? args[3] : desktopType
|
|
190
|
-
out += `${ns}.clamp_mb(${mobile}, ${ns}.min_px(${mobile}, ${mobileType}, mobile))`
|
|
204
|
+
const replacement = mapArgsToReplacement(args)
|
|
205
|
+
if (replacement != null) {
|
|
206
|
+
out += replacement
|
|
191
207
|
changed = true
|
|
192
208
|
} else {
|
|
193
209
|
// Not enough args: keep original call.
|
|
@@ -200,6 +216,33 @@ function replaceRespCalls(value, { ns, name }) {
|
|
|
200
216
|
return { value: out.trim(), changed }
|
|
201
217
|
}
|
|
202
218
|
|
|
219
|
+
function replaceAutofillCalls(value, { ns, name, vwName }) {
|
|
220
|
+
const respToken = `${ns}.${name}(`
|
|
221
|
+
const replacedResp = replaceFunctionCalls(value, respToken, (args) => {
|
|
222
|
+
if (args.length < 3) return null
|
|
223
|
+
const mobile = args[1]
|
|
224
|
+
const desktopType = args[2]
|
|
225
|
+
const mobileType = args.length >= 4 ? args[3] : desktopType
|
|
226
|
+
return `${ns}.clamp_mb(${mobile}, ${ns}.min_px(${mobile}, ${mobileType}, mobile))`
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const vwToken = `${ns}.${vwName}(`
|
|
230
|
+
const replacedVw = replaceFunctionCalls(
|
|
231
|
+
replacedResp.value,
|
|
232
|
+
vwToken,
|
|
233
|
+
(args) => {
|
|
234
|
+
if (args.length < 2) return null
|
|
235
|
+
const mobile = args[1]
|
|
236
|
+
return `${ns}.vw_mb(${mobile})`
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
value: replacedVw.value.trim(),
|
|
242
|
+
changed: replacedResp.changed || replacedVw.changed,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
203
246
|
function combineSelectors(parents, children) {
|
|
204
247
|
const out = []
|
|
205
248
|
for (const p of parents) {
|
|
@@ -216,7 +259,7 @@ function combineSelectors(parents, children) {
|
|
|
216
259
|
return out
|
|
217
260
|
}
|
|
218
261
|
|
|
219
|
-
function scanScssForAutofill_legacy(absFilePath, { ns, name }) {
|
|
262
|
+
function scanScssForAutofill_legacy(absFilePath, { ns, name, vwName }) {
|
|
220
263
|
const raw = fs.readFileSync(absFilePath, 'utf8')
|
|
221
264
|
const lines = raw.split(/\r?\n/)
|
|
222
265
|
|
|
@@ -275,7 +318,7 @@ function scanScssForAutofill_legacy(absFilePath, { ns, name }) {
|
|
|
275
318
|
value = value.replace(/\s!important\s*$/i, '').trim()
|
|
276
319
|
}
|
|
277
320
|
|
|
278
|
-
const replaced =
|
|
321
|
+
const replaced = replaceAutofillCalls(value, { ns, name, vwName })
|
|
279
322
|
if (!replaced.changed) continue
|
|
280
323
|
|
|
281
324
|
const finalValue = important
|
|
@@ -290,7 +333,7 @@ function scanScssForAutofill_legacy(absFilePath, { ns, name }) {
|
|
|
290
333
|
return rules
|
|
291
334
|
}
|
|
292
335
|
|
|
293
|
-
function scanScssForAutofill_ast(absFilePath, { ns, name }) {
|
|
336
|
+
function scanScssForAutofill_ast(absFilePath, { ns, name, vwName }) {
|
|
294
337
|
// Lazy-load optional deps so `init` can run before `npm install`.
|
|
295
338
|
/** @type {typeof import('postcss')} */
|
|
296
339
|
let postcss
|
|
@@ -332,7 +375,7 @@ function scanScssForAutofill_ast(absFilePath, { ns, name }) {
|
|
|
332
375
|
const property = String(child.prop)
|
|
333
376
|
let value = String(child.value ?? '').trim()
|
|
334
377
|
|
|
335
|
-
const replaced =
|
|
378
|
+
const replaced = replaceAutofillCalls(value, { ns, name, vwName })
|
|
336
379
|
if (!replaced.changed) continue
|
|
337
380
|
|
|
338
381
|
const finalValue = child.important
|
|
@@ -362,12 +405,12 @@ function scanScssForAutofill_ast(absFilePath, { ns, name }) {
|
|
|
362
405
|
return rules
|
|
363
406
|
}
|
|
364
407
|
|
|
365
|
-
function scanScssForAutofill(absFilePath, { ns, name }) {
|
|
408
|
+
function scanScssForAutofill(absFilePath, { ns, name, vwName }) {
|
|
366
409
|
try {
|
|
367
|
-
return scanScssForAutofill_ast(absFilePath, { ns, name })
|
|
410
|
+
return scanScssForAutofill_ast(absFilePath, { ns, name, vwName })
|
|
368
411
|
} catch (e) {
|
|
369
412
|
// Fallback for edge cases where parser can't handle a file.
|
|
370
|
-
return scanScssForAutofill_legacy(absFilePath, { ns, name })
|
|
413
|
+
return scanScssForAutofill_legacy(absFilePath, { ns, name, vwName })
|
|
371
414
|
}
|
|
372
415
|
}
|
|
373
416
|
|
|
@@ -388,7 +431,7 @@ function generateAutofillScss(cfg, collectedRules) {
|
|
|
388
431
|
const header = `@use "./responsive" as ${ns};
|
|
389
432
|
|
|
390
433
|
// Generated by scss-kit from ${CONFIG_NAME}
|
|
391
|
-
// Source: scanned ${ns}.resp(pc, mobile,
|
|
434
|
+
// Source: scanned ${ns}.resp(pc, mobile, desktopType[, mobileType]) and ${ns}.vw(pc, mobile) markers in scss.
|
|
392
435
|
// Do not edit this file directly; re-run: npm run scss-kit:responsive:generate
|
|
393
436
|
\n`
|
|
394
437
|
|
|
@@ -529,10 +572,11 @@ function toScssMap(obj) {
|
|
|
529
572
|
function generateResponsiveScss(cfg) {
|
|
530
573
|
const mobileMap = toScssMap(cfg.coefficients.mobile)
|
|
531
574
|
const desktopMap = toScssMap(cfg.coefficients.desktop)
|
|
575
|
+
const mobileFloorMap = toScssMap(cfg.floors?.mobile ?? {})
|
|
576
|
+
const desktopFloorMap = toScssMap(cfg.floors?.desktop ?? {})
|
|
532
577
|
|
|
533
578
|
return `@use "sass:map";
|
|
534
579
|
@use "sass:math";
|
|
535
|
-
@use "sass:list";
|
|
536
580
|
@use "sass:meta";
|
|
537
581
|
|
|
538
582
|
// Generated by scss-kit from ${CONFIG_NAME}
|
|
@@ -546,6 +590,10 @@ $coef-mobile: ${mobileMap};
|
|
|
546
590
|
|
|
547
591
|
$coef-desktop: ${desktopMap};
|
|
548
592
|
|
|
593
|
+
$floor-mobile: ${mobileFloorMap};
|
|
594
|
+
|
|
595
|
+
$floor-desktop: ${desktopFloorMap};
|
|
596
|
+
|
|
549
597
|
@function _to-px($value) {
|
|
550
598
|
@return if(math.is-unitless($value), $value * 1px, $value);
|
|
551
599
|
}
|
|
@@ -571,25 +619,36 @@ $coef-desktop: ${desktopMap};
|
|
|
571
619
|
@error "Unknown coef type: #{$type}";
|
|
572
620
|
}
|
|
573
621
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
622
|
+
@function _floor($type, $range: mobile) {
|
|
623
|
+
$table: if($range == desktop, $floor-desktop, $floor-mobile);
|
|
624
|
+
@if map.has-key($table, $type) {
|
|
625
|
+
@return map.get($table, $type);
|
|
626
|
+
}
|
|
627
|
+
@return null;
|
|
628
|
+
}
|
|
577
629
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
@return math.
|
|
630
|
+
// 根据设计稿 px + 系数推导 clamp() 最小值:max(design * coef, floor)。
|
|
631
|
+
// $type 可传命名类型(字符串)或直接传数字系数(跳过系数表查询,不使用 floor)。
|
|
632
|
+
@function min_px($value, $type, $range: mobile, $override-coef: null, $override-floor: null) {
|
|
633
|
+
$v: _to-px($value);
|
|
634
|
+
|
|
635
|
+
// 直接传数字系数:跳过类型表,直接以系数计算
|
|
636
|
+
@if meta.type-of($type) == number {
|
|
637
|
+
$min: $v * $type;
|
|
638
|
+
@if $override-floor != null {
|
|
639
|
+
@return math.max($min, _to-px($override-floor));
|
|
588
640
|
}
|
|
641
|
+
@return $min;
|
|
589
642
|
}
|
|
590
643
|
|
|
591
644
|
$c: if($override-coef == null, coef($type, $range), $override-coef);
|
|
592
|
-
|
|
645
|
+
$min: $v * $c;
|
|
646
|
+
|
|
647
|
+
$f: if($override-floor != null, _to-px($override-floor), _floor($type, $range));
|
|
648
|
+
@if $f {
|
|
649
|
+
@return math.max($min, $f);
|
|
650
|
+
}
|
|
651
|
+
@return $min;
|
|
593
652
|
}
|
|
594
653
|
|
|
595
654
|
// 生成桌面段 clamp:min + calc(pc * var(--px-to-vw)) + pc
|
|
@@ -606,6 +665,29 @@ $coef-desktop: ${desktopMap};
|
|
|
606
665
|
@return clamp(#{$min}, calc(#{$n} * var(--px-to-vw-mb)), #{$v});
|
|
607
666
|
}
|
|
608
667
|
|
|
668
|
+
// 直接输出 PC 段 vw,并用设计稿 px 做上限兜底。
|
|
669
|
+
@function vw_pc($pc) {
|
|
670
|
+
$v: _to-px($pc);
|
|
671
|
+
$n: _to-num($pc);
|
|
672
|
+
@return min(calc(#{$n} * var(--px-to-vw)), #{$v});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// 直接输出移动段 vw(无 clamp)。
|
|
676
|
+
@function vw_mb($mobile) {
|
|
677
|
+
$n: _to-num($mobile);
|
|
678
|
+
@return calc(#{$n} * var(--px-to-vw-mb));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// vw(): spacing-first helper(间距优先,默认不设最小值)
|
|
682
|
+
// - PC: min(vw, px)
|
|
683
|
+
// - Mobile: 纯 vw(通过 autofill 覆盖生成)
|
|
684
|
+
@function vw($pc, $mobile) {
|
|
685
|
+
@if meta.type-of($pc) != number or meta.type-of($mobile) != number {
|
|
686
|
+
@error "vw() expects numeric px values for both pc and mobile";
|
|
687
|
+
}
|
|
688
|
+
@return vw_pc($pc);
|
|
689
|
+
}
|
|
690
|
+
|
|
609
691
|
// resp():
|
|
610
692
|
// - arg3: desktop type
|
|
611
693
|
// - arg4 (optional): mobile type, defaults to desktop type
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"type": "object",
|
|
26
26
|
"properties": {
|
|
27
27
|
"function": {"type": "string"},
|
|
28
|
+
"vwFunction": {"type": "string"},
|
|
28
29
|
"mobileMax": {"type": "integer", "minimum": 1},
|
|
29
30
|
"scanDirs": {"type": "array", "items": {"type": "string"}},
|
|
30
31
|
"output": {"type": "string"},
|
|
@@ -47,6 +48,13 @@
|
|
|
47
48
|
"mobile": {"type": "object", "additionalProperties": {"type": "number"}},
|
|
48
49
|
"desktop": {"type": "object", "additionalProperties": {"type": "number"}}
|
|
49
50
|
}
|
|
51
|
+
},
|
|
52
|
+
"floors": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"properties": {
|
|
55
|
+
"mobile": {"type": "object", "additionalProperties": {"type": "string"}},
|
|
56
|
+
"desktop": {"type": "object", "additionalProperties": {"type": "string"}}
|
|
57
|
+
}
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
}
|