CSS 布局技巧 - 实践记录 | 青训营

📝 CSS 布局技巧

💡一项投票调查中,关于 Web 开发人员合作中使他们最痛苦的技术,CSS 位居榜首。

CSS 确实臃肿且难以全面学习,但这只是因为它在过去 25 年间不断发展。它出现在 1996 年,netscape 那时还是 top 级别的浏览器。而如今 RWD ( Responsive Web Design ) 的想法也已有十多年,越来越多浏览器进入市场,它们又以不同方式去呈现 CSS。这导致 code 在一个浏览器中会运作,另一个又不运作,使得开发人员就必须在代码中编写一堆令人困惑的供应商前缀。

css 复制代码
.lame-sauce {
    -webkit-transition: all 4s ease; /* chrome */
    -moz-transition: all 4s ease; /* firefox */
    -ms-transition: all 4s ease; /* IE */
    -o-transition: all 4s ease; /* opera */
    transition: all 4s ease;
}

我也知道大家为何讨厌 CSS,所以我今天会介绍几个最近在做 side project 查资料时,看到国外大佬使用的一些 tips,或是我这一路上的所学。让我们学习用现代功能编写干净的 CSS,同时避免 3202 年还在编写一些意义不明的💩。

✒️ 学习 Box Model

当你刚开始学习 CSS 时,先不要急着用 bootstraptailwind 这样的框架。

它们确实非常sexy,可以快速帮助你获得一个漂亮的 UI。

但在你的 project 中使用这类东西,你不会学习到真正 CSS 基础知识。

当哪天你主意变了,想转换框架了,你还必须历经离婚一般的痛苦。

当你好好去学习 CSS 的基础,你将能更好更自由的去控制你的 code。

开始说回第一个标题。在我刚开始学习 CSS 时,我收到其中一个最好的建议就是去学习 Box Model,因为当你确实了解了这东西,这个语言中的其它所有内容对你来说,才会开始变得更有意义。

关于 Box Model,你可以先把每个 HTML 元素都视为一个 box,每个 box 都有自己的 widthheight,你可在 box 周围添加 padding 去挤压 content。在 box 外部可以添加 border,而周边还有个不可见的范围叫做 margin。这边我只是简单的带过,毕竟面向初学者的教程这部份是必讲的,更详细的内容去搜索引擎输入关键字去了解吧。

CSS 中与布局和位置相关的所有内容都受到 Box Model 的影响,所以关于如何应用就显得更为重要。

打开现代主流浏览器的开发人员工具基本上都有图化形界面可以参考。

这时候就引出了第二个我要讲的重点。

✒️ 调试时使用 Firefox

在调试时我通常不会选择用 Chrome,Firefox 的开发人员工具更为优秀,尤其是涉及 CSS 时。当你在检查一个元素时,能像在 chrome 中那样细分 Box Model,甚至也可以直接在上头编辑属性,而 Firefox 也会回馈给我影响 Box Model 的所有属性的详细信息。

它还在 HTML 中提供了一些有料的注释,例如当一个元素使另一元素溢出时会有 overflow 标记。

它也还为 flex 和 grid 布局提供了很好的图形。

✒️ Flexbox 好棒棒

而关于布局和元素间的定位,历史上一直是 CSS 最具挑战性的方面之一。就像现在我问你如何在水平和垂直方向居中一个 div 这种老掉牙的问题,请问阁下如何实现?

一种方法是给 child 绝对定位,然后用 top 和 left 属性把它移动到右下角,然后反向平移 50% 让它回到中间。但这种方法非常不直观,也让看的人也觉得这孩子脑袋是不是有病。(双关😁)

