果味博客卡片系统 - 技术文档

果味博客卡片系统 - 技术文档

系统架构

层级结构

品牌层 (Brand Layer)
├── 颜色系统 (Color System)      ← _variables.scss
│   ├── 8种水果色
│   └── 4种渐变配置
│
├── 组件层 (Component Layer)      ← _post-card.scss
│   ├── 卡片容器 (.posts)
│   ├── 卡片本体 (.post-card)
│   ├── 封面区域 (.card-cover)
│   ├── 内容区域 (.card-content)
│   ├── 标题 (.card-title)
│   ├── 标签 (.card-tags)
│   ├── 元数据 (.card-meta)
│   ├── 摘要 (.card-excerpt)
│   └── 底部 (.card-footer)
│
└── 模板层 (Template Layer)       ← blog.html
    ├── 循环遍历文章
    ├── 渲染卡片
    └── 处理可选字段

CSS 类层次

顶级容器

.posts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--space-6);
  padding: var(--space-5) 0;
  margin: 0;
}

响应式断点

  • >768px: 自动 3+ 列
  • 480-768px: 自动 1-2 列
  • <480px: 1 列

卡片主体

.post-card {
  /* 布局 */
  display: flex;
  flex-direction: column;
  height: 100%;
  
  /* 外观 */
  background: var(--body-bg);
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.5);
  
  /* 动画 */
  transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

悬停效果

  • transform: translateY(-12px) - 上浮
  • box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15) - 增强阴影
  • border-color: var(--fruit-peach) - 边框变色

封面区域

.card-cover {
  width: 100%;
  height: 200px;
  background: linear-gradient(135deg, var(--fruit-mango), var(--fruit-peach), var(--fruit-orange));
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
  overflow: hidden;
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

特殊处理

  • background-blend-mode: overlay - 与背景图融合
  • ::after - 渐变叠加层
  • ::before - 装饰表情符号

标签系统

.tag {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 20px;
  letter-spacing: 0.5px;
  transition: all 0.3s ease;
  
  /* 根据位置着色 */
  &:nth-child(1) { background: rgba(255, 215, 0, 0.2); color: var(--fruit-mango); }
  &:nth-child(2) { background: rgba(255, 107, 157, 0.2); color: var(--fruit-strawberry); }
  &:nth-child(3) { background: rgba(255, 149, 0, 0.2); color: var(--fruit-orange); }
  &:nth-child(n+4) { background: rgba(124, 179, 66, 0.2); color: var(--fruit-kiwi); }
}

响应式设计细节

断点定义

// 桌面:1200px+
@media (max-width: 1200px) {
  // 3列 → 2列
}

// 平板:768px-1200px
@media (max-width: 768px) {
  grid-template-columns: 1fr;  // 改为单列
  gap: var(--space-5);
}

// 手机:480px以下
@media (max-width: 480px) {
  gap: var(--space-4);
  padding: var(--space-4);
  -webkit-line-clamp: 2;  // 摘要从3行变2行
}

移动优化

  1. 字体大小:自动缩小
  2. 间距:自动减少
  3. 图片高度:保持固定(200px)
  4. 动画:禁用某些复杂效果

Jekyll 集成

Front Matter 字段

---
layout: post
title: String              # 必须
date: DateTime            # 必须
tags: [String]            # 可选,数组
excerpt: String           # 可选,摘要
cover_image: String       # 可选,封面图路径
image: String             # 可选,备用图路径
toc: Boolean              # 可选,目录
---

模板逻辑


  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog16.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">mysql</span>
        
          <span class="tag">unity</span>
        
          <span class="tag">软件开发</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog15.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">软件开发</span>
        
          <span class="tag">数字孪生</span>
        
          <span class="tag">unity</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog14.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">openvino</span>
        
          <span class="tag">Qwen</span>
        
          <span class="tag">unity</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog13.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">软件开发</span>
        
          <span class="tag">cron表达式</span>
        
          <span class="tag">Web</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog12.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">时间序列预测</span>
        
          <span class="tag">边缘AI</span>
        
          <span class="tag">机会网络</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog11.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">联邦学习</span>
        
          <span class="tag">仿真实验</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog10.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">科研</span>
        
          <span class="tag">总结</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog9.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">C++</span>
        
          <span class="tag">program</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog8.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">指数滑动平均法</span>
        
          <span class="tag">加权质心定位</span>
        
          <span class="tag">Mobile CrowdSensoring</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog7.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">BaiduMap</span>
        
          <span class="tag">android</span>
        
          <span class="tag">Mobile CrowdSensoring</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog5.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">android</span>
        
          <span class="tag">BaiduMap</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog4.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">Django</span>
        
          <span class="tag">websocket</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog1.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">Mobile-CrowdSensoring</span>
        
          <span class="tag">BaiduMap</span>
        
          <span class="tag">android</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog3.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">BaiduMap</span>
        
          <span class="tag">android</span>
        
          <span class="tag">java</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog2.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">BaiduMap</span>
        
          <span class="tag">android</span>
        
          <span class="tag">java</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

  <article class="post-card">
    <!-- 封面:优先使用 cover_image,其次 image -->
    <div class="card-cover" 
         
           style="background-image: url('/img/covers/blog6.jpg')"
         >
    </div>
    
    <!-- 标签:限制显示3个 -->
    
      <div class="card-tags">
        
          <span class="tag">RL</span>
        
          <span class="tag">Robot</span>
        
          <span class="tag">pathplan</span>
        
      </div>
    
    
    <!-- 其他内容 -->
  </article>

颜色系统详解

设计原则

  1. 温暖底色:以橙黄色系为主,传递活力感
  2. 冷色点缀:绿色和蓝色用于平衡
  3. 渐变过渡:使用 135deg 线性渐变创建动态感
  4. 可访问性:对比度 > 4.5:1(WCAG AA)

颜色使用规则

颜色 亮度 用途 对比度
芒果黄 主色调、亮点 ✓ 好
桃子色 中-高 次主色、过渡 ✓ 好
橙子色 强调、按钮 ✓ 好
草莓红 中-高 标签、突出 ✓ 好
樱桃红 深调、对比 ✓ 好
西瓜红 特殊强调 ✓ 好
猕猴桃绿 辅助、平衡 ✓ 好
蓝莓蓝 补充、替代 ✓ 好

深色模式适配

@media (prefers-color-scheme: dark) {
  :root {
    --body-color: var(--dark-body-color);
    --body-bg: var(--dark-body-bg);
    --heading-color: var(--dark-heading-color);
    --link-color: var(--dark-link-color);
    --border-color: var(--dark-border-color);
  }
}

深色模式优化

  • 背景从白色改为 #1a1a2e
  • 文字颜色自动反转
  • 阴影透明度增加
  • 渐变强度调整

动画效果

使用的 easing 函数

cubic-bezier(0.34, 1.56, 0.64, 1)

这是一个过度缓动函数,特点:

  • 略微超调(overshoot)
  • 自然、有弹性的感觉
  • 适合 UI 动画

过渡属性

transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);

