# 布局之道——Flex 与 Grid 的协奏


## 概述

页面结构就像房子的框架——框架不牢，装修再好也没用。

CSS 布局的两大工具是 **Flexbox** 和 **Grid**。简单区分：

- **Flex**：一维布局，要么横着排，要么竖着排
- **Grid**：二维布局，行和列同时控制

Tailwind 把这两者的核心功能包装成了简洁的工具类。

## Display 基础

在用 Flex 和 Grid 之前，先了解最基础的 display 类型：

```html
<div class="block">块级元素，独占一行</div>
<span class="inline">行内元素，不换行</span>
<span class="inline-block">行内块，不换行但可以设宽高</span>
<div class="hidden">完全隐藏，不占空间</div>
```

| 类名 | 行为 |
|------|------|
| `block` | 独占一行，可以设宽高 |
| `inline` | 行内排列，不能设宽高 |
| `inline-block` | 行内排列，可以设宽高 |
| `hidden` | `display: none` |

## Flex 布局

### 容器

给父元素加上 `flex`，子元素就自动横向排列：

```html
<div class="flex">
  <div>项目 1</div>
  <div>项目 2</div>
  <div>项目 3</div>
</div>
```

### 方向

```html
<div class="flex flex-row">横向排列（默认）</div>
<div class="flex flex-col">纵向排列</div>
<div class="flex flex-row-reverse">横向反向排列</div>
<div class="flex flex-col-reverse">纵向反向排列</div>
```

### 换行

```html
<div class="flex flex-wrap">
  <!-- 子元素过多时自动换行 -->
</div>
<div class="flex flex-nowrap">
  <!-- 不换行，压缩子元素宽度 -->
</div>
```

### 子元素伸缩

Flex 最强大的能力是子元素可以按比例分配空间：

```html
<div class="flex">
  <div class="flex-1 bg-blue-500">flex-1 占 1 份</div>
  <div class="flex-1 bg-green-500">flex-1 也占 1 份</div>
  <div class="flex-1 bg-yellow-500">flex-1 也占 1 份</div>
</div>
```

三个元素宽度相等，各占三分之一。如果把其中一个改成 `flex-[2]`，它就占 2 份空间：

```html
<div class="flex">
  <div class="flex-1 bg-blue-500">1 份</div>
  <div class="flex-[2] bg-green-500">2 份（两倍宽）</div>
  <div class="flex-1 bg-yellow-500">1 份</div>
</div>
```

| 类名 | 等比例 | 初始宽度 | 适用场景 |
|------|--------|----------|----------|
| `flex-1` | 按比例扩展 | 0 | 等分空间 |
| `flex-auto` | 按内容扩展 | 内容宽度 | 内容长度不统一时 |
| `flex-initial` | 不自动扩展 | 内容宽度 | 保持固有大小 |
| `grow` | 可扩展 | — | 允许元素填满剩余空间 |
| `shrink-0` | 不收缩 | — | 防止元素被压缩 |

### 主轴对齐（justify）

控制 Flex 容器内元素在**主轴方向**上的排列：

```html
<div class="flex justify-start">起点对齐（默认）</div>
<div class="flex justify-center">居中对齐</div>
<div class="flex justify-end">末端对齐</div>
<div class="flex justify-between">两端对齐，中间均匀分布</div>
<div class="flex justify-around">每个元素两侧间距相等</div>
<div class="flex justify-evenly">所有间距完全相等</div>
```

`justify-between` 是最常用的——它让第一个元素在起点，最后一个在终点，中间的均匀分布。

![](images/04-flex-justify.svg)

### 交叉轴对齐（items）

控制 Flex 容器内元素在**交叉轴方向**上的对齐：

```html
<div class="flex items-start">顶部对齐</div>
<div class="flex items-center">垂直居中 —— 最常用</div>
<div class="flex items-end">底部对齐</div>
<div class="flex items-stretch">拉伸填满（默认值）</div>
```

`items-center` 搭配 `flex` 是最常见的组合之一，快速实现垂直居中：

```html
<div class="flex items-center gap-4">
  <img class="w-10 h-10 rounded-full" src="avatar.jpg" alt="头像" />
  <span class="font-medium">用户名</span>
</div>
```

