卓琪的开发笔记zhuoqidev.comRSSllms.txt

← 全部灵感~/design/highlighter-button

按钮与控件原创

荧光笔划过

链接和按钮被荧光笔从左往右涂一道:笔头是斜的、位置偏低、跨行每行单独划,离开时往右擦掉。纯 CSS。

html · cssMIT收录于 #按钮#链接#手作感#零依赖#微交互

在线演示新窗口打开 ↗

看点

  • 划线是背景,不是伪元素:链接的背景是 linear-gradient(transparent 52%, 黄 52%, 黄 90%, transparent 90%),只占行高的下半截,像荧光笔拖得偏低;background-size 从 0% 100% 过渡到 100% 100%,就是从左往右涂。
  • 跨行也对:box-decoration-break: clone 让换行的链接每一行各自有一道,而不是第一行起、最后一行止的一整块。
  • 斜笔头:按钮上的那道是 ::before,skewX(-18deg) 把两端切成斜口,再 rotate(-2deg) 让它不那么正;scaleX 从 0 到 1。
  • 擦掉的方向:离开时把 transform-origin 换到右边,收起来就像往右擦掉,而不是原路缩回。
  • 标重点:第三种是可切换的,aria-pressed="true" 时留着那道黄,适合「读过」「收藏」这类状态。