涵盖的属性

  • transform - 位置变化
  • box-shadow - 阴影变化
  • color - 文字颜色
  • border-color - 边框颜色

性能考虑

优化点

  1. 使用 GPU 加速transformopacity 使用 GPU
  2. 避免 layout trashing:使用 transform 而不是 top/left
  3. 条件渲染:标签限制 3 个,防止 DOM 过多
  4. 选择器优化:使用简洁的类名和选择器

性能数据

  • 首屏加载:无额外 JS,仅 CSS
  • 重排开销:最小化(只使用 transform
  • 动画帧率:60fps(通过 GPU 加速)
  • 文件大小
    • _post-card.scss: ~8KB(压缩后 ~2KB)
    • _variables.scss 增量:~1KB

浏览器特性检测

必需特性

  • display: grid - 所有现代浏览器
  • CSS 变量 - 所有现代浏览器
  • background-blend-mode - 95% 浏览器支持
  • transition - 所有浏览器

备选方案

  1. 不支持 Grid:降级到 Flexbox
  2. 不支持 CSS 变量:使用预定义值
  3. 不支持 blend-mode:使用 ::after 伪元素

调试技巧

常见问题

1. 卡片样式不生效?

# 清除缓存重新构建
rm -rf .jekyll-cache _site
bundle exec jekyll serve

2. 封面图不显示?

// 在浏览器控制台检查
console.log(document.querySelector('.card-cover').style.backgroundImage);

3. 响应式不工作?

/* 检查视口宽度 */
body::before {
  content: 'Width: ' attr(data-width);
  position: fixed;
  top: 0;
  left: 0;
}

扩展可能性

可以添加的功能

  1. 图片懒加载:使用 loading="lazy"
  2. 阅读时间:显示预估阅读时间
  3. 评论计数:显示评论数量
  4. 分享按钮:添加社交分享
  5. 过滤器:按标签筛选
  6. 搜索:全文搜索功能
  7. 排序:按日期/热度排序
  8. 卡片变体:不同大小的卡片

代码示例

<!-- 添加阅读时间 -->


技术栈:Jekyll + SCSS + CSS Grid + CSS Variables 兼容性:IE 11+ (需要 polyfill) 无障碍:WCAG 2.1 AA 级别