### 单个元素对齐（self）

只调整 Flex 中某一个子元素的对齐：

```html
<div class="flex items-center h-32 bg-gray-100">
  <div class="self-start bg-blue-500">顶部</div>
  <div class="self-center bg-green-500">居中</div>
  <div class="self-end bg-yellow-500">底部</div>
</div>
```

## Grid 布局

Grid 适合做**二维排列**，比如卡片网格、仪表盘。

### 基本用法

```html
<div class="grid grid-cols-3 gap-6">
  <div class="bg-gray-100 p-4 rounded-lg">卡片 1</div>
  <div class="bg-gray-100 p-4 rounded-lg">卡片 2</div>
  <div class="bg-gray-100 p-4 rounded-lg">卡片 3</div>
  <div class="bg-gray-100 p-4 rounded-lg">卡片 4</div>
  <div class="bg-gray-100 p-4 rounded-lg">卡片 5</div>
  <div class="bg-gray-100 p-4 rounded-lg">卡片 6</div>
</div>
```

`grid-cols-3` 将容器分为 3 列，子元素自动按行排列。`gap-6` 控制行列间距。

![](images/04-grid-cols.svg)

| 类名 | 列数 |
|------|------|
| `grid-cols-1` ~ `grid-cols-12` | 1~12 列 |
| `grid-cols-none` | 不指定列宽 |

### 跨列和跨行

让某个元素占据多列或多行：

```html
<div class="grid grid-cols-3 gap-4">
  <div class="bg-blue-500 p-4 col-span-2">跨 2 列</div>
  <div class="bg-green-500 p-4">普通</div>
  <div class="bg-yellow-500 p-4">普通</div>
  <div class="bg-purple-500 p-4 col-span-3">跨 3 列（整行）</div>
</div>
```

| 类名 | 效果 |
|------|------|
| `col-span-2` | 跨越 2 列 |
| `col-span-full` | 跨越所有列 |
| `col-start-1` | 从第 1 列开始 |
| `col-end-4` | 到第 4 列结束 |

行方向同理：`row-span-2`、`row-span-full`、`row-start-1`。

### 密集排列

一般来说 Grid 按顺序从左到右排列。但可以启用"密集模式"来填充空隙：

```html
<div class="grid grid-cols-3 gap-4 grid-flow-dense">
  <!-- 元素会自动填充空白区域 -->
</div>
```

## 定位

除了 Flex 和 Grid，Tailwind 也支持 CSS 定位方式：

```html
<div class="relative">
  <!-- 相对定位，保留占位，做绝对定位的参照 -->
  <div class="absolute top-0 right-0">
    <!-- 绝对定位，相对于最近的 relative 父元素 -->
  </div>
</div>

<div class="fixed top-0 inset-x-0 z-50">
  <!-- 固定定位，相对于浏览器窗口，不随页面滚动 -->
</div>

<div class="sticky top-0">
  <!-- 粘性定位，滚动到顶部后固定 -->
</div>
```

`inset-x-0` 表示 `left: 0; right: 0;`，`inset-0` 表示所有方向都为 0。

> [!tip] 技巧
> `fixed` 适合导航栏和浮层，`sticky` 适合侧边栏和表格表头，`absolute` 适合下拉菜单和角标。

## 实战：给产品页搭骨架

现在用布局工具重新组织产品展示页的结构：