css 复制代码
.classic {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

而更好的方法就是使用 Flexbox ,就相当于给 div 一个 x、y 轴,当你想要置中时就让它沿着水平轴和铅直轴对齐就好了。

css 复制代码
.flex {
    display: flex;
    justify-content: center;
    align-items: center;
}

虽然 Flex 布局用起来挺简便的,但如果你今天遇到一个很庞大还很复杂的 UI,它的一个主要缺点就出来了。许多相交的行与列,会使你 HTML 中出现很多容器或包装元素。而这些元素没有语意意义,它们就只是待在那里减少你荧幕的阅读空间。

html 复制代码
<div class="row-wrapper">
    <div class="column-wrapper">
        <div class="box">💩</div>
        <div class="box">💩</div>
    </div>
    
    <div class="column-wrapper">
        <div class="box">💩</div>
        <div class="box">💩</div>
    </div>
    
    <div class="column-wrapper">
        <div class="box">💩</div>
        <div class="box">💩</div>
    </div>
</div>
css 复制代码
.row-wrapper {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.column-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

✒️ Grid 好棒棒

幸运的是,关于上述的问题,Grid 布局可以帮你解决。它不像 Felxbox 只能处理单独的行与列。而它呈现上看起来像旧时代的 table layout

你若使用 grid,你的 code 会相对友善一些。你可以将 children 定义为一堆列元素,行列宽度使用 template-columnstemplate-rows 定义。这里注意一下 fr ( fractional unit ) 值,它会与 grid 中其它列元素共享可用空间。我们还可以定义一些行,grid 内元素都将自动定位。和 Flexbox 或 table layout 相比,能减少许多 HTML 和 CSS 的 code 数量。

html 复制代码
<div class="grid">
    <i>😎</i>
    <i>😎</i>
    <i>😎</i>
    <i>😎</i>
    <i>😎</i>
    <i>😎</i>
</div>
css 复制代码
.grid {
    display: grid;
    grid-template-columns: 1fr 500px 1fr;
    grid-template-rows: 100px 200px;
    place-items: center;
}

✒️ Clamp 夹烂它

前面讲了一堆关于布局和定位的东西,而当我们在实现 RWD 时,我们要因应用户的设备萤幕可用空间调整画面呈现。这时候有很多方式能做到这点。

例如你可能有一篇文章,它首选宽度为 50%。但在小萤幕上,你可能会希望它宽度固定在 200px。而换到大萤幕时,又希望它固定在 800px。你可以透过 media query 来实现。

css 复制代码
article {
    width: 50%;
}

@media only screen and (max-width: 600px) {
    article {
        width: 200px;
    }
}

@media only screen and (max-width: 1200px) {
    article {
        width: 800px;
    }
}

唯一的缺点是,media query 会让你在 project 变大时想自杀。

但有个好消息是你可以通过使用 minmaxclamp 之类的函数去改写它,使它变得简洁利落。

css 复制代码
article {
    width: clamp(200px, 50%, 600px);
}

✒️ 用 emoji 挑战你的同事与上司 (读到这了,休息一下吧)

当你的 className 长到你想死的时候,不妨试试看用 emoji 代替。为你烦躁的一天增添些许乐趣。

✒️ 使用 Aspect Ratio

如果你曾经不得不编写一个保持一定宽高比的 RWD 图片或影片,这东西会make u shock😮

例如今天你想放一个 16x9 的视频,你可能会被迫写出一个像这样的鸟东西。

css 复制代码
.container-16x9 {
    position: relative;
    padding-top: 56.25%;
}

video {
    width: 100%;
    position: absolute;
    top: 0;
}

这时候,你可以试试看使用 aspect-ratio 这属性。

css 复制代码
video {
    width: 100%;
    aspect-ratio: 16/9;
}

但有个缺点是有些老地方不一定支援这属性,这部分就只能自己斟酌了。

✒️ 创造变量

除了减少代码量,更重要的一点是让 code 更灵活,这样在改写和重构时才不会让人想死。一种方式是用 CSS 自定义属性和变量。像下列 code 中,我在多个地方使用了相同颜色,当我们想变更颜色时,你会开始想钻个洞把自己埋进去。

css 复制代码
p {
    color: rgb(255, 0, 0);
}

h1 {
    color: rgb(255, 0, 0);
}

h2 {
    color: rgb(255, 0, 0);
}

而更好的方法是在 root 选择器上定义一个全局变量,然后在需要的地方进行引用。

css 复制代码
:root {
    --text-color: rgb(255, 0, 0);
}

p {
    color: var(--text-color);
}

h1 {
    color: var(--text-color);
}

h2 {
    color: var(--text-color);
}

你甚至可以在 tree 更深的的地方重新定义去 override

css 复制代码
:root {
    --text-color: rgb(255, 0, 0);
}

p {
    --text-color: rgb(0, 255, 0);
    color: var(--text-color);
}

亦或分解组合成得更细。

css 复制代码
:root {
    --r: 255;
    --g: 0;
    --b: 0;
    --text-color: rgb(var(--r), var(--g), var(--b));
}

p {
    --text-color: rgb(0, 255, 0);
    color: var(--text-color);
}

✒️ 花式 calc

现代 CSS 不是传统意义上的编程语言,但它确实能够使用 calc 函数进行基本运算。了解这点的你,现在你可以快乐的和小学生一起做算数题了。

css 复制代码
calc(2px + 2px)

单位甚至不用相同。

css 复制代码
width: calc(100vw - 80px);
font-size: calc(1rem * 1.25);
padding: calc(5% + 2px);

更酷的是,你可以这样做动画,就能在不增加CSS代码的情况下实现相同效果。

html 复制代码
<style>
    .drop {
        animation: dropIn 1s ease forwards;
        animation-delay: calc(var(--order) * 100ms);
    }
    @keyframes dropIn {
        from { transform: translateY(-500px); }
        to { transform: translateY(0); }
    }
</style>
<i class="drop" style="--order: 1">1️⃣</i>
<i class="drop" style="--order: 2">2️⃣</i>
<i class="drop" style="--order: 3">3️⃣</i>

✒️ 状态管理计数器

虽然刚才说 CSS 不是一种编程语言,但它实际上有一个内置的状态管理机制,你可以追踪你 code 中的运行计数,而不用多编写一行 JavaScript。

若你想对标题进行编号,最白痴的方法就长这样。

html 复制代码
<h1> 1. apple </h1>
<h1> 2. bird </h1>
<h1> 3. dog </h1>

这时当你突然想要从中间补上一个新的标题时,就会有种想去顶楼体验自由落体的感觉。

html 复制代码
<h1> 1. apple </h1>
<h1> 2. bird </h1>
<h1> 3. cat </h1>   ←←←←←←←←←←←←←←
<h1> 3. dog </h1>

聪明的方法是去 root 创建一个计数器,用 counter-reset 给一个你喜欢的名字,然后在需要应用的地方去用 counter-increment 递增它,它将从 0 开始将 1 添加到 dom 的每个 h1 元素。

css 复制代码
:root {
    counter-reset: headings;
}

h1 {
    counter-increment: headings;
}

h1::before {
    content: counter(headings);
}

✒️ focus-within 的魔力

构建复杂下拉式选单时,你可能会认为这必须涉及一些 JavaScript 来管理选单的开启和关闭 state,但你可能也会想你若只透过纯 CSS 可以做到多远。

大家都很熟悉 focus 这伪类,当你进行表单输入或单击按钮时,该伪类会应用于元素上。问题是,在构建下拉式选单时,你可能会使用 focus 打开选单,但当你点击选单内的某个内容时,它会失去焦点并关闭。这就为何你会使用 JavaScript 去管理 state。

但有趣的是,有个伪类叫做 focus-within ,如果任何 children 也有这伪类,它就会保持 active 的状态。

这样简单的一个功能,可以多减少几行用于管理打开和关闭 state 的 JavaScript 代码。

css 复制代码
.dropdown {
    opacity: 0;
    visibility: hidden;
}

button:focus-withing .dropdown {
    opacity: 1;
    visibility: visible;
}

✒️ 結語

这样,我们就简单提完了几个 CSS 技巧。还记的刚开头提到的浏览器供应商前缀吗,那东西就像疱疹一样不会消失。但幸运的是我们有些有料的小工具可以让它变得不成问题。

其中一个工具就是 PostCSS (Icon看起来像光X会来的),它使用一个叫做 auto prefixer 的工具,来自动添加所有供应商前缀。此外,它还允许你使用现代 CSS 功能,会帮你自动转换代码,以尽可能让所有目标浏览器都支援。

读完这篇文章的你,日后还可以去看看 Sassstylusless 等预处理器,那部份之后有缘我会再分享的。

相关推荐
Find23 天前
MaxKB 集成langchain + Vue + PostgreSQL 的 本地大模型+本地知识库 构建私有大模型 | MarsCode AI刷题
青训营笔记
理tan王子23 天前
伴学笔记 AI刷题 14.数组元素之和最小化 | 豆包MarsCode AI刷题
青训营笔记
理tan王子23 天前
伴学笔记 AI刷题 25.DNA序列编辑距离 | 豆包MarsCode AI刷题
青训营笔记
理tan王子23 天前
伴学笔记 AI刷题 9.超市里的货物架调整 | 豆包MarsCode AI刷题
青训营笔记
夭要7夜宵25 天前
分而治之,主题分片Partition | 豆包MarsCode AI刷题
青训营笔记
三六1 个月前
刷题漫漫路(二)| 豆包MarsCode AI刷题
青训营笔记
tabzzz1 个月前
突破Zustand的局限性:与React ContentAPI搭配使用
前端·青训营笔记
Serendipity5651 个月前
Go 语言入门指南——单元测试 | 豆包MarsCode AI刷题;
青训营笔记
wml1 个月前
前端实践-使用React实现简单代办事项列表 | 豆包MarsCode AI刷题
青训营笔记
用户44710308932421 个月前
详解前端框架中的设计模式 | 豆包MarsCode AI刷题
青训营笔记