微信小程序 Skyline 样式守卫

SKILL开发
开发 未标注权限 许可证不明确
安装这个技能

写/改 Taro + 微信小程序 Skyline 渲染项目的 WXSS 时使用。覆盖 Skyline 样式支持黑名单(radial-gradient 只支持 circle 不支持椭圆、尺寸只支持 px、background-image 最多 2 个值、box-shadow 不支持叠加、backdrop-filter/filter 只支持单 function、无 inline-block、无属性选择器等)、默认值差异(border-width 默认 3、box-sizing 默认 border-box、display 默认 flex)、布局与滚动陷阱(flex-wrap+gap 首行错位少放一张、scroll-view 偏移被 setData 重置、横滚 scroll-into-view 不生效须改用受控 scroll-left+交替 1px 偏移、scroll-view 裁掉撑满高度子项的 box-shadow、scroll-into-view 常驻生效),以及不依赖 devtools 的 SCSS 编译校验脚本(含渐变违规自动断言)。触发词:Skyline、小程序样式不生效、背景没渲染、渐变不生效、网格错位、chip 弹回首位、横向标签栏回弹/缩回最左边、scroll-left 不生效、scroll-into-view 没反应、Taro 样式、wxss warning、glass-easel。

一句话选型结论

按资料字段整理

微信小程序 Skyline 样式守卫是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_4edac78e/taro-skyline-wxss
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_4edac78e仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791722674
正文体量19.1 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
微信小程序 Skyline 样式守卫当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

19.1 KB
模型实际读到的说明书原文,来自技能仓库

Taro + Skyline WXSS 守卫

改 Skyline 项目的样式前先过一遍这里的黑名单,不要靠猜——很多在 WebView 里人畜无害的写法在 Skyline 里会被静默丢弃(不报错、不警告,只是没效果)。

一、最容易踩的 5 个坑

1. 渐变 / 背景 ⚠️ 最容易中招,全是"静默丢弃"