```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>星光科技 · 智能灯</title>
  <script src="https://unpkg.com/@tailwindcss/browser@4"></script>
</head>
<body>
  <!-- 固定导航栏 -->
  <nav class="fixed top-0 inset-x-0 bg-white/80 backdrop-blur-sm border-b border-gray-100 z-50">
    <div class="max-w-5xl mx-auto flex items-center justify-between px-6 h-16">
      <span class="text-xl font-bold text-gray-900">星光科技</span>
      <div class="flex gap-8 text-gray-600">
        <a href="#" class="hover:text-gray-900">产品</a>
        <a href="#" class="hover:text-gray-900">特性</a>
        <a href="#" class="hover:text-gray-900">用户评价</a>
        <a href="#" class="hover:text-gray-900">联系我们</a>
      </div>
    </div>
  </nav>

  <!-- Hero 区域（留出导航栏高度） -->
  <main class="max-w-5xl mx-auto px-6 pt-32 pb-16">
    <div class="flex flex-col items-center text-center">
      <h1 class="text-5xl font-extrabold text-gray-900">
        智能台灯 <span class="text-blue-600">Lumina</span>
      </h1>
      <p class="mt-6 text-xl text-gray-500 leading-relaxed max-w-2xl">
        光线随你而变。一盏懂你的台灯。
      </p>
      <button class="mt-8 px-8 py-4 bg-blue-600 text-white text-lg font-semibold rounded-xl hover:bg-blue-700">
        立即购买
      </button>
    </div>

    <!-- 特性卡片网格 -->
    <section class="mt-32">
      <h2 class="text-3xl font-bold text-gray-900 text-center">为什么选择 Lumina</h2>
      <div class="mt-12 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
        <div class="p-6 bg-gray-50 rounded-2xl text-center">
          <div class="w-12 h-12 bg-blue-100 rounded-xl flex items-center justify-center mx-auto">
            <span class="text-2xl">🧠</span>
          </div>
          <h3 class="mt-4 text-xl font-semibold text-gray-900">AI 调光</h3>
          <p class="mt-2 text-gray-500 leading-relaxed">
            内置传感器实时感知环境光线，自动调整亮度和色温。
          </p>
        </div>

        <div class="p-6 bg-gray-50 rounded-2xl text-center">
          <div class="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center mx-auto">
            <span class="text-2xl">👁️</span>
          </div>
          <h3 class="mt-4 text-xl font-semibold text-gray-900">护眼模式</h3>
          <p class="mt-2 text-gray-500 leading-relaxed">
            无频闪、低蓝光，通过德国莱茵 TÜV 护眼认证。
          </p>
        </div>

        <div class="p-6 bg-gray-50 rounded-2xl text-center">
          <div class="w-12 h-12 bg-purple-100 rounded-xl flex items-center justify-center mx-auto">
            <span class="text-2xl">🎤</span>
          </div>
          <h3 class="mt-4 text-xl font-semibold text-gray-900">语音控制</h3>
          <p class="mt-2 text-gray-500 leading-relaxed">
            支持 Siri、小爱同学、Google Assistant 语音指令。
          </p>
        </div>
      </div>
    </section>

    <!-- 页脚 -->
    <footer class="mt-32 pt-8 border-t border-gray-100 flex items-center justify-between text-sm text-gray-400">
      <span>&copy; 2026 星光科技</span>
      <div class="flex gap-6">
        <a href="#" class="hover:text-gray-600">隐私政策</a>
        <a href="#" class="hover:text-gray-600">服务条款</a>
      </div>
    </footer>
  </main>
</body>
</html>
```

这个页面里用了这些布局技巧：

- `fixed top-0 inset-x-0` —— 导航栏固定在顶部
- `flex items-center justify-between` —— 导航栏左右布局，Logo 在左，链接在右
- `flex-col items-center` —— Hero 区域垂直居中
- `grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8` —— 响应式卡片网格：手机 1 列，平板 2 列，桌面 3 列
- `flex items-center justify-between` —— 页脚左右分布

> [!summary] 本章小结
> - Flex 是一维布局，`flex` + `justify-*` + `items-*` 三件套搞定大部分场景
> - Grid 是二维布局，`grid-cols-*` 配合 `gap-*` 做卡片网格
> - `flex-1` 等比例分配空间，`flex-auto` 按内容分配
> - 定位方式：`fixed` 固定、`sticky` 粘性、`absolute` 绝对
> - `items-center` 是实现垂直居中的核心类

---

布局搭起来了，但卡片看起来还有点"平"。下一篇，我们来添加**边框、圆角和阴影**，让元素更有质感。


---

> 作者: Aphros  
> URL: https://blog.papergate.top/posts/04.%E5%B8%83%E5%B1%80%E4%B9%8B%E9%81%93flex-%E4%B8%8E-grid-%E7%9A%84%E5%8D%8F%E5%A5%8F/  

