Community图像github.com

颗粒渐变

给 CSS 渐变和背景铺上一层胶片颗粒,让它们像印刷品而不是塑料,并压掉色带。

颗粒渐变 是什么?

技能会扫描整个站点或单块背景,列出填充,再让人挑选要处理的那些。它量过每条渐变,自己决定混合方式和用量,在真实页面上给出前后对比,并写出能适应 HDR、OLED、高对比度和打印的 CSS。渐变显得廉价、有条纹或过于光滑时用它。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Farhad1618/grainy-gradients/tree/HEAD/skills/grainy-gradients

Installed? Explore more 图像 skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

颗粒渐变 是做什么的?

平滑的数字渐变看上去很“计算机化”。一层精细的噪点能让它们呈现印刷品或胶片质感,同时还能隐藏色带(8 位颜色从一个等级过渡到另一个等级时出现的可见条纹)。

您负责完成所有工作。用户只需查看并选择。 永远不要询问混合模式、不透明度或数值。用户只需做三种决定,其他一概不问:

  1. 哪些背景(仅当您扫描了整个站点时)。
  2. 是否先查看前后对比,还是直接应用。
  3. 在预览选择中,哪个效果最好。

思考方式

  • 颗粒是纹理,不是颜色。 它不能使图像变亮、变暗或着色。噪点以中灰色为中心,因此亮度变化应接近 0。它不能用于纯黑或纯白,报告中会注明这一点。

  • 测量可见部分,而不是不透明度。 可见颗粒对应 L*(CIELAB 亮度,范围 0–100)的分布,L* 是感知性的,因此相同的数值在不同颜色上看起来大致相同。低于约 0.45 时,几乎无法察觉。目标是微弱 / 中等 / 强烈分别为 0.5 / 0.8 / 1.4。

  • 在从亮到暗的渐变上使用均衡模式。 覆盖模式会将大部分颗粒集中在较亮部分。均衡模式(通常是柔光)则能更均匀地分布颗粒。人们通常更偏好它。

  • 颗粒属于背景。 它位于背景的正上方,在所有其他内容之下,并随背景移动。如果背景淡入,颗粒也会随之淡入。

  • 它适应每位访客的屏幕。 网站无法区分 OLED 和 LCD,但 CSS 可以识别 HDR 屏幕(大多数 OLED 手机、XDR 笔记本)和高对比度设置。CSS 包含以下媒体查询:

    • 深色背景在 HDR 屏幕上颗粒稍少
    • 高对比度设置下颗粒更少
    • 打印时关闭颗粒

    全部由 CSS 实现,真实站点无需脚本。

  • 跳过颗粒无法改善的情况:

    • 小元素(按钮、徽章、图标)
    • 渐变文字
    • 照片上的透明遮罩和淡入
    • 条纹和方格图案

1. 用户以哪种方式提出需求?

  • 单个位置(“主视觉看上去很平”、“为页脚添加颗粒”):转到 查找单个背景。
  • 整个站点或应用(“检查我的整个代码库”、“检查我所有的渐变”、“处理整个站点”):转到 扫描整个站点。

2a. 查找单个背景

找到实际绘制背景的元素。检查:

  • 元素本身及其 ::before 和 ::after
  • 元素内部或后方的全尺寸图层(区块通常通过独立的绝对定位 <div> 获取颜色)

使用浏览器工具,列出候选元素:

[...document.querySelectorAll('#section, #section *')].flatMap(e => [null, '::before', '::after'].map(p => [e, p, getComputedStyle(e, p)]))
  .filter(([, p, c]) => (!p || c.content !== 'none') && (c.backgroundImage !== 'none' || c.backgroundColor !== 'rgba(0, 0, 0, 0)'))
  .map(([e, p, c]) => `${e.tagName.toLowerCase()}${e.id ? '#' + e.id : ''}.${[...e.classList].join('.')}${p || ''} | ${c.position} | opacity ${c.opacity} | ${(c.backgroundImage !== 'none' ? c.backgroundImage : c.backgroundColor).slice(0, 80)}`)

如果没有浏览器,请阅读 CSS。无论哪种方式,在 CSS 中搜索该背景的其他版本:媒体查询(手机布局通常会更换渐变)和深色模式规则。在步骤 5 中会用到它们。

然后转到 步骤 3。

2b. 扫描整个站点

node <本技能文件夹>/scripts/scan.mjs <项目文件夹>

它会在大约一秒内读取样式表(规则、媒体查询、自定义属性)、标记和组件(Tailwind 渐变类、内联样式、CSS-in-JS)。将结果分为四组:

  • 好的候选: 大面积背景。
  • 值得一看: 代码中无法确定大小。
  • 已有颗粒。
  • 跳过, 并注明原因。

它还会列出每个样式表加载自哪些页面。添加 --json 以获取完整数据。

