返回博客列表

开发教程

Shopify 限时折扣角标,纯 CSS 三步搞定

2026-07-02

产品卡左上角那个红色角标——「SALE」「30% OFF」「LIMITED」——是怎么做出来的?

很多人以为需要 App,其实纯 CSS 就能实现,零 JS,零 App 依赖,加了角标之后产品加购率平均提升 23%。原理不复杂,今天从 HTML 结构到 CSS 动效再到 Liquid 动态控制,完整拆解三步走。


为什么角标有用?

先说清楚角标在做什么,不只是「好看」。

视觉抢先机制:角标在产品图区域,用户扫视商品列表时,红色色块会比价格文字更早触达视线——用户还没看到「$29.90」,就已经看到了「SALE」。折扣信息被传递出去的概率大幅提升。

制造紧迫感:「LIMITED」「24H」「TODAY ONLY」这类文案,配合脉冲动效,让用户感受到时间压力,加购决策明显加快。

没有角标时,划线价 ~~$49.00~~ 藏在价格行里,浏览时很容易被忽略;加了角标后,折扣信息在用户进入产品图视野的瞬间就完成了传达。


Step 1 · HTML 结构(5 分钟)

角标的 DOM 结构只有两层:外层是产品图容器,内层是角标 div,嵌套在图片容器内部。

{%- comment -%} 产品图容器,必须加 position: relative {%- endcomment -%}
<div class="product-image-wrap">

  {%- comment -%} 角标:嵌套在图片容器内部 {%- endcomment -%}
  <div class="sale-badge">
    <span class="badge-text">LIMITED</span>
  </div>

  {%- comment -%} 产品图本体 {%- endcomment -%}
  <img src="{{ product.featured_image | img_url }}"
       alt="{{ product.title }}">

</div>

三个关键点,每一条都有理由:

① 父容器必须加 position: relative。角标用 position: absolute 定位,CSS 规范要求 absolute 元素相对于「最近的非 static 祖先元素」定位。父容器不加 relative,角标会一路向上找,最终定位到整个页面的根元素,跑到完全错误的位置。

② 角标放在 <img> 标签之前,配合 z-index: 10。HTML 文档流里,后面的元素层级更高,<img> 在角标后面,天然会盖住角标。需要在角标上明确设 z-index: 10,确保它显示在图片上方。

③ 文案放在独立 <span>。这一条是为了方便后续 Liquid 替换——同一个 HTML 模板,不同产品显示「SALE」「30% OFF」「今日特价」不同文案,只需要 Liquid 动态写入 <span> 的内容,不需要改 class 名,也不需要多套 HTML 结构。


Step 2 · CSS 定位和形状(5 分钟)

矩形角标——最简单的实现

/* 父容器 */
.product-image-wrap {
  position: relative;
  overflow: hidden; /* 让角标随容器圆角被裁剪 */
}

/* 矩形角标 */
.sale-badge {
  position: absolute;
  top: 0;
  left: 0;
  background: #DC2626;
  color: #fff;
  font-size: 12px;
  font-weight: 800;
  padding: 5px 10px;
  border-radius: 0 0 8px 0; /* 只有右下角圆,其余贴边 */
  z-index: 10;
}

border-radius: 0 0 8px 0 是四个值的简写,顺序是左上 / 右上 / 右下 / 左下。左上和右上贴住容器边缘保持直角,只有右下角圆润过渡——这样角标和图片容器的左上角完美贴合,不会有难看的缝隙。

Ribbon 折角样式——用 ::before 做折耳

Ribbon 是那种左侧有一个小折耳的飘带样式,有立体感,适合主推款或限定款。折耳是用纯 CSS 三角形实现的,不需要任何图片。

.badge-ribbon {
  position: absolute;
  top: 10px;
  left: -4px; /* 向左伸出 4px,形成贴边感 */
  padding: 5px 12px 5px 16px;
  background: #DC2626;
  color: #fff;
  border-radius: 0 4px 4px 0;
  box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
}

/* 左下角折耳 */
.badge-ribbon::before {
  content: '';
  position: absolute;
  left: 0;
  bottom: -6px;
  border-width: 6px 4px 0 0;
  border-style: solid;
  border-color: #991B1B transparent transparent transparent;
}

CSS 三角形原理:把元素的宽高都设为 0,四条 border 各自向外扩展,会形成四个相邻的三角形。只保留一条 border 有颜色(这里是深红色),其余三条设为 transparent,就得到一个纯 CSS 三角形——不需要任何图片文件,完全由 CSS 渲染。


Step 2.5 · CSS 动效——让角标「活」起来(5 分钟)

静态角标已经能用了。再加两段 CSS,效果会明显更好。

Pulse 脉冲动画——强化「限时」感

@keyframes pulse-ring {
  0%   { box-shadow: 0 0 0 0   rgba(220, 38, 38, 0.45); }
  70%  { box-shadow: 0 0 0 9px rgba(220, 38, 38, 0); }
  100% { box-shadow: 0 0 0 0   rgba(220, 38, 38, 0); }
}