怎么用

  1. 正文链接:拷 .hl,给 <a> 加类名;换色改 --c。
  2. 按钮:拷 .mk 和它的 ::before、:hover 规则;按钮背景要透明,黄条才在字下面露出来(isolation: isolate + z-index: -1 已经处理好层级)。
  3. 深色模式下荧光黄要换成暗一点的饱和色(示例里是 #7d6416),浅色字压在上面才读得清。

坑

  • background-size 的过渡在 Safari 很老的版本里会跳帧,可以接受。
  • 链接默认没有下划线,记得留一个平时就看得出是链接的记号(示例用一条虚线),不然只有指上去才知道能点。
  • 触屏没有 hover:链接点下去就跳走了,看不到动画,这没关系;按钮可以把同一效果挂在 :active 上。
$给 Agent 读这篇:index.md

源码 MIT 开源,拿去改。

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>荧光笔划过</title>
    <style>
      :root {
        --paper: #fbf8f1;
        --card: #fffdf8;
        --ink: #1c2230;
        --ink-2: #4a5163;
        --ink-3: #636877;
        --line: #e6dfd0;
        --dot: #d8d0bf;
        --blue: #2f5bd3;
        --seal: #c0391f;
        --hi: #ffe066; /* highlighter yellow */
        --hi-pink: #ffb3c7;
        --hi-green: #b6f0a8;
        --ease: cubic-bezier(0.16, 1, 0.3, 1);
        --f-sans: system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
        --f-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
        color-scheme: light;
      }
      @media (prefers-color-scheme: dark) {
        :root {
          --paper: #14161b;
          --card: #1e2129;
          --ink: #ece7dc;
          --ink-2: #bab6ac;
          --ink-3: #959aa8;
          --line: #31353f;
          --dot: #2c3039;
          --blue: #8eaaff;
          --seal: #f0694e;
          /* On dark paper the ink is light, so the marker turns into a dim, saturated stripe. */
          --hi: #7d6416;
          --hi-pink: #7a2e44;
          --hi-green: #2f5f2a;
          color-scheme: dark;
        }
      }
      * { box-sizing: border-box; }
      body {
        margin: 0;
        min-height: 100vh;
        display: grid;
        place-items: center;
        padding: 32px 16px;
        background: var(--paper) radial-gradient(var(--dot) 1px, transparent 1.2px) 0 0 / 22px 22px;
        color: var(--ink);
        font: 16px/1.8 var(--f-sans);
      }
      .sheet {
        width: min(560px, 100%);
        padding: 28px;
        background: var(--card);
        border: 1px solid var(--line);
        border-radius: 14px;
        box-shadow: 0 10px 28px -14px rgba(28, 34, 48, 0.28);
      }
      .label { margin: 0 0 10px; font: 500 11px/1 var(--f-mono); letter-spacing: 0.12em; color: var(--ink-3); }
      .label + * { margin-top: 0; }
      section + section { margin-top: 26px; }

      /* ---------- 1. a link the marker sweeps across ----------
         The stroke is a background that grows from 0 to 100% wide. The gradient stops leave a band
         in the lower part of the line, like a highlighter dragged a bit low; box-decoration-break
         makes each wrapped line get its own stroke. */
      .hl {
        --c: var(--hi);
        color: inherit;
        text-decoration: none;
        background: linear-gradient(transparent 52%, var(--c) 52%, var(--c) 90%, transparent 90%) no-repeat 0 0 / 0% 100%;
        -webkit-box-decoration-break: clone;
        box-decoration-break: clone;
        padding: 0 2px;
        transition: background-size 0.45s var(--ease);
        border-bottom: 1px dashed var(--ink-3);
      }
      .hl:hover,
      .hl:focus-visible { background-size: 100% 100%; border-bottom-color: transparent; outline: none; }
      .hl.pink { --c: var(--hi-pink); }
      .hl.green { --c: var(--hi-green); }

      /* ---------- 2. a button with a marker stroke behind its label ----------
         The stroke is a pseudo-element: skewed so its ends are cut at an angle like a chisel tip,
         scaled from the left edge, and a little rotated so it is never perfectly straight. */
      .mk {
        position: relative;
        isolation: isolate;
        padding: 10px 18px;
        border: 1.5px solid var(--ink);
        border-radius: 999px;
        background: transparent;
        color: var(--ink);
        font: 600 15px/1 var(--f-sans);
        cursor: pointer;
        transition: scale 0.2s;
      }
      .mk::before {
        content: "";
        position: absolute;
        z-index: -1;
        left: 10px;
        right: 10px;
        top: 52%;
        height: 42%;
        background: var(--hi);
        border-radius: 2px;
        transform-origin: 0 50%;
        transform: rotate(-2deg) skewX(-18deg) scaleX(0);
        transition: transform 0.4s var(--ease);
      }
      .mk:hover::before,
      .mk:focus-visible::before { transform: rotate(-2deg) skewX(-18deg) scaleX(1); }
      .mk:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
      .mk:active { scale: 0.95; }
      /* Leaving the button, the stroke wipes away to the right instead of shrinking back. */
      .mk:not(:hover):not(:focus-visible)::before { transform-origin: 100% 50%; }

      /* ---------- 3. "already read" lines that stay highlighted ----------
         Toggling aria-pressed draws the stroke; it stays, like marking up a printout. */
      .pick {
        all: unset;
        cursor: pointer;
        --c: var(--hi);
        background: linear-gradient(104deg, transparent 0.6em, var(--c) 0.6em calc(100% - 0.4em), transparent calc(100% - 0.4em)) no-repeat 0 62% / 0% 58%;
        -webkit-box-decoration-break: clone;
        box-decoration-break: clone;
        transition: background-size 0.5s var(--ease);
        border-radius: 3px;
        margin: 0 -0.6em 0 -0.6em;
        padding: 0 0.4em 0 0.6em;
      }
      .pick[aria-pressed="true"] { background-size: 100% 58%; }
      .pick:focus-visible { outline: 2px dashed var(--blue); outline-offset: 2px; }
      p.quote { color: var(--ink-2); margin: 0; }
      .hint { margin: 8px 0 0; font-size: 12px; color: var(--ink-3); }

      @media (prefers-reduced-motion: reduce) {
        .hl, .mk::before, .pick { transition: none; }
      }
    </style>
  </head>
  <body>
    <main class="sheet">
      <section>
        <p class="label">01 · 链接</p>
        <p>
          正文里的链接平时只有一条虚线,指上去才被荧光笔<a class="hl" href="#">从左往右划一道</a>;换个颜色也行:<a class="hl pink" href="#">粉色</a>、<a class="hl green" href="#">绿色</a>。跨行的长链接<a class="hl" href="#">每一行都会单独划到,不会被拉成一整块奇怪的色带</a>。
        </p>
      </section>
      <section>
        <p class="label">02 · 按钮</p>
        <div style="display: flex; flex-wrap: wrap; gap: 12px">
          <button class="mk" type="button">读全文 →</button>
          <button class="mk" type="button">订阅更新</button>
        </div>
        <p class="hint">笔头是斜的,进来从左往右涂,离开往右边擦掉。</p>
      </section>
      <section>
        <p class="label">03 · 点一下标重点</p>
        <p class="quote">
          <button class="pick" type="button" aria-pressed="false">先给结论,再给依据。</button>
          读者没时间的时候,只看标黄的那几句也能明白你要说什么。
          <button class="pick" type="button" aria-pressed="true">数字放进图里,别堆在段落里。</button>
        </p>
      </section>
    </main>
    <script>
      for (const b of document.querySelectorAll(".pick")) {
        b.addEventListener("click", () => b.setAttribute("aria-pressed", String(b.getAttribute("aria-pressed") !== "true")));
      }
      for (const a of document.querySelectorAll('a[href="#"]')) a.addEventListener("click", (e) => e.preventDefault());
    </script>
  </body>
</html>

来加个微信吧

刘卓琪的微信二维码

微信扫码,或在手机上长按识别

加好友请备注来意,不然可能会漏掉哦~