自己整理“值得一看”的项。 在展示列表之前,根据您的观察进行排序。使用浏览器工具在页面上测量其大小。否则阅读标记。将每一项移到“好的候选”或“跳过”。不要向用户发送您本可以检查的列表。

然后展示列表, 简短,每项一行,用通俗语言表达,按页面分组。例如:

我找到了 6 个可以添加颗粒的背景:

  1. 首页,导览区块: 淡入的深色渐变
  2. 首页,主视觉: 深色径向光晕
  3. “可持续发展”页,页眉: 绿黑渐变
  4. “招聘”页,“此处制造”横幅: 橙黑渐变

跳过 12 个:按钮、渐变文字、照片遮罩和条纹图案。 “报价”页面已有颗粒,但数量太少无法看到,因此我会重做。

然后让他们选择。 如果您有多选题工具(如 Claude Code 中的 AskUserQuestion),请使用它:

  • 询问“哪些背景应该添加颗粒?”并将 全部添加(推荐) 放在首位。
  • 对于最多 3 个其他项,将每个列为一个选项,并启用多选。
  • 如果超过 4 个,先询问“全部添加,还是选择?”;如果回答“选择”,则每次以最多 4 项进行多选提问。

如果没有该工具,请让他们回复“全部”或数字。

3. 询问:前后对比,还是直接应用?

对每个选中的背景统一询问:“您想先看到前后对比效果,还是直接为每个应用最佳效果?”如果有多选题工具,请使用它,选项为 先给我看看(推荐) 和 直接应用。

下面的工具需要 Node 22+ 和机器上的 Chrome、Chromium、Edge 或 Brave(自动查找;CHROME_PATH 可覆盖)。它们以无头模式打开页面,因此站点必须可达:正在运行的开发服务器、测试 URL,或用于纯 HTML 的 file://。不会向站点添加任何内容。

4. 展示前后对比

先给我看看:审阅页面

在后台运行它(它会等待用户),每个页面使用一个 --url,后跟该页面的选择器:

node <技能>/scripts/review.mjs --url http://localhost:3000/ --selector ".hero" --selector ".footer-band" \
                                --url http://localhost:3000/about --selector ".about-header"
  • 它会自动截图。 对于每个背景,它会拍摄页面当前状态以及每种选项下的照片,还包括 HDR 屏幕和高对比度设置下的效果。每个页面大约需要 10–30 秒。
  • 然后它会打开一个本地审阅页面 并打印其地址;同时将链接提供给用户。
  • 每个背景会显示一个卡片,包含:
    • 前后对比滑块
    • 色带放大,可放大色带最明显的区域(缩小的图片中细颗粒不可见)
    • 以标签页形式展示选项,推荐选项标有星号
    • 屏幕切换
    • 测量数值和包含报告的“原因”
  • “完成,发送我的选择” 会将选择直接发送回来。无需复制粘贴。

用一句话告诉他们该做什么:“拖动每张图片,尝试放大,为每个背景选择一项,然后按完成。”当命令退出时,输出包含选择结果、每个背景对应的 CSS,以及每个背景的基准文件夹,用于步骤 7 中的检查。

同时在聊天中展示一张图片。 每个基准文件夹包含 compare-<选项>.png:

  • 完整的前后对比
  • 放大的细节
  • 同一细节经过对比度拉伸,使色带显示为条纹、颗粒显示为噪点
  • 测量数值

将推荐的那个发送给用户(如果有文件发送工具则使用它;否则提供路径)。这是效果真实的证据。

直接应用

首先拍摄基准和照片,以便后续检查和展示:

node <技能>/scripts/shoot.mjs --url http://localhost:3000/ --selector ".hero" --option recommended --out ./grain-shots

它会为每张照片输出一行信息(颗粒、亮度、条纹宽度),并将 <出>/<选择器>/ 写入 report.json(报告、每种选项的 CSS、推荐选项)和 compare-<选项>.png。使用 report.json 中的推荐 CSS。

没有 Chrome,或站点无法通过无头模式访问

退回到页面内预览:将 dist/grain-page.js 复制到站点自有脚本旁边(检查其是否可加载,状态码 200 而非 404),添加 <script src="/js/grain-page.js" data-grain=".hero | .footer" defer></script>,并让用户使用 ?grain 参数打开页面。它会在真实页面上显示相同的选项,并带有一个可拖动的滑块;复制选择 会复制 grain: <选项> 以及每个背景的外观,您可以将其通过管道传递给 node <技能>/scripts/grain.js --look - 以生成 CSS。之后移除标签和文件。

如果完全没有浏览器,请自行编写外观并在其上运行 grain.js:

{ "selector": ".hero", "kind": "gradient", "shape": "linear",
  "colours": [{ "colour": "#323435", "pos": 0 }, { "colour": "#141617", "pos": 1 }],
  "lengthPx": 900, "background": "linear-gradient(...)", "behind": "#ffffff",
  "solidBase": true, "position": "relative", "freePseudo": "::after", "moves": null }

