---
title: "底部悬浮药丸导航"
summary: "页面底部居中的药丸形导航：毛玻璃底、当前栏目高亮、按下回弹，旁边点缀会闪的小星星。"
category: "navigation (导航)"
origin: "original"
stack: ["html","css"]
tags: ["导航","药丸","手作感","毛玻璃"]
demo: "https://zhuoqidev.com/design/demos/pill-dock/"
url: "https://zhuoqidev.com/design/pill-dock/"
license: "MIT"
added: "2026-10-14"
translation: "https://zhuoqidev.com/en/design/pill-dock/index.md"
---

# 底部悬浮药丸导航

> 页面底部居中的药丸形导航：毛玻璃底、当前栏目高亮、按下回弹，旁边点缀会闪的小星星。

## 看点

- **位置**：`position: fixed` + `left: 50%` + `translateX(-50%)`，永远贴在底部正中，手机上大拇指也够得着。
- **毛玻璃**：底色是 `color-mix(in srgb, var(--card) 82%, transparent)` 再加 `backdrop-filter: blur(16px)`，压在内容上也读得清。
- **回弹**：按下 `scale: .93`，松开用 `linear()` 写的弹簧曲线弹回来，不用任何 JS 动画库。
- **编号**：每项前面一个等宽小编号（01–04），让一排字有节奏。

## 怎么用

1. 把 `.dock` 和 `.stars` 两段 CSS 拷进你的样式表，颜色变量换成自己的。
2. 当前页面的链接加 `aria-current="page"`（服务端或路由里设），高亮和读屏都靠它。
3. 页面底部留出 `padding-bottom`，别让最后一段内容被导航盖住。

## 坑

- `backdrop-filter` 在 Safari 要带 `-webkit-` 前缀。
- `linear()` 弹簧曲线不支持的老浏览器会退回线性过渡，不影响使用。

## 参考实现（完整源码）

在线演示：https://zhuoqidev.com/design/demos/pill-dock/。单文件，存成 .html 就能打开。

### index.html

```html
<!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;
        --paper-2: #f3eee2;
        --card: #fffdf8;
        --ink: #1c2230;
        --ink-2: #4a5163;
        --ink-3: #636877;
        --line: #e6dfd0;
        --dot: #d8d0bf;
        --blue: #2f5bd3;
        --on-blue: #fff;
        --hi: #ffe066;
        --ease: cubic-bezier(0.16, 1, 0.3, 1);
        /* A spring as a CSS easing curve: overshoots a little, then settles. */
        --spring: linear(
          0,
          0.062,
          0.215,
          0.411,
          0.615,
          0.799,
          0.95,
          1.059,
          1.126,
          1.158,
          1.161,
          1.145,
          1.116,
          1.083,
          1.051,
          1.023,
          1.001,
          0.986,
          0.977,
          0.974,
          0.974,
          0.978,
          0.983,
          0.988,
          0.994,
          0.998,
          1.001,
          1.003,
          1.004,
          1.004,
          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;
          --paper-2: #1c1f26;
          --card: #1e2129;
          --ink: #ece7dc;
          --ink-2: #bab6ac;
          --ink-3: #959aa8;
          --line: #31353f;
          --dot: #2c3039;
          --blue: #8eaaff;
          --on-blue: #14161b;
          --hi: #7d6416;
          color-scheme: dark;
        }
      }
      body {
        margin: 0;
        min-height: 100vh;
        background: var(--paper) radial-gradient(var(--dot) 1px, transparent 1.2px) 0 0 / 22px 22px;
        color: var(--ink);
        font: 16px/1.7 var(--f-sans);
      }
      main {
        max-width: 560px;
        margin: 0 auto;
        padding: 56px 16px 140px;
      }
      h1 {
        font-size: 28px;
        margin: 0 0 8px;
      }
      p {
        color: var(--ink-2);
      }

      /* ---------- the dock ---------- */
      .dock {
        position: fixed;
        left: 50%;
        bottom: 18px;
        z-index: 50;
        transform: translateX(-50%);
        display: flex;
        gap: 4px;
        padding: 6px;
        background: color-mix(in srgb, var(--card) 82%, transparent);
        border: 1px solid var(--line);
        -webkit-backdrop-filter: blur(16px);
        backdrop-filter: blur(16px);
        border-radius: 999px;
        box-shadow: 0 8px 32px rgba(28, 34, 48, 0.12);
      }
      .dock a {
        display: flex;
        align-items: baseline;
        gap: 6px;
        padding: 9px 16px;
        border-radius: 999px;
        font: 500 14px/1 var(--f-sans);
        text-decoration: none;
        color: var(--ink-2);
        white-space: nowrap;
        transition:
          scale 0.45s var(--spring),
          background 0.25s var(--ease),
          color 0.25s var(--ease);
      }
      .dock a .k {
        font: 400 10px/1 var(--f-mono);
        color: var(--ink-3);
      }
      .dock a:hover {
        color: var(--ink);
        background: var(--paper-2);
      }
      .dock a:active {
        scale: 0.93;
      }
      .dock a[aria-current] {
        background: var(--blue);
        color: var(--on-blue);
      }
      .dock a[aria-current] .k {
        color: var(--on-blue);
        opacity: 0.85;
      }
      @media (max-width: 480px) {
        .dock a {
          padding: 9px 12px;
        }
      }

      /* ---------- twinkling stars around the page ---------- */
      .stars {
        position: fixed;
        inset: 0;
        pointer-events: none;
      }
      .stars span {
        position: absolute;
        width: 14px;
        height: 14px;
        background: var(--hi);
        clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%);
        animation: twinkle 3.6s ease-in-out infinite;
      }
      @keyframes twinkle {
        0%,
        100% {
          opacity: 0.35;
          scale: 0.8;
          rotate: -8deg;
        }
        50% {
          opacity: 0.9;
          scale: 1.1;
          rotate: 8deg;
        }
      }
      @media (prefers-reduced-motion: reduce) {
        .stars span {
          animation: none;
        }
      }
    </style>
  </head>
  <body>
    <div class="stars" aria-hidden="true">
      <span style="left: 12%; top: 18%"></span>
      <span style="left: 82%; top: 26%; animation-delay: -1.2s"></span>
      <span style="left: 68%; top: 72%; animation-delay: -2.4s; width: 10px; height: 10px"></span>
      <span style="left: 20%; top: 64%; animation-delay: -0.6s; width: 9px; height: 9px"></span>
    </div>
    <main>
      <h1 id="title">首页</h1>
      <p>底部是悬浮的药丸导航：毛玻璃底、当前栏目蓝色高亮、按下有一点回弹。点点看。</p>
    </main>

    <nav class="dock" aria-label="主导航">
      <a href="#home" aria-current="page"><span class="k">01</span>首页</a>
      <a href="#posts"><span class="k">02</span>文章</a>
      <a href="#projects"><span class="k">03</span>项目</a>
      <a href="#about"><span class="k">04</span>关于</a>
    </nav>

    <script>
      // In a real site each link goes to its page and the server marks the current one; here a click
      // just moves the highlight.
      const links = document.querySelectorAll(".dock a");
      for (const link of links) {
        link.addEventListener("click", (event) => {
          event.preventDefault();
          links.forEach((a) => a.removeAttribute("aria-current"));
          link.setAttribute("aria-current", "page");
          document.getElementById("title").textContent = link.lastChild.textContent;
        });
      }
    </script>
  </body>
</html>
```
