推挤式侧边栏

导航侧边栏从左侧滑入,把页面推到一旁,页面略微缩小变暗,表明它退到了后面。

  • CSS
  • 布局
  • 切换
  • 收录于
控制台

AI 提示词

用 React 和 CSS 制作推挤式侧边栏。侧边栏(宽 180px)初始位于左侧 translateX(-100%),页面内容保持静止。打开时侧边栏滑到 translateX(0),页面移到 translateX(140px) scale(0.92),加上圆角并降低亮度,两者都在 420ms 内完成,缓动 cubic-bezier(0.16, 1, 0.3, 1),让页面看起来被推到了后面。侧边栏中的链接用 opacity 和 translateX(-12px) → 0 依次进入,transition-delay 为 60ms × 序号。点击变暗的页面或按 Escape 时关闭。菜单按钮带 aria-expanded 和 aria-controls,侧边栏关闭时设为 inert。在 prefers-reduced-motion 下只显示和隐藏侧边栏,不移动页面。链接为概览、项目、团队、设置。

源代码

Demo.tsx
'use client'

import { useId, useState } from 'react'
import s from './demo.module.css'

export default function SideDrawer({ labels }: { labels: Record<string, string> }) {
  const [open, setOpen] = useState(false)
  const id = useId()

  return (
    <div className={s.shell} data-open={open || undefined} onKeyDown={(e) => e.key === 'Escape' && setOpen(false)}>
      <nav id={id} className={s.drawer} inert={!open}>
        {[labels.a, labels.b, labels.c, labels.d].map((t, i) => (
          <a key={t} href="#" className={s.link} style={{ '--i': i } as React.CSSProperties} onClick={(e) => e.preventDefault()}>
            {t}
          </a>
        ))}
      </nav>
      <div className={s.page} onClick={() => open && setOpen(false)}>
        <div className={s.bar}>
          <button
            type="button"
            className={s.menu}
            aria-expanded={open}
            aria-controls={id}
            onClick={(e) => {
              e.stopPropagation()
              setOpen(!open)
            }}
          >
            ☰ {labels.menu}
          </button>
          <strong className={s.title}>{labels.title}</strong>
        </div>
        <div className={s.grid}>
          <span />
          <span />
          <span />
          <span />
        </div>
      </div>
    </div>
  )
}
demo.module.css
.shell {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--stage-bg);
}

.drawer {
  position: absolute;
  inset: 0 auto 0 0;
  display: grid;
  align-content: start;
  gap: 4px;
  width: 180px;
  padding: 56px 16px 16px;
  transform: translateX(-100%);
  transition: transform 420ms var(--ease-out);
}

.link {
  padding: 8px 10px;
  border-radius: 8px;
  color: var(--stage-ink);
  font: 500 var(--text-sm) / 1 var(--font-body);
  text-decoration: none;
  opacity: 0;
  transform: translateX(-12px);
  transition: opacity 260ms, transform 260ms var(--ease-out), background-color var(--dur-micro);
}

.link:hover { background: var(--stage-surface); }
.link:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; }

.page {
  position: absolute;
  inset: 0;
  padding: 14px 16px;
  background: var(--stage-surface);
  transform-origin: 0 50%;
  transition: transform 420ms var(--ease-out), border-radius 420ms var(--ease-out), filter 420ms;
}

.shell[data-open] .drawer { transform: none; }
.shell[data-open] .link { opacity: 1; transform: none; transition-delay: calc(120ms + var(--i) * 60ms); }

.shell[data-open] .page {
  transform: translateX(140px) scale(0.92);
  border-radius: 16px;
  filter: brightness(0.7);
  cursor: pointer;
}

.bar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.menu {
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--stage-rule);
  border-radius: 8px;
  background: var(--stage-bg);
  color: var(--stage-ink);
  font: 500 var(--text-sm) / 1 var(--font-body);
  cursor: pointer;
}

.menu:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

.title { font: 600 var(--text-base) / 1 var(--font-display); color: var(--stage-ink); }

.grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.grid span { height: 64px; border-radius: 10px; background: var(--stage-bg); }

@media (prefers-reduced-motion: reduce) {
  .drawer, .link, .page { transition: none; }
  .shell[data-open] .page { transform: none; filter: brightness(0.5); }
}

投稿创意

看到想收录的动效或主题?简单描述一下,会打开一个预先填好的 GitHub Issue,确认后即可提交。

你想投稿的是?

需要 GitHub 账号,投稿内容会公开。