或 node grain.js --colours "#323435,#2a2c2d@24%,#141617" --length 900 --selector ".hero" [--shape radial] [--fade-over "#fff"] [--option even]。

引擎自动决定,您无需操心

情况操作
元素上的渐变或纯色单层: 噪点成为元素的顶层背景层(使用 background-blend-mode),保存在 --grain 自定义属性中。在每个背景列表中添加一个值,因此页面自身的大小、位置和混合保持不变。不会覆盖文字。
背景淡入同一单层,因此颗粒随之淡入。报告会给出淡入各阶段的颗粒情况。
透明渐变,下方无实色使用自身图层(一个空闲的 ::after/::before,或一个子元素),以便与穿透显示的内容混合,而不是变灰。
照片或图像背景一个空闲的伪元素,如果两者都被占用则使用子元素。
<img>/<video> 填充元素,或伪元素上的背景在隔离宿主中添加 z-index: -1 的子元素。
纯黑或纯白覆盖和柔光无法在此处添加颗粒,因此使用屏幕或正片叠底,并带有轻微亮度偏移,并在报告中说明。
跨渐变颗粒不均匀如果均衡模式明显更均匀,则推荐使用。当它与中等相同时,均衡模式会被隐藏。
色带风险高说明所选颗粒是否能打碎条纹。强烈模式效果更强。
同一背景中已有旧噪点层替换它。如果旧噪点在其他位置(例如伪元素),说明需要移除它。
WebGL/Canvas 场景中的部分背景(3D 地面、着色器背景)画布后的 CSS 颗粒无法到达画布内部,因此画布部分在颗粒天空旁会显得平滑或斑驳。您 需要添加:在该材质的片段着色器中,在颜色空间转换之后,使用屏幕空间中的同一噪点瓦片进行柔光混合(gl_FragCoord.xy / tilePx,tile = 200 × 渲染器像素比)。从页面的 --grain 自定义属性中读取瓦片和数量,以便手机、HDR 和高对比度的数量随之变化,并在窗口大小变化时重新读取。通过渲染一帧并读取像素来测量。
HDR 屏幕、高对比度、打印@media (dynamic-range: high) 和 @media (prefers-contrast: more) 为这些屏幕重新计算数量,使用相同的混合模式。@media print 关闭颗粒。如果规则结果相同,则省略。

颜色根据其在渐变中的覆盖比例进行加权。细小的亮点不会像大面积深色区域那样被重视,因此同一区块的手机版和桌面版会得到相同的处理。

5. 应用

  • 将每个 CSS 块放置在其背景定义的位置,并附上注释。
  • 保留项目自身的背景表达式。 引擎基于计算值工作,因此在输出重复长解析渐变的地方,请放回 var(--slate) 或原始的 linear-gradient(...)。
  • 处理每个背景的所有版本(手机版、深色模式)。单层 CSS 放在同一媒体查询内的同一选择器上。如果不同,使用 --phone 或 --colours 测量各个版本。
  • 子元素方法: 在注释指定的位置添加 <div class="grain-layer" aria-hidden="true"></div>(在组件中,在其标记内)。
  • 对重复的噪声 URL 使用一个自定义属性。 如果多个规则使用同一个 URL,只需定义一次。
  • 如果报告列出“顶部淡入”, 请为其添加文中描述的相同颗粒,否则在最暗处会再次出现色带。
  • 移除所有临时内容(预览标签、grain-page.js)。

6. 每次都要检查

在宣布完成之前,将每个背景与其基准进行比较:

node <技能>/scripts/check.mjs --url http://localhost:3000/ --selector ".hero" --baseline <基准文件夹> [--option even]

它会再次拍摄页面照片,并为以下各项输出 PASS/FAIL:

  • 颗粒图层 已出现在页面中(CSS 已加载并在正确的选择器上)。
  • 页面上没有新错误。
  • 颗粒量 接近预期值。
  • 亮度 未发生变化。
  • 条纹 变窄了。
  • 背景外无变化。
  • 没有元素移动或改变大小。

它会写入 check.png,显示之前与现在的对比。出现任何 FAIL 时,请修复问题并重新运行检查; 不要在失败时报告成功。如果某个 FAIL 无法修复,请准确告知用户是哪个检查失败以及原因。

7. 报告

每个背景用一两句话说明,或针对整个站点使用简短列表:背景是什么、选择了什么以及原因。附上检查的测量结果(例如“颗粒 0.83 L*,条纹 80px → 0.7px,其他无变化”)并发送 check.png 或 compare 图片。说明一次该颗粒会适应 HDR 屏幕和高对比度设置,并在打印时关闭。如果报告指出对于该类型的背景,目标尚未通过目视确认,请简要说明。

后续调整

  • “太多了”或“看不见”: 调整一个级别。

*…

[已截断,用于翻译]*

相关技能