// ❌ 一条声明里叠 5 张图 → 整条被丢弃,页面只剩纯色底
background-image: radial-gradient(...), radial-gradient(...), radial-gradient(...);
// ❌ 两个长度 = 椭圆 → Skyline 只支持 circle,整条被丢弃
background-image: radial-gradient(464px 392px at -8% 4%, #ffdccb 0%, rgba(255,220,203,0) 68%);

Skyline 的硬限制(官方文档只写"radial-gradient() ✓",细节在社区/官方 wxss skill 里):

项 限制
background-image / mask-image 最多 2 个值(不是"支持多层")
background-position 最多 2 组值
background-repeat / background-size 不支持多组值
radial-gradient 形状 只支持 circle,不支持 ellipse(写两个长度 = 椭圆 → 整条丢弃)
radial-gradient 尺寸 只支持 px(写 rpx 不生效)
radial-gradient 色标 只支持 %
linear-gradient 色标位置 支持 % 和固定长度(px / rpx 等)
conic-gradient 完全支持,无额外限制

Taro 特有陷阱:pxtransform 会把 px 编译成 rpx,所以"尺寸只支持 px"这条在 Taro 项目里等于没法写尺寸。
解法:索性不写渐变尺寸,用默认的 farthest-corner,把范围交给节点自身的 width/height(布局属性用 rpx 没问题)。

<view class="glow-layer glow-1" />
<view class="glow-layer glow-2" />
// 只负责定位与穿透,具体位置/尺寸/渐变各页面自己给
.glow-layer { position: absolute; pointer-events: none; }

// 一团光晕 = 一个节点:sizeless circle + 节点自身定尺寸,中心用 % + translate 对齐
@mixin glow($rgb, $size, $x, $y) {
  left: $x;
  top: $y;
  width: $size;
  height: $size;
  transform: translate(-50%, -50%);
  background-image: radial-gradient(circle, rgba($rgb, 1) 0%, rgba($rgb, 0) 70%);
}
.glow-1 { @include glow(#ffdccb, 928px, -8%, 4%); }

父容器要 position: relative,内容要 position: relative; z-index: 1 压上去
(Skyline 的 z-index 只作用于兄弟节点,所以"光晕节点在前、内容在后"正好够用)。

正方形容器的 farthest-corner 半径 = 边长/√2 ≈ 0.707×边长,色标停在 70% 时可见半径≈边长/2,刚好等于设计稿的半径。

配套注意:渐变的透明端写 rgba(同一个颜色, 0),不要写 transparent——后者等价 rgba(0,0,0,0),收尾会泛灰边。

同一个坑的另一面:box-shadow 不支持多个叠加。

> 如果 radial-gradient 连 circle 形式都不生效(老版本客户端),退路是:① 用 linear-gradient 斜向近似角落染色(linear 支持度最好);② 预渲染一张 PNG 用 url() 贴上去。

2. filter / backdrop-filter 只支持单个 function

不支持 drop-shadow、不支持 url()。backdrop-filter 还有"与 opacity 混合表现异常""blur 某些情况不一致"的已知问题。
所以毛玻璃方案直接放弃,改用不透明底 + 上浮阴影模拟。

3. 布局类

  • inline / inline-block 不支持(inline-block 仅能在 text 组件内嵌套使用)
  • position: sticky 不支持 → 用 sticky-header / sticky-section 组件替代
  • overflow: scroll 不支持 → 只能用 scroll-view
  • 通配选择器 * {}、属性选择器 [attr] {} 不支持
  • currentColor 不支持
  • 伪元素只支持 ::before / ::after
  • env() 只支持 safe-area-inset-* 系列

3.1 scroll-view 必须显式给宽高 ⚠️

Skyline 下 scroll-view 默认不会按内容撑开,必须自己写 width + height,否则高度塌成 0、内容看不见。
(除非在 rendererOptions.skyline 里开 enableScrollViewAutoSize: true,这是全局开关,改了会影响所有 scroll-view,谨慎。)

所以这类"一行标签"的容器要把高度定死,别指望内容撑开:

.cat-bar { width: 100%; height: 64px; }        /* scroll-view 本体 */
.cat-chip { box-sizing: border-box; height: 64px; }  /* 子项也用同一个高度,避免被裁切/留白 */

> 注意 defaultContentBox: true 的项目里默认是 content-box,height 不含 padding/border,
> 想让 64px 就是最终高度得显式写 box-sizing: border-box。

3.2 横向滚动标签栏的正确写法 ⚠️

社区经典写法是 white-space: nowrap + 子项 display: inline-block。
Skyline 不支持 inline-block,照抄会把每个 chip 压成整行宽,表现为「点 A 命中 B」、
布局竖着堆——很多人以为是 scroll-view 的 bug,其实是 inline-block 没生效。

Skyline 正确写法:内层 flex 行 + 子项 flex-shrink: 0,靠子项溢出撑出可滚宽度。

<scroll-view class="cat-bar" :scroll-x="true" :show-scrollbar="false" :enhanced="true">
  <view class="cat-row">
    <view class="cat-chip" v-for="c in tabs" :key="c.value" @tap="pick(c.value)">
      <text class="cat-chip-text">{{ c.label }}</text>
    </view>
  </view>
</scroll-view>
.cat-bar { width: 100%; height: 64px; }
.cat-row { display: flex; flex-direction: row; align-items: center; }
.cat-chip { flex-shrink: 0; box-sizing: border-box; height: 64px; display: flex; align-items: center; padding: 0 28px; }

横向 scroll-view 嵌在纵向 scroll-view 里是常规用法,没问题。

3.3 横向 scroll-view 的两个连带坑(都源于"内容一变就重置/裁剪")⚠️

① 滚动位置会被同一次 setData 冲掉:切换选中项时子项 class 变化、或 v-if 切换结构,横条会缩回最左边。用户的原话常是「点了靠后的标签就弹回首位」,注意是回弹——先滚过去又被弹回来。

⚠️ 别用 scroll-into-view 救(实测踩过):Skyline 的横滚 scroll-view 上它不生效,条子照旧缩回最左边(纵向的"回顶"能用它,横滚不行)。而且它是常驻生效属性——值保持非空时,之后任何 setData 都会重新滚到该节点。加上 v-for 出来的子项会被包在 Fragment 里,id 查不查得到还得看渲染器实现,又多一层不确定。

正确做法:受控 scroll-left 像素级定位。 它是普通属性,值一变就精确滚到该像素位置,不查 id、不看节点结构。

const scrollLeft = ref(0)
let liveLeft = 0, syncTimer = null, nudge = 0

/** 滚动中零 setData(会打断原生滚动),停下 220ms 再把真实位置写回绑定值 */
function onBarScroll(e) {
  liveLeft = (e.detail && e.detail.scrollLeft) || 0
  clearTimeout(syncTimer)
  syncTimer = setTimeout(() => {
    if (Math.abs(scrollLeft.value - liveLeft) > 1) scrollLeft.value = liveLeft
  }, 220)
}

/** 让选中项可见;已经看得见就不滚 */
function keepVisible(value) {
  const idx = tabs.findIndex((t) => t.value === value)
  if (idx < 0) return
  const q = Taro.createSelectorQuery()
  q.select('.bar').boundingClientRect()
  q.select('.bar').scrollOffset()
  q.selectAll('.chip').boundingClientRect()      // ⚠️ 用 selectAll + 下标,别用 #id
  q.exec((res) => {
    const bar = res && res[0], off = res && res[1], chips = res && res[2]
    if (!bar || !off || !chips || !chips[idx]) return
    const cur = off.scrollLeft || 0
    const pad = 22                               // 单位是 px:这两个 API 都返回真实 px,不是 rpx
    const left = chips[idx].left - bar.left + cur   // 子项在内容坐标系里的位置
    const right = left + chips[idx].width
    let target = cur
    if (left < cur + pad) target = Math.max(0, left - pad)
    else if (right > cur + bar.width - pad) target = right - bar.width + pad
    else return                                  // 已经看得见,不动
    nudge = nudge ? 0 : 1                        // ⚠️ 见下,不能省
    scrollLeft.value = Math.round(target) + nudge
  })
}

⚠️ 那个交替 1px 偏移不能省,否则就是"回弹":Skyline 只在 scroll-left 值发生了变化的那次渲染才应用滚动。而 loading=false 会把位置重置为 0,此时若重算出的目标值和绑定值恰好相同(都是 300),Vue 不触发更新 → 条子停在最左边,表现为「滚过去了又被弹回来」。每次写入交替加 0/1px 保证值一定在变;1px 在 335px 可视区里看不出来,用整数是为了避免被渲染器取整丢掉。

量坐标的时机也要留神:位置重置是异步的,补定位(如 loading 结束后)要隔一拍(setTimeout(..., 60))再量,否则量到的是重置前的坐标、算出来"不用滚"就真的不动了。左偏移 + 当前 scrollLeft 这个式子对"位置有没有被重置"两种情形都成立,可以放心用。

② 会裁掉"撑满它高度"的子项的 box-shadow:scroll-view 高 64px、子项也高 64px 时,向下偏移的投影整段落在裁剪区之外,一行都画不出来(实测:子项底边往下第 2 行就是纯背景色)。
要么别写这个阴影(写了是无效声明,还会让人误以为有),要么给 scroll-view 加高 / 留 padding-bottom 腾出裁剪空间。
判据:同一页里不贴裁剪边的元素(如网格卡片)阴影能正常渲染 → 说明不是"不支持 box-shadow",而是"被裁了"。

3.4 多列网格:不要用 gap + 百分比宽度 ⚠️

flex-wrap: wrap + gap 在 Skyline 下,首行会被多顶出一段 gap(实测截图几何:首行整体右移恰好一个 gap 的距离,并因此少放一张卡 → 表现为「第一行 2 张、第二行 3 张」,后面几行正常)。
即使算术上"刚好放得下"也不安全:3×31% + 2×22px = 667.1 ≤ 670 只余 2.9px,一点取整误差就换行。

稳妥写法——不用 gap,用 margin 撑间距,并留 ≥1% 余量:

.grid { display: flex; flex-wrap: wrap; }
.grid-item { width: 29.5%; margin: 0 11px 22px; }   /* 3×(29.5% + 22px) = 658.9 ≤ 每屏 670px */

> 想做到"左右贴齐 + 间距精确"就要让每行最后一个不吃 margin(行容器切行、或 :nth-child(3n) 清零)——
> 但 :nth-child 要 Skyline 1.3.3 / 微信 8.0.50+,行容器/负 margin/calc 在 Skyline 上的支持也没背书。
> 多数情况下选"左右各内缩半格间距(如 11px)"的稳方案更划算。

4. 默认值和 web 不一样

属性 Skyline 默认 Web 默认
border-*-width 3 medium
border-*-color black currentcolor
display flex inline/block
box-sizing border-box content-box

后两个项目里常通过 rendererOptions.skyline.defaultDisplayBlock / defaultContentBox 改回 web 语义,改样式前先看 app.json 有没有开。

5. wxss 前缀警告

Unsupported Property. Style Text: -webkit-box-sizing... 这类警告源码里往往没有前缀——是 Taro mini 构建默认开的 autoprefixer 注入的。关掉:

// config/index.js
mini: { postcss: { autoprefixer: { enable: false } } }  // h5 端别动

二、改完必须做的编译校验(不依赖 devtools)

沙箱里 node_modules/.bin/sass 可能被执行策略拦(exit 66 + 乱码 stderr),但 sass 的 JS API 可用。
脚本必须写到项目目录内,否则 require('sass') 解析不到 node_modules。

// save as <project>/.scss_check.cjs
const sass = require('sass'), fs = require('fs'), path = require('path')
const ROOT = path.join(process.cwd(), 'src')
const strip = (c) => c.replace(/\/\*[\s\S]*?\*\//g, '')   // 断言前必须去注释,否则注释里的示例代码会误报
function walk(d, o = []) { for (const e of fs.readdirSync(d, { withFileTypes: true })) { const p = path.join(d, e.name); e.isDirectory() ? walk(p, o) : ((e.name.endsWith('.vue') || e.name.endsWith('.scss')) && o.push(p)) } return o }
let fail = 0
for (const f of walk(ROOT)) {
  const src = fs.readFileSync(f, 'utf8'), rel = path.relative(ROOT, f)
  let blocks = []
  if (f.endsWith('.vue')) { const re = /<style[^>]*lang=["']scss["'][^>]*>([\s\S]*?)<\/style>/g; let m; while ((m = re.exec(src))) blocks.push(m[1]) }
  else blocks.push(src)
  if (!blocks.length) continue
  blocks.forEach((code, i) => {
    const tag = rel + (blocks.length > 1 ? ' #' + (i + 1) : '')
    try {
      const raw = sass.compileString(code, {
        loadPaths: [path.dirname(f), ROOT, path.join(ROOT, 'styles')],
        style: 'expanded',
        logger: { warn(msg) { throw new Error('WARN: ' + msg) } }   // 把废弃警告当失败,@import 之类藏不住
      })
      const css = strip(raw.css)
      console.log(`OK ${tag} -> ${raw.css.length} bytes (去注释后 ${css.length})`)
      // 断言 1:background-image 最多 2 个值
      for (const line of css.split('\n')) {
        if (/background-image\s*:/.test(line)) {
          const n = (line.match(/gradient\(/g) || []).length
          if (n > 2) { fail++; console.log(`   !! ${tag} 一条 background-image 塞了 ${n} 张图(Skyline 最多 2)`) }
        }
      }
      // 断言 2:radial-gradient 只能是 circle + px 尺寸
      for (const g of css.matchAll(/radial-gradient\(([^;]*)\)/g)) {
        const head = g[1].split(',')[0]
        if (/\d+(px|rpx)\s+\d+(px|rpx)/.test(head)) { fail++; console.log(`   !! ${tag} 椭圆尺寸「${head.trim()}」,Skyline 只支持 circle`) }
        if (/\d+rpx/.test(head)) { fail++; console.log(`   !! ${tag} rpx 尺寸「${head.trim()}」,Skyline 只支持 px`) }
      }
    } catch (e) { fail++; console.log(`FAIL ${tag}\n     ` + String(e.message).split('\n').slice(0, 5).join('\n     ')) }
  })
}
console.log('\n=== ' + (fail === 0 ? '全部通过' : fail + ' 处问题') + ' ===')

运行:node .scss_check.cjs(用 binary_context 里当前的 managed node 绝对路径),跑完删掉脚本。

顺手抓到的一个真问题:共享 tokens 文件的注释会被复制

被 @use 的 tokens 文件里如果用 /** ... */(CSS 注释),sass 会原样输出到每一个引用者的产物。
一个文件 @use 它就多一份注释字节。改成 //(SCSS 行注释)sass 会丢弃,产物立刻变干净:
某项目 tokens.scss 用 /** */ 时编译出 689 字节、改 // 后 0 字节;引用它的 nav-bar.vue 从 1201 → 511 字节。

结论:styles/tokens.scss 这类共享文件一律用 // 注释,且只放变量 + @mixin,不写选择器规则。

配套:类名对账(Skyline 下尤其值得做)

Skyline 静默丢弃不合规样式,所以"模板用了某个 class 但样式没定义"也不会报错,只是看起来没效果。
写个脚本对账,能提前抓到。三个坑:

  1. 提静态类名的正则要加负向后顾:/(?<!:)class="([^"]*)"/g。
    用 /class="([^"]*)"/g 会把 :class="..." 的内容也匹配进来(:class=" 本身含 class="),
    于是表达式里的变量名(如 activeCat、showTopBtn)被当成类名,刷出一堆假阳性。
  2. :class 只取单引号字符串字面量:'chip-' + (tone || 'plain') 会切成 chip- 和 plain 两个片段,
    不是真实类名——把 ^(chip-|tint-|glow-)$ 这类已知前缀片段列进白名单排除,最终类是拼接后的 chip-blue。
  3. 要双向查:既查"模板用了但样式没定义",也查"色板/常量里可能出现、但样式没定义"。
    后者抓到过真 bug:常量表新增了 pink,但 .chip-pink 忘了写,那个分类就落回默认底色。

比"大括号配平"之类的静态检查可靠得多,而且能在没有微信开发者工具的情况下提前抓到废弃警告和语法错误。

三、长度换算(改数值前必须确认)

先看 config/index.js 的 pxtransform 配置确定比例:

  • 本项目现状:1px(code) = 1rpx(编译产物里 464px → 464rpx),
    即 code 数值 = 375 设计稿数值 × 2。
  • 从 375 宽的设计稿搬数值进代码时,记得乘以 2,否则所有尺寸会小一半。

判断方法:改完看编译产物 wxss,464px 出来是 464rpx 就是 1:1,是 928rpx 就是 2:1。

四、其它 Skyline 行为陷阱

  • scroll-into-view 是常驻生效属性:只要保持非空,之后任何 setData 都会重新滚到该节点,
    表现是"往下滚一点就被弹回顶部"。用法:设值 → nextTick → 定时器(约 400ms)后清空。
    它只在纵向(页面/竖滚 scroll-view)上可用;横滚 scroll-view 上实测不生效,见 § 3.3 ①。
  • scroll-left 只在值变化时应用:想用受控 scroll-left 精确回滚位置(横条保持选中项可见、列表恢复位置),
    每次写入都要让值真的变(交替 1px 偏移即可),否则"位置被重置为 0 + 目标值与旧值相同"就什么都不会发生。同 § 3.3 ①。
  • 滚动过程中 setData 会打断原生滚动:滚动事件要跨线程回调逻辑层,所以
    @scroll 里不要直接 setData,用 ~120ms 防抖,等滚动停止再更新 UI(如"回到顶部"按钮)。
  • 不支持的客户端会 fallback 到 WebView:所以两套渲染都要能看。glassEaselWebview 是 app.json/页面 json 的顶层字段(与 componentFramework 并列,不能放 window 里),WebView 下启用 glass-easel 运行时需基础库 ≥ 3.8.12。

五、图标怎么办

Skyline 不支持 <u-svg> 之类的 svg 组件。图标用这三条路:
1. CSS 画(圆点、色块、border 三角)
2. 单色字符(› ↑ ‹ 之类,注意字体渲染差异)
3. <image> 载入 png/svg 资源

常见问题

5 问
答案由本站依据条目字段整理
微信小程序 Skyline 样式守卫怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_4edac78e/taro-skyline-wxss。安装方式为未标注。装完刷新技能列表或重开客户端。

微信小程序 Skyline 样式守卫支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

微信小程序 Skyline 样式守卫权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

微信小程序 Skyline 样式守卫收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

微信小程序 Skyline 样式守卫用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。