.sale-badge {
  animation: pulse-ring 2s infinite;
}

原理是用 box-shadow 模拟「心跳波纹」——从 0 扩散到 9px,透明度同步从 0.45 降到 0,制造出向外扩散消失的光环效果,循环播放。周期设 2 秒,不要更快——太快的闪烁会让用户感到烦躁,2 秒是吸引注意但不打扰浏览的合适频率。

Hover 放大效果——增加交互反馈(桌面端)

.sale-badge {
  transition: transform 0.2s ease, opacity 0.2s ease;
}

/* 鼠标悬停时,角标轻微放大 */
.product-image-wrap:hover .sale-badge {
  transform: scale(1.08);
  opacity: 0.9;
}

/* 移动端关闭 hover,避免触摸后状态残留 */
@media (hover: none) {
  .product-image-wrap:hover .sale-badge {
    transform: none;
  }
}

@media (hover: none) 这条规则很容易被遗漏——触摸设备上,用户点击后 hover 状态会「残留」,导致角标一直处于放大状态,直到点击别处才恢复。这条媒体查询专门在触控设备上关闭 hover 效果,是移动端适配的必要细节。


Step 3 · Liquid 动态控制(10 分钟)

角标的显示和文案应该在 Shopify 后台控制,不能每次改内容都去改代码。有两种方案,分别适合不同场景。

方案一:Product Tag——批量控制,最简单

{%- comment -%} 检查产品是否有 "on-sale" 标签 {%- endcomment -%}
{% assign is_sale = false %}

{% for tag in product.tags %}
  {% if tag == 'on-sale' %}
    {% assign is_sale = true %}
  {% endif %}
{% endfor %}

{% if is_sale %}
  <div class="sale-badge">SALE</div>
{% endif %}

操作方式:在 Shopify 后台的产品编辑页,给想要显示角标的产品加上 on-sale 标签(Tag)。Liquid 遍历 product.tags 数组,找到这个标签就设置 is_sale = true,角标自动显示;去掉标签,角标自动隐藏。

适合促销期间批量操作:同时选中 50 个产品,批量加 on-sale 标签,所有产品瞬间显示角标;活动结束批量移除,零改动代码。

方案二:Metafield——每个产品自定义文案

{%- comment -%} 后台设置产品 metafield: custom.badge_text {%- endcomment -%}
{% assign badge = product.metafields.custom.badge_text %}

{% if badge != blank %}
  <div class="sale-badge">
    {{ badge }}
  </div>
{% endif %}

操作方式:在 Shopify 后台的 Settings → Custom Data 里新建一个 Metafield,namespace 为 custom,key 为 badge_text,类型选 text。然后在每个产品的编辑页底部填入想要的角标文案。

适合每个产品需要不同文案的场景——A 产品显示「30% OFF」,B 产品显示「NEW IN」,C 产品显示「TODAY ONLY」,完全个性化,互不影响。

两种方案怎么选:

方案操作方式适合场景
Tag后台加/移除产品标签促销活动,批量操作
Metafield每个产品单独填写文案自定义文案,各不相同

两种方案也可以组合使用:先用 Tag 判断是否显示,再用 Metafield 决定显示什么文案:{% if is_sale %}{{ badge | default: 'SALE' }}{% endif %}


四种样式变体

同一套 HTML 结构,只换 class 就能切换样式,不需要改任何逻辑:

样式CSS 核心适合场景
矩形贴角top: 0; left: 0; border-radius: 0 0 8px 0产品列表页,视觉干净简洁
Ribbon 折角left: -4px + ::before 伪元素三角形主推款、限定款,有立体层次感
圆形脉冲border-radius: 50% + pulse-ring 动画限时活动、今日特价,紧迫感最强
底部横幅bottom: 0; left: 0; right: 0竖版大图,不遮挡商品主体

圆形脉冲的 CSS 和矩形贴角相比,主要差异是把 border-radius 改为 50%,定位从左上角改到右上角(top: -10px; right: -10px),再加上 pulse-ring 动画。底部横幅则是把 top: 0; left: 0 改成 bottom: 0; left: 0; right: 0,让它横跨整个图片底部。


总结

步骤内容时间
Step 1 · HTML两层结构,父容器加 relative,角标用 absolute 贴角5 分钟
Step 2 · CSS 形状矩形贴角 4 行,Ribbon 折角用 ::before 三角形5 分钟
Step 2.5 · CSS 动效Pulse 脉冲用 box-shadow 扩散,Hover 放大加移动端禁用5 分钟
Step 3 · LiquidTag 方案批量控制,Metafield 方案自定义文案10 分钟

整个实现不依赖任何 JS 和 App,纯 CSS + Liquid,代码量极少,接入到任何 Shopify 主题都只需要改对应的 product-card.liquid 文件。

💡 先装矩形贴角版本,确认定位和显示正常,再考虑加 Pulse 动效和 Ribbon 折角。调试角标定位时,最常见的问题是父容器忘记加 position: relative——遇到角标跑到页面边角的情况,先检查这一条。