在前面的学习里,我们已经对元素的基本展示方式有了初步的了解。这节我们来进一步学习影响元素框模型表现和彼此间隔的核心CSS属性,包括内边距(padding)、边框(border)、外边距(margin),以及特殊的轮廓(outline)。

当我们用 HTML 写一个网页时,可以把页面上的每个元素想象成一个“盒子”。无论是文本、图片、按钮,还是表格,它们的本质都是一个矩形框。这个“盒子”模型,在网页排版和布局中非常重要。如果你刚开始学习前端,理解盒模型能帮你更好地控制元件的大小和相互之间的间距。
简单来说,盒模型把每个元素分成四个部分,从内到外分别是:内容区域(content)、内边距(padding)、边框(border)、外边距(margin)。内容区域就是元素真正显示内容的地方,内边距是内容和边框之间的空白,边框是围在元素周围的线,外边距则是元素和其它元素之间的间距。
你可以把盒子想象成一个包裹着物品的盒子:物品本身是内容,填充物是内边距,包住盒子的纸壳是边框,而盒子外面和其它盒子之间留的空隙就是外边距。不同的组合和设定,会让元素在页面上呈现不同的效果。
内边距(padding)就是内容区和边框之间的空间。可以把它想象成内容和边框之间的“缓冲区”,让内容不会贴着边框显示,看起来更舒服。
我们想要给元素加内边距时,最常用也最简单的方法就是用 padding 这个属性。只要给它一个数值(比如像素或百分比),就能设置内边距的大小。
注意,内边距不能是负数。大多数元素默认其实是没有内边距的,如果不设置的话,内容就会直接贴着边框,视觉上显得有点“挤”。所以,很多时候当我们给元素加了边框后,也会顺手加一点内边距,让内容整体看起来更舒展、更舒服。
最简单的情况,我们只写一个 padding 值,这样四个方向的内边距都会变成一样大。比如如果你希望所有的二级标题(h2)上下左右都有2倍字体大小的内边距,你可以这样写:
|<!DOCTYPE html> <html> <head> <style> h2 { padding: 2em; background-color: #f0f0f0; } </style> </head> <body> <h2>这是一篇技术文章的标题</h2
有一点需要特别注意:当你给元素加了内边距(padding),它的背景色会自动延伸到内边距的区域里。也就是说,如果背景色不是透明的,你就会看到背景把内边距部分也填满了。如果背景是透明的,那内边距添加的只是透明空间,看不出来颜色的变化。 利用这个特性,其实你可以通过调整内边距,让背景色看起来“包裹”得更紧或者更松。
如果你不希望背景色延伸到内边距,可以用 background-clip 这个 CSS 属性来实现。别担心,我们后面会学到怎么使用它。
实际写样式的时候,我们常常会遇到:希望某个元素的上下左右内边距都不一样。例如,你可能想让一级标题的上面有10像素的空隙,右边20像素,下面15像素,左边5像素。这时候,我们可以直接给 padding 填四个数值:
|<!DOCTYPE html> <html> <head> <style> h1 { padding: 10px 20px 15px 5px; background-color: #e8f4f8; } </style> </head> <body
这四个值的顺序很有讲究,必须按照“上 右 下 左”来写,也就是从上面开始,顺时针一圈。这么多方向不太好记?可以用“TRBL”这个缩写帮忙,分别是 Top(上)、Right(右)、Bottom(下)、Left(左)。你可以想象成 Trouble(麻烦),这样一联想顺序就不会忘啦! 这里的顺序,其实就是现实中页面的物理方向(上、右、下、左),和你写字的方向没关系。
设置内边距时,CSS 其实很灵活。每条边选什么单位完全由你决定,不一定都用 px,也可以像下面这样各用各的:
|<!DOCTYPE html> <html> <head> <style> .article-title { padding: 14px 5em 0.1in 3ex; background-color: #fff3cd; border: 2px solid #ffc107; } </
在实际开发中,常常会遇到需要为元素四个方向设置内边距(padding),但四个值有时候并不全都不一样。比如,我们希望段落的上下内边距都是 0.25em,左右则是 1em。如果按照最“啰嗦”的写法,需要写成 padding: 0.25em 1em 0.25em 1em;,其实这样并不高效。
好在 CSS 提供了非常智能的“值简写”规则,可以让我们省事不少。简单来说,如果你:
下面举几个常见场景,帮助你更直观地理解值复制规则:
|<!DOCTYPE html> <html> <head> <style> .header { padding: 0.25em 0 0.5em; background-color: #e3f2fd; margin-bottom: 10px; } .sidebar { padding:
不过,值复制机制有时候会有点小麻烦。比如说,你想让标题的上下内边距是10像素,左右都是20像素。这个时候,不能只用两个或者三个值快速写完,只能写成 padding: 10px 20px 20px 10px;,虽然有点重复,但这是实现这种效果的唯一方法。
再比如,你想四周的内边距都是0,只有左边有3em,这时候也只能写成 padding: 0 0 0 3em;,虽然看起来有点啰嗦,但其实很正常,我们只需要记住就好了。
有时候用内边距(padding)来分隔内容区域,可能没有用外边距(margin)那么直接,不过它也有自己的优势。比如,你想让每个段落之间看起来有“一个空行”的距离,并且每段文字都有左边框标记,可以这样写:
|<!DOCTYPE html> <html> <head> <style> p { margin: 0; padding: 0.5em 0; border-bottom: 1px solid #ddd; border-left: 3px double #333;
在这个例子里,我们把段落的外边距(margin)都去掉了,只设置了上下各 0.5em 的内边距(padding)。这样一来,段落之间的“距离”其实就是两个 0.5em 内边距相加,总共 1em。为什么不直接用 margin 控制间距呢? 这样处理的好处是,结合下边框(border-bottom),每个段落之间的分隔线会首尾衔接,看起来就像一条连续的线,非常整齐。此外,给每个段落的左边加上了一个双线边框,也能让内容显得更有层次感和视觉引导效果。
在 CSS 里,我们可以用四个非常直观的单独属性来分别设置某一侧的内边距,分别是:padding-top(上)、padding-right(右)、padding-bottom(下)、padding-left(左)。这些属性的默认值都是 0,不能是负数,而且都支持动画效果。
举个例子,比如你只想让二级标题的左边多一点空间,其实没必要写成 padding: 0 0 0 3em;,直接用 padding-left 属性就可以了:
|<!DOCTYPE html> <html> <head> <style> h2 { padding-left: 3em; background-color: #e1f5fe; } </style> </head> <body> <h2>这篇技术文章的重要章节</h2
这些单边属性的工作方式与它们的名称一致。比如,padding: 0 0 0 0.25in; 和 padding-left: 0.25in; 会产生相同的结果。同样的,padding: 0.25in 0 0; 和 padding-top: 0.25in; 也是等价的。
同样地,我们可以在同一个规则中使用多个单边属性。比如,我们可以这样设置二级标题的左边和底部有内边距,但顶部和右边没有:
|<!DOCTYPE html> <html> <head> <style> h2 { padding-left: 3em; padding-bottom: 2em; padding-right: 0; padding-top: 0; background-color: #f3e5f5; }
其实上述实现方式是可以满足需求的,但如果追求代码简洁,我们可以直接用简写属性 padding: 0 0 2em 3em;,这样的写法和上面的效果是一样的。在实际开发中,两者没有本质差别,你可以根据你的习惯和代码可读性自行选择。
在我们的学习中,你会发现:CSS 里的许多物理属性都有相应的逻辑属性,这些属性的命名很有规律。比如,block-size 和 inline-size 分别对应元素的高度和宽度,而内边距则配有四个逻辑属性——分别作用于块方向和行内方向的起始与结束。我们将它们叫做“逻辑属性”,因为它们不会直接绑定在上、下、左、右这些物理方向,而是根据文本的书写模式自动适配。
具体来说,padding-block-start 表示块方向起始(通常是上方)的内边距,padding-block-end 表示块方向结束(通常是下方),padding-inline-start 和 padding-inline-end 对应行内方向的开始和结束(通常是左侧和右侧)。这些属性的价值在于,无论文本方向如何变化,都能保证你设置的内边距始终如你所愿。
例如,无论从左到右还是从右到左书写,或是切换为竖排书写,内边距的表现都不会出错,让你的界面在多语言和不同布局下更加稳定和通用。
|<!DOCTYPE html> <html> <head> <style> p { padding-block-start: 0.25em; padding-block-end: 0.25em; padding-inline-start: 1em; padding-inline-end: 1em; background-color: #f0f8ff
要注意,逻辑内边距像 padding-inline-start: 10% 这样的百分比数值,其实都是按照盒子的实际宽度来算的,不管文本方向怎么变。打个比方,如果你的容器宽度是1000像素,写 padding-inline-start: 10%,实际就是有100像素的内边距。
每一条边都分别写内边距其实蛮麻烦的,好在 CSS 提供了两个简写属性,可以帮我们一下子设置好:padding-block 控制上下(块方向)的内边距,padding-inline 控制左右(行内方向)的内边距。下面的写法和上面的效果是一样的:
|<!DOCTYPE html> <html> <head> <style> p { padding-block: 0.25em; padding-inline: 1em; background-color: #fff5f5; } </style> </head> <body
padding-block 和 padding-inline 这两个属性都支持一个或两个值。如果只设置一个值,则起始边和结束边的内边距都相同;如果设置两个值,第一个值对应“起始边”(start),第二个值对应“结束边”(end),这样可以分别控制。例如,下例设置块方向的起始边内边距为 10px,结束边为 1em:
|<!DOCTYPE html> <html> <head> <style> p { padding-block: 10px 1em; background-color: #f5f5dc; } </style> </head> <body> <p
目前,逻辑内边距(比如 padding-block 和 padding-inline)还没有像传统 padding 属性那样的简写方式,不能一次性用四个值设置四个方向。也就是说,不能直接用 padding 加个特殊关键字来设置逻辑内边距。现在最省事的方法,就是分别用 padding-block 和 padding-inline 来设置,虽然麻烦点,但当前还没有更简单的写法被标准采纳。
我们可以用百分比给元素设置内边距。这里的百分比,其实是参考元素父级内容区域的宽度来算的。也就是说,父元素宽度变了,内边距跟着变,非常适合响应式布局的需求。比如,我们想让一个段落的内边距是父元素宽度的10%,可以这样写:
|<!DOCTYPE html> <html> <head> <style> .container { width: 600px; border: 1px dotted #999; } .narrow-container { width: 300px; border:
在上面的例子中,你可能注意到,采用百分比内边距时,无论是左右还是上下的内边距,都会随着父元素宽度的变化而动态调整。这其实是 CSS 规范中的标准行为:使用百分比时,无论是 padding-top、padding-bottom、padding-left,还是 padding-right,百分比始终是基于父元素内容区的宽度来计算的,而不是高度。 乍一看,上下内边距跟随宽度变化似乎有些反直觉,但这种规范考虑到了布局的稳定性。如果上下内边距基于父元素高度,那么由于高度通常依赖于内容和内边距,这会导致浏览器在计算时产生循环依赖,甚至可能导致无限递归,页面无法正确渲染。
因此,规范规定,所有方向的百分比内边距都以父元素宽度为基准。这不仅保证了布局的可靠性,还能让我们方便地通过 padding: 10% 等简写方式实现四周等比的流式内边距,而不用担心由于高度动态变化带来的问题。
对于那些没有指定宽度的元素来说,元素的总宽度(包括内容、内边距等)还是会受到父容器宽度的影响。这种机制,让我们能够创建响应式布局:随着父元素或浏览器窗口的宽度变化,内边距会相应调整,使页面在不同设备下保持良好的观感和可用性。当你希望页面具有一定的弹性和自适应能力时,使用百分比内边距是非常有效的选择。
此外,百分比值也可以与绝对长度单位(如 em、px 等)混合使用。例如,如果你想让二级标题的上下内边距固定为 0.5em,而左右内边距随父容器宽度变化并占 10%,可以这样设置:
|<!DOCTYPE html> <html> <head> <style> .parent { width: 800px; } h2 { padding: 0.5em 10%; background-color: #ffe4b5; } </style
在这个例子中,虽然上下内边距在任何情况下都保持不变,但左右内边距会根据父元素的宽度而变化。
前面我们讲的内边距,主要都是给块级元素(比如 div、段落、标题等)使用的,这些元素本身就会独占一整行,内边距的效果也很直观。但如果你把内边距加到了行内非替换元素(比如 <strong>、<span> 这类会在同一行显示的标签)上,情况就有些不一样了。比如说,我们希望给加粗的文字(<strong>标签)设置上下内边距,直觉上觉得文字应该会离上下多出一些距离,但实际效果可能和你想的不一样:
|<!DOCTYPE html> <html> <head> <style> strong { padding-top: 25px; padding-bottom: 50px; } </style> </head> <body> <p>这是一段普通的文字,其中<
虽然规范允许你这样设置,但当我们把内边距应用到行内非替换元素时,它实际上并不会影响行高。尤其是在没有设置背景色的情况下,内边距区域是透明的,看起来几乎没有任何变化。这是因为行内非替换元素的内边距不会让元素在垂直方向上占据更高的空间。
需要注意的是,如果你为这些元素设置了背景色,再添加内边距时,背景色会延伸到内边距的上下区域,实际效果会像这样:
|strong { padding-top: 0.5em; padding-bottom: 0.5em; background-color: #c0c0c0; }
|<p>这是一段包含<strong>强调文字</strong>的内容。注意强调文字的上下都有背景延伸。</p>
虽然行高并不会因此改变,但背景色会延伸到内边距区域,所以每一行内的背景部分最终会与上一行的背景重叠,这是正常现象。 需要注意的是,这种行为只影响行内非替换元素的上下方向;左右方向又是另一种情况。我们先来看一下当一个较小的行内非替换元素与其他文字在同一行时,如果为它设置左右内边距,这部分空间会直接体现出来:
|strong { padding-left: 25px; background: #c0c0c0; }
|<p>注意行内元素<strong>强调文字</strong>前面的额外间距。</p>
你会发现,在行内非替换元素前面,前一个单词的末尾和该元素的背景边缘之间会出现额外的空隙。其实,我们可以通过在行内元素的两侧都增加内边距,让这种额外空间同时出现在元素的左右两端:
|<!DOCTYPE html> <html> <head> <style> strong { padding-left: 25px; padding-right: 25px; background: #c0c0c0; } </style> </head> <body
正如我们预期的那样,行内元素的左右两侧会额外留出一些空间,而上下方向上则没有这样的空隙。
不过,当一个行内非替换元素内容较长,需要换行显示时,情况就会有所不同。此时,左侧的内边距只会出现在元素的起始处,右侧内边距只会出现在元素的结束处。
默认情况下,这些内边距不会出现在每行的左右两端。另外你还会发现,如果不设置内边距,文本的换行位置可能会有所变化。padding 属性其实只是通过调整元素内容起始的位置来间接影响换行效果。
我们同样可以给替换元素设置内边距。很多人可能没想到,给图片这类元素添加内边距时会发生什么,比如下面这样:
|<!DOCTYPE html> <html> <head> <style> img { background: #c0c0c0; padding: 1em; border: 2px dashed #666; } </style> </head>
不论替换元素是块级还是行内,内边距都会包裹在其内容四周,背景色也会延伸覆盖到内边距的区域。你会发现,内边距不仅让元素的边框(比如这里的虚线边框)变得离内容更远,也让整个元素看起来更大。
还记得我们之前提到,给普通行内元素设置内边距,并不会影响文本的行高吗?但对于替换元素来说,规则是不同的——行内替换元素(比如 img)的内边距其实会直接影响行高:
|<!DOCTYPE html> <html> <head> <style> img { padding: 1em; background: #e0e0e0; } </style> </head> <body> <p>这是一段包含<img
我们需要注意的是,就算图片没有加载出来,或者元素的高度、宽度被设为0,内边距依然会在元素本应出现的位置被渲染出来——哪怕这个位置看不见内容或完全没有尺寸。
在元素的内边距(padding)外侧,就是元素的边框(border)。边框是包裹着元素内容和内边距的一圈线条,它能帮助内容与外部区分开。默认情况下,元素的背景(background)会一直延伸到边框的外沿,但不会延伸到更外层的“外边距”(margin)区域。 也就是说,边框是在元素背景的最外层,看上去像是背景的“覆盖层”。这种关系在边框带有透明区域时(比如点线、虚线等非实心样式)特别明显,背景颜色会在线条的空隙中显现出来。
边框有三个最重要的属性:宽度(border-width)、样式(border-style)和颜色(border-color)。其中,边框的默认宽度是 medium,大约等于3像素。但如果没有设置边框样式(即 border-style: none,这是默认值),那么不管宽度和颜色设得如何,边框在页面上都不会显示。
边框的默认颜色是 currentcolor,即当前元素的文字颜色。如果不给边框单独指定颜色,它会自动和文字颜色一致。这样即使一个元素没有文字内容(如只包含图片的容器),边框颜色也会根据父级元素的文字颜色继承。例如,如果父元素 <body> 设置了 color: purple;,那么没有特殊声明的子级元素边框也会变成紫色,除非被其他规则覆盖。
CSS 规定,背景区域默认会延伸到边框的外边缘,这样在遇到点状或虚线边框时,背景色就能出现在这些空隙之间。如果想让背景只显示在边框以内,可以通过 background-clip 属性进行控制。
我们先来看边框的“样式”这一属性。因为如果没有指定边框样式(border-style),即使设置了宽度和颜色,边框也不会显示。border-style 属性定义了边框的具体表现形式。CSS 标准为它提供了 10 种取值类型,包括默认值 none(无边框)。这个属性不会继承。还有一个特殊值 hidden,它和 none 类似,但在表格布局等特殊情况下,两者表现略有不同。
接下来我们看看常见的几种边框样式:
|<!DOCTYPE html> <html> <head> <style> div { margin: 10px; padding: 10px; background: #f5f5f5; } .solid { border-style: solid; border-width: 3
double(双线)边框的显示,其实是把 border-width 拆分为两条线加中间的间隔——但具体两条线和空隙分别多宽,浏览器会自己决定,CSS 规范并没有详细规定。比如,两条线可以等宽,也可能一粗一细,中间空隙也可能宽或窄,这些细节开发者是无法直接控制的,完全看浏览器实现。
类似地,inset、outset、groove 和 ridge 这些立体效果的边框,颜色的生成规则也各不相同。浏览器一般会根据设置的 border-color,自动调整不同边的明暗对比来模拟立体,但具体是哪几条边加深、哪几条边变亮,以及明暗的幅度,也都不是固定公式。
比如,有些浏览器会让右下边变亮,左上边变暗,有些则处理得更柔和,这就导致在不同环境下的表现可能不太一样。
正因为这种不确定性,如果你想让边框颜色精确可控,通常建议直接设置每条边的颜色,而不是完全依赖 outset 等样式让浏览器决定。
举个例子:假如我们想让未访问过的链接中的图片有个“按钮凸起”的边框效果,可以这样写:
|<!DOCTYPE html> <html> <head> <style> a:link img { border-style: outset; border-width: 3px; } </style> </head> <body> <a
有时候我们希望元素的每一条边(上、右、下、左)都拥有不同的边框样式。其实 CSS 完全支持这种需求,我们可以分别为四条边设置不同的样式。比如下面的例子,就为上边框设置了实线、右边框是虚线、下边框是点状线、左边框再次是实线:
|<!DOCTYPE html> <html> <head> <style> .aside { border-style: solid dashed dotted solid; border-width: 3px; border-color: #666; padding: 10px; margin: 10
就像我们学习设置内边距时看到的那样,所有关于值复制的规则都适用于边框样式,就像它们对内边距一样。因此,下面的两个语句会产生相同的效果:
|p.new1 { border-style: solid none dashed; } p.new2 { border-style: solid none dashed none; }
在实际开发中,我们经常需要只为元素的某一侧添加边框样式,而不是四周都添加。此时,可以使用单独指定某一边的边框样式属性。常用的单边边框样式属性包括:border-top-style(上边框)、border-right-style(右边框)、border-bottom-style(下边框)和 border-left-style(左边框)。这些属性的默认值都是 none,并且它们无法设置动画效果。
单边边框样式属性的命名非常直观,含义也清晰。例如,如果你只需要设置元素下边框的样式,可以直接使用 border-bottom-style。
实际应用中,常见的做法是将简写属性 border-style 和单边属性结合使用。例如,若你希望标题有三边实线边框,并且左侧没有边框,可以这样设置:
|<!DOCTYPE html> <html> <head> <style> h1 { border-style: solid; border-left-style: none; border-width: 3px; border-color: #666; padding: 10px; }
实现这个三边边框有两种等价写法:你可以用 border-style: solid solid solid none;,也可以用 border-style: solid; border-left-style: none;。
不过要注意,如果用第二种方式,必须把单独的 border-left-style: none; 写在 border-style: solid; 的后面。原因是 border-style: solid 实际上等同于同时设置四个方向的样式,等同于 border-style: solid solid solid solid;,它会覆盖掉写在前面的单独样式。
如果顺序颠倒,就会被简写属性覆盖,达不到预期效果。
如果你想让边框样式随文本书写方向自动适应,可以用逻辑属性。常用的逻辑边框属性有:border-block-start-style(块轴起始,比如从上)、border-block-end-style(块轴结束,比如下)、border-inline-start-style(行内起始,比如左/右,和书写方向相关)、border-inline-end-style(行内结束)。
这些属性可以让你的布局更好地兼容不同语言的书写模式。
此外,border-block-style 和 border-inline-style 是它们的简写形式,可以分别设置块轴和行轴的起止边框样式。例如,border-block-style: solid double; 会分别设置块轴的起始和结束(比如上和下)为 solid 和 double。和 padding-block、padding-inline 类似,写两个值时,顺序是“起始 结束”。下面的 CSS 就是个例子:
|<!DOCTYPE html> <html> <head> <style> p { border-block-style: solid double; border-inline-style: dashed dotted; border-width: 5px; border-color: #666; padding: 10px
你可以用更详细的方式获得相同的结果:
|p { border-block-start-style: solid; border-block-end-style: double; border-inline-start-style: dashed; border-inline-end-style: dotted; }
这两种方式的唯一区别是你需要打得的字符数量,所以到底使用哪种方式取决于你自己的个人喜好。
在给边框设置好样式后,接下来就是为它指定宽度。最简单的办法是用 border-width 属性,或者通过类似的属性分别设置每一条边的宽度,比如 border-top-width(上边框)、border-right-width(右边框)、border-bottom-width(下边框)和 border-left-width(左边框)。
这些属性的用法类似于设置外边距,你可以灵活控制每一边的边框粗细。
你可以用四种方式为边框设置宽度:直接指定具体长度值,比如 4px 或 0.1em;或者使用关键词,如 thin(细)、medium(中,默认值)、和 thick(粗)。
按照标准,thin 对应1像素、medium 是3像素、thick 则是5像素,数值从细到粗一目了然,非常容易理解和记忆。
|<!DOCTYPE html> <html> <head> <style> div { margin: 10px; } .thin-border { border-style: solid; border-width: thin; border-color: #666; padding: 10
假设一个段落有背景色和边框样式设置:
|<!DOCTYPE html> <html> <head> <style> p { background-color: #e0e0e0; border-style: solid; border-width: thick; border-color: #666; padding: 10px; } </
边框的默认宽度是 medium,我们可以很容易地修改这个值。实际上,你甚至可以把边框宽度设置得非常大,比如1000px,虽然一般并不推荐这样做。需要注意的是,border-width 作为盒模型的一部分,会直接影响元素的实际大小。
为不同方向的边框指定宽度有两种常用方法。第一种是直接用具体属性,比如 border-bottom-width 单独设置。第二种方法是在 border-width 里依次写入多个值,按照上(Top)、右(Right)、下(Bottom)、左(Left)的顺序依次应用,即常说的 TRBL 顺序:
|<!DOCTYPE html> <html> <head> <style> h1 { border-style: dotted; border-width: thin 0px; border-color: #666; padding: 10px; } p {
如果你希望根据文本的书写方向来设置元素的边框宽度,可以使用 CSS 的逻辑属性。border-block-width 用于控制块方向(通常是上下)的边框宽度,而 border-inline-width 用来设置行内方向(通常是左右)的边框宽度。这两个属性都可以接收一个值或两个值:如果是两个值,第一个代表“起始边”,第二个代表“结束边”,即“开始 结束”的顺序。
此外,还可以使用更具体的逻辑属性,比如 border-block-start-width、border-block-end-width、border-inline-start-width 和 border-inline-end-width,分别精确设置每一侧的宽度。你既可以分别设置每一边,也可以使用简写属性一次性设置一组方向。
下面两种写法实际效果完全一致:
|/* 方式一 */ p { border-block-width: thick thin; border-inline-width: 1em 5px; } /* 方式二 */ p { border-inline-start-width: 1em; border-inline-end-width: 5px; border-block-start-width: thick; border-block-end-width: thin; }
到目前为止,我们只讨论了使用可见边框样式,比如 solid 或 outset。让我们考虑一下当把 border-style 设置为 none 时会发生什么:
|p { border-style: none; border-width: 20px; }
即使你将边框宽度设置为20像素,但如果 border-style 设为 none,那么这个边框实际上完全不会显示,也不会占据任何宽度。此时,边框不仅不可见,实际上在页面布局中完全不存在。为什么会这样呢?
这是因为在CSS规范中,边框样式被设置为 none 时,这条边框就等同于未定义,宽度无论设置多少都会被自动视作0。只有当边框样式为可见类型(比如 solid、dashed 等),浏览器才会按照你设置的宽度来渲染边框。
这种机制非常重要,也容易被忽略。初学者或开发者常常只设置了 border-width,却忘了声明 border-style,结果发现边框并没有出现。比如,下面的代码中,所有 <h1> 元素其实都不会显示任何边框,更不会有20像素宽的效果:
|h1 { border-width: 20px; }
由于 border-style 的默认值是 none,不声明样式完全等同于声明 border-style: none。因此,如果你想让边框出现,你需要声明你需要的边框样式。
与边框的其他方面相比,设置颜色相当简单。CSS使用物理简写属性 border-color,它可以一次接受多达四个颜色值。如果没有提供四个值,值复制规则就会生效。所以,如果你想让 <h1> 元素有细灰色上下边框和粗绿色左右边框,以及 <p> 元素周围有中等灰色边框,以下样式就足够了:
|```html <!DOCTYPE html> <html> <head> <style> h1 { border-style: solid; border-width: thin thick; border-color: gray green; padding: 10px; }
单个颜色值会被应用到所有四个边,就像上面例子中的段落一样。另一方面,如果你提供四个颜色值,你可以让每个边有不同的颜色。可以使用任何类型的颜色值,从命名颜色到十六进制和HSL值:
|<!DOCTYPE html> <html> <head> <style> p { border-style: solid; border-width: thick; border-color: black hsl(0 0% 25% / 0.5) #808080 rgb(128
如果你不声明颜色,默认值是 currentcolor,它总是元素的前景色。因此,以下声明会显示如下效果:
|<!DOCTYPE html> <html> <head> <style> p.shade1 { color: gray; border-style: solid; border-width: thick; } p.shade2 { color: gray;
结果是第一个段落有灰色边框,使用了段落的前景色。然而,第二个段落有黑色边框,因为那个颜色是使用 border-color 显式分配的。
就像边框样式和宽度一样,逻辑属性对应物理属性:两个简写属性,四个长属性。border-block-color 和 border-inline-color 分别用于设置块方向和行内方向的边框颜色。还有四个单独的属性:border-block-start-color、border-block-end-color、border-inline-start-color 和 border-inline-end-color。
因此,下面的两个规则会产生完全相同的结果:
|/* 方式一 */ p { border-block-color: black green; border-inline-color: orange blue; } /* 方式二 */ p { border-inline-start-color: orange; border-inline-end-color: blue; border-block-start-color: black; border-block-end-color: green; }
你可能还记得,如果边框没有样式,它就没有宽度。然而,在某些情况下,你可能想要创建一个不可见的边框,但仍然有宽度。这就是边框颜色值 transparent 的用武之地。
假设我们想让一组三个链接默认有不可见的边框,但当链接被悬停时看起来像内嵌。我们可以通过在非悬停状态下使边框透明来实现这一点:
|<!DOCTYPE html> <html> <head> <style> a:link, a:visited { border-style: inset; border-width: 3px; border-color: transparent; padding: 5px; margin:
这样设置会产生如下效果:transparent 作为边框颜色时,虽然看不到边框,但边框所占的空间依然存在。这样一来,如果你后续需要让边框变得可见,或者在悬停等状态改变边框颜色,就不会导致页面内容突然移动,用户体验更加平稳。
你可能已经注意到,像 border-color 和 border-style 这样的简写属性虽然方便,但在有些场景下并不能满足实际需求。例如,你想让所有的 <h1> 元素只在底部显示一个较粗、灰色、实线的边框,而不是四周都有边框。仅靠我们前面介绍的那些通用属性,这种需求其实很难实现。
好在 CSS 为我们提供了更灵活的方法:
|<!DOCTYPE html> <html> <head> <style> h1 { border-bottom: thick solid gray; padding: 10px; } </style> </head> <body> <h1
这样设置之后,只有元素的底部会显示边框,其他三条边还是默认没有边框(因为默认值是 none),所以视觉上只有底部有一条线。
你可能已经发现了,CSS 不仅有四个基本的物理简写属性(border-top、border-right、border-bottom、border-left),还有对应的四个逻辑属性(border-block-start、border-block-end、border-inline-start、border-inline-end)。这些属性的命名就是直观表达它们所作用的位置。
通过这些属性,我们可以很灵活地给不同方向加上不同样式的边框,比如下面这样:
|<!DOCTYPE html> <html> <head> <style> h1 { border-left: 3px solid gray; border-right: green 0.25em dotted; border-top: thick goldenrod inset; border-bottom: double rgb
如你所见,实际值的顺序并不重要。以下三个规则会产生完全相同的边框效果,你选择哪种写法都可以:
|h1 { border-bottom: 3px solid gray; } h2 { border-bottom: solid gray 3px; } h3 { border-bottom: 3px gray solid; }
你也可以省略一些值,让它们的默认值生效,比如这样:
|h3 { color: gray; border-bottom: 3px solid; }
由于没有声明边框颜色,会使用默认值(currentcolor)。只要记住,如果你省略边框样式,默认值 none 会阻止边框存在。
相比之下,如果你只设置样式,你仍然会得到一个边框。假设你想要一个顶部的虚线边框,并且愿意让宽度默认为 medium,颜色与元素的文字本身相同。在这种情况下,你只需要以下标记(如下代码所示):
|<!DOCTYPE html> <html> <head> <style> p.roof { border-top: dashed; padding: 10px; } </style> </head> <body> <p class
还要注意,由于这些边框边属性中的每一个只适用于特定的一边,没有值复制的可能性——这没有任何意义。每种类型的值只能有一个:也就是说,只有一个宽度值,只有一个颜色值,只有一个边框样式。所以不要尝试声明多个相同类型的值:
|h3 { border-top: thin thick solid purple; } /* 两个宽度值 - 无效 */
现在,我们来看看最简短的简写边框属性:border,它同时影响元素的所有四个边。这个属性的优点是它非常紧凑,尽管这种简洁性会带来一些限制。在我们担心这些限制之前,让我们看看 border 是如何工作的。如果你想让所有 <h1> 元素都有一个厚的银色边框,以下声明会显示如下效果:
|<!DOCTYPE html> <html> <head> <style> h1 { border: thick silver solid; padding: 10px; } </style> </head> <body> <h1
border 的缺点是,你只能定义一个全局的样式、宽度和颜色。你为 border 提供的值会同样应用到所有四个边。如果你想让单个边的边框不同,可以使用其他边框属性。不过,你也可以利用层叠的优势:
|<!DOCTYPE html> <html> <head> <style> h1 { border: thick goldenrod solid; border-left-width: 20px; padding: 10px; } </style> </head>
如你所见,第二条规则覆盖了第一条规则中为左边框设置的宽度,因此左边框宽度由 thick 变成了 20px。
在使用简写属性时,你仍然要注意:如果省略了某些取值,浏览器会使用默认值,这可能会导致意想不到的结果。比如下面这个例子:
|h4 { border: medium green; }
这里,我们忘记分配 border-style,这意味着会使用默认值 none,因此没有任何 <h4> 元素会有任何边框。
处理边框与行内元素时,其实与我们之前讨论过的内边距和行内元素的规则非常类似。下面我们再简单地回顾一下相关内容。 首先,不管你在行内元素上添加多粗的边框,都不会改变元素的行高。下面我们以粗体文本为例,分别给它设置块起始和块结束的边框:
|<!DOCTYPE html> <html> <head> <style> strong { border-block-start: 10px solid hsl(216, 50%, 50%); border-block-end: 5px solid #AEA010; padding: 5
正如前面所看到的,为块的开始和结束添加边框并不会影响行高。但由于边框是可见的,它们仍然会被绘制出来——具体来说,边框会显示在前一行文字的上方和后一行文字的下方(如果有需要的话)。 需要注意的是,这些规则只适用于行内元素的块开始和块结束边。对于行内开始和行内结束边的情况就不一样了:如果你在行内开始和行内结束边上添加边框,它们不仅会显示出来,还会把周围的文字向两侧推开:
|<!DOCTYPE html> <html> <head> <style> strong { border-inline-start: 25px double hsl(216, 50%, 50%); padding: 5px; } </style>
对于边框,就像内边距一样,浏览器在计算行内非替换元素的行分割时,并不会直接受到这些盒子属性的影响。唯一的区别在于,边框所占据的空间有可能轻微推动部分内容的位置,这可能间接导致换行点不同。
边框是否在每一行的末尾绘制,可以通过 box-decoration-break 属性进行控制,相关内容已在前面的章节详细介绍过。
而对于图片等替换元素,情况则类似于内边距:边框不仅会把周围的文字推开,也会直接影响该行的高度。比如,假设使用下面的样式,就会得到类似的效果:
|<!DOCTYPE html> <html> <head> <style> img { border: 1em solid rgb(216, 108, 54); } </style> </head> <body> <
border-radius 属性可以用来让元素的边框角变得更加圆润,这同样会影响元素的整个背景区域。通过这个属性,我们可以设置一个或两个圆角半径,定义角的圆滑程度。所谓圆角,就是在每个角上画出一个圆或椭圆的四分之一弧线,用来代替原本的直角。
我们先从理解圆形的圆角开始,因为它最直观。比如,如果想让元素的四个角都变得非常圆润,可以这样设置:
|<!DOCTYPE html> <html> <head> <style> #example { border-radius: 2em; border: 3px solid #666; padding: 20px; background-color: #f0f0f0; } </
在这个例子中,边框在距离顶部边框2em的地方开始弯曲,在距离左边边框2em的地方也开始弯曲。曲线沿着2em半径圆的外部路径。如果我们要画一个只包含左上角弯曲部分的盒子,那个盒子会是2em宽和2em高。同样的效果会发生在右下角。 使用单个长度值时,我们得到圆形的圆角形状。如果使用单个百分比值,结果会更接近椭圆形。比如,考虑以下情况:
|<!DOCTYPE html> <html> <head> <style> #example { border-radius: 33%; border: 3px solid #666; padding: 20px; background-color: #e8f4f8; width: 300
再来看左上角这一点。在元素的左边缘,边框的曲线起始于距离元素顶部高度33%的位置。也就是说,如果元素整体高度是100像素,那么曲线会从离顶部33像素处开始。 同理,在元素的顶部边缘,曲线会从距离左边缘宽度33%的地方起始。举例来说,如果元素宽度为600像素,那么曲线就在距离左侧198像素(600 × 0.33)的地方开始。
连接这两个点的曲线,形状就如同一个椭圆的左上部分:该椭圆的水平半径为198像素,垂直半径为33像素。每个角都会做类似的处理,因此四个角并不是完全一样的圆弧,而是镜像的椭圆弧线。
当你给 border-radius 只设置一个长度或百分比数值时,四个角的圆角效果是一样的。当然,你也可以像 CSS 语法规范说明的那样,同时设置多达四个数值。由于 border-radius 这个属性是物理属性,这四个值的顺序是从左上角开始,按顺时针依次为:左上、右上、右下、左下,比如:
|#example { border-radius: 1em; /* 左上 */ 2em; /* 右上 */ 3em; /* 右下 */ 4em; /* 左下 */ }
这种“左上-右上-右下-左下”的顺序,可以用助记词“TiLTeR BuRBLe”方便记忆。你只需要记住,圆角是从左上角开始,然后依次沿顺时针方向指定各角的。
如果你没有写满四个数值,缺省的数值会按照类似 padding 的规则自动补全。如果写三个值,第四个(左下角)会取第二个(右上角)的值;如果写两个值,第三个(右下角)会复制第一个(左上角),第四个(左下角)复制第二个(右上角);如果只写一个数值,其余三个角都会用这个值。
需要注意,border-radius 虽然会改变元素边框和背景的圆角样式,但并不会影响元素本身的盒模型形状。元素依然是一个矩形盒子,只是它的边框和背景被圆润处理了。因此,页面布局和内容流动仍然以这个矩形为基础。
那么,如果半径值设置得过大,会出现什么情况?比如为一个宽高远小于 10000 像素的元素设置 border-radius: 100% 或 border-radius: 9999px?
这种情况下,圆角值会被“限制”为元素每个象限所允许的最大半径。比如,让按钮永远呈现为像药丸那样的圆端效果,你可以这样写:
|<!DOCTYPE html> <html> <head> <style> .button { border-radius: 9999em; border: 2px solid #0066cc; padding: 10px 20px; background-color: #0066cc; color
这样设置,只会让元素的最短边(通常为左右两侧,但这并不绝对)呈现出光滑的半圆效果。
现在你已经了解给所有角设置同一个半径值会产生什么样的效果了,接下来我们来说说:如果给角分别指定两个不同的半径值,会发生什么?这些值又代表什么意义?
举个例子,假如我们想要让元素四个角在水平方向上拥有 3 个字符单位的圆角、垂直方向上拥有 1 个字符单位的圆角。直接写 border-radius: 3ch 1ch 是无法实现这个效果的,因为这样写会造成左上和右下角为 3ch,右上和左下角为 1ch。要达到我们想要的水平方向与垂直方向分别控制,就需要加上一个斜杠,如下所示:
|<!DOCTYPE html> <html> <head> <style> #example { border-radius: 3ch / 1ch; border: 3px solid #666; padding: 20px; background-color: #fff5f5;
这在功能上等同于说:
|#example { border-radius: 3ch 3ch 3ch 3ch / 1ch 1ch 1ch 1ch; }
这种语法的工作方式是,每个角的圆角椭圆的水平半径在斜杠之前给出,然后斜杠之后给出每个角的垂直半径。在这两种情况下,值都按照"TiLTeR BuRBLe"的顺序排列。 这是一个更简单的例子:
|<!DOCTYPE html> <html> <head> <style> #example { border-radius: 1em / 2em; border: 3px solid #666; padding: 20px; background-color: #f5f5dc;
这里每个角在水平方向的圆角为 1em,垂直方向的圆角为 2em。下面是一个更复杂的例子:斜杠前后分别指定了两个长度值:
|<!DOCTYPE html> <html> <head> <style> #example { border-radius: 2.5em 2em / 1.5em 3em; border: 3px solid #666; padding: 20px; background-color
在这个例子中,左上角和右下角的圆角在水平方向为2.5em,垂直方向为1.5em;而右上角和左下角则是水平方向2em,垂直方向3em。 请记住,斜杠前写的是各个角的水平半径,斜杠后写的是对应的垂直半径。如果我们希望左上和右下角在水平和垂直方向的圆角都为1em(即形成正圆的圆角),可以这样写:
|#example { border-radius: 1em 2em / 1em 3em; }
这里同样可以使用百分比。如果我们希望元素的左右两侧是完全的圆角,并且圆角在水平方向上延伸到元素宽度的2个字符单位,可以这样写:
|<!DOCTYPE html> <html> <head> <style> #example { border-radius: 2ch / 50%; border: 3px solid #666; padding: 20px; background-color: #fff9c4;
了解了 border-radius 的用法后,你可能会好奇,能不能只让某一个角变成圆角。答案是当然可以!我们其实可以分别设置每个物理角的圆角半径,这些对应的属性是:border-top-left-radius(左上角)、border-top-right-radius(右上角)、border-bottom-right-radius(右下角)和 border-bottom-left-radius(左下角)。
每个属性只作用于指定的那个角,不会影响其他角。有趣的是,当你为某个角的圆角属性同时设置两个值时(一个水平半径,一个垂直半径),它们之间不需要用斜杠分隔。没错!这就意味着,下面的两种写法实际效果是一样的:
|/* 方式一 */ #example { border-radius: 1.5em 2vw 20% 0.67ch / 2rem 1.2vmin 1cm 10%; } /* 方式二 */ #example { border-top-left-radius: 1.5em 2rem; border-top-right-radius: 2vw 1.2vmin;
单独的角边框半径属性主要用于设置常见的角圆角,然后只覆盖一个。因此,一个类似漫画书的文字气球形状可以这样做:
|<!DOCTYPE html> <html> <head> <style> .tabs { border-radius: 2em; border-bottom-left-radius: 0; border: 2px solid #333; padding: 15px; background-color: #f0f8ff
除了物理角以外,CSS 还提供了逻辑角的属性,分别是 border-start-start-radius、border-start-end-radius、border-end-start-radius 和 border-end-end-radius。
你可能会想,“等等,这和其它逻辑属性的命名方式好像不太一样!”没错,这组属性的命名方式确实有点不同。原因在于,如果像 border-block-start-radius 那样的属性存在,它会同时作用于块起始边的两个角。而如果用 border-inline-start-radius,它又会作用于行内起始边的两个角,其中有一个实际上也在块起始边上。
所以 CSS 采用了 border-block-inline-radius 这种组合方式来命名逻辑边角半径属性。比如,border-start-end-radius 就是用来设置块起始边和行内结束边交汇处那个角的圆角半径。下面是一个例子:
|<!DOCTYPE html> <html> <head> <style> p { border-start-end-radius: 2em; border: 2px solid #666; padding: 15px; background-color: #f5f5f5; } </
需要注意的是,你可以像前文介绍的 border-top-left-radius 等属性一样,使用空格分隔的值来定义椭圆形的圆角半径。不过,这些值依然是按照“水平半径,接着是垂直半径”的顺序,而不是根据块方向和行内方向。
还有一点要记住:正如你看到的,圆角的形状会影响背景,可能还会影响元素的内边距和内容区域,但不会影响任何图像边框。你可能会问,什么是图像边框?别着急,下面就来介绍!
虽然常见的边框样式已经可以满足大部分需求,但视觉效果还是比较有限。如果你希望为某些元素添加更加复杂、丰富的边框,以前我们可能需要用多层嵌套的表格来实现。但现在有了图像边框功能,你几乎可以实现任何样式的边框,没有太多局限。
当你准备使用图片来制作边框时,首先需要把图像定义好,或者提供相应的图片路径。border-image-source 属性就是用来告诉浏览器你的边框图片在哪里。它可以设置为 none(无边框图像)或具体的图片路径,默认值是 none。
下面我们加载一个简单的圆形图像,作为边框图像来看看效果:
|<!DOCTYPE html> <html> <head> <style> .example { border: 25px solid #333; border-image-source: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><circle cx="50" cy="50" r="40" fill="%23ff6b6b"/></svg>'); padding: 20px; background-color: #f5f5f5;
这里有几个新手要注意的小地方。首先,你必须写出类似 border: 25px solid; 这样的声明,页面上才会显示边框。如果你把 border-style 设置成 none,其实等于没边框,哪怕你写了 border-width,宽度也会变成0,看不到任何东西。所以想让图片边框(border-image)出现,必须指定一个边框样式,比如 solid、dashed 等,不能用 none 或 hidden,样式不限于 solid。
另外,border-width 决定实际的边框图像有多宽。它不写时会用默认值medium(大多数浏览器就是3px)。如果你的图片加载失败,会只显示用 border-color 画出来的普通边框。
现在我们给元素设置了25像素宽的边框,再加上一张图片。结果你会发现,圆形图片只显示在四个角落,而不是整个边都围上一圈。这是因为 border-image-slice 的切片方式决定了图片在哪里显示。
border-image-slice 这个属性,简单说就是把你的图片按照一定比例切成9块(像9宫格那样)。设置这个属性时,可以指定1到4个值,分别对应上、右、下、左的切片距离(也就是TRBL模式,top、right、bottom、left)。如果只写一个值,四个方向都用这个值,非常简单。
你可以尝试用一张3行3列的彩色圆圈SVG图片,每个圈颜色都不同。当你设置 border-image-slice: 33.33%;,就是把图片每边切成三等份。这样切出来后,四角各用一个圆,四条边用四个圆,并且边上的图案会被自动拉伸填满每条边,这也是默认行为,很多情况下符合我们的预期。
如果我们希望边框四周全都有图片,必须用3×3的网格图片。
那么为什么刚开始例子的圆形图片只在四角有,边上一片空白呢?原因是如果切片线刚好交叉或重叠,就只剩下角落部分,边上的图片就没了。最直观的例子是 border-image-slice: 50%,这样图片被切成四个角,边上没有图案。如果你用更大的值,比如 border-image-slice: 100%,四个角其实都是整个图片,边上一样没有东西。
除了百分比(比如 33.33%),你还可以用数字来当做像素,比如 border-image-slice: 25。注意这里的数字就表示图片上的像素。所以比如一张PNG或者JPEG,用数字25,意思就是每边切25像素出来作为切片,非常直观。
|<!DOCTYPE html> <html> <head> <style> .example { border: 25px solid #333; border-image-source: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150"><rect width="150" height="150" fill="%23e0e0e0"/><circle cx="25" cy="25" r="15" fill="%23ff6b6b"/><circle cx="75" cy="25" r="15" fill="%234ecdc4"/><circle cx="125" cy="25" r="15" fill="%2395e1d3"/><circle cx="25" cy="75" r="15" fill="%23ffe66d"/><circle cx="75" cy="75" r="15" fill="%23c0c0c0"/><circle cx="125" cy="75" r="15" fill="%23ffa07a"/><circle cx="25" cy="125" r="15" fill="%23dda0dd"/><circle cx="75" cy="125" r="15" fill="%2398d8c8"/><circle cx="125" cy="125" r="15" fill="%23f4a460"/></svg>'); border-image-slice: 25
如果你将图像更改为不同大小的图像,数字偏移量不会适应新的大小,而百分比会。关于数字偏移量的有趣之处在于,它们在非光栅图像(如SVG)上也能正常工作。百分比也是如此。一般来说,只要可能,最好使用百分比作为切片偏移量,即使这意味着做一些数学计算以获得正确的百分比。
现在让我们解决图像中心的奇怪情况。在前面的例子中,3×3网格圆圈的中央有一个圆圈,但当图像应用于边框时它消失了。在之前的例子中,不仅中间的圆圈缺失,整个中心切片也缺失。丢弃中心切片是图像切片的默认行为,但你可以通过在 border-image-slice 值的末尾添加 fill 关键字来覆盖它。如果我们在前面的例子中添加 fill,我们会得到如下结果:
|.example { border: 25px solid; border-image-source: url('...'); border-image-slice: 25 fill; }
那里是中心切片,填充元素的背景区域。实际上,它被绘制在元素可能拥有的任何背景之上,包括任何背景图像或颜色,所以你可以将它用作背景的替代品或作为背景的补充。
你可能已经注意到我们所有的边框区域都有一致的宽度(通常是25px)。这不一定是这样的情况,无论边框图像实际如何切片。假设我们使用我们一直在使用的圆圈边框图像,像我们之前一样将其切片为三分之一,但使边框宽度不同:
|<!DOCTYPE html> <html> <head> <style> .example { border-style: solid; border-width: 20px 40px 60px 80px; border-image-source: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150"><rect width="150" height="150" fill="%23e0e0e0"/><circle cx="25" cy="25" r="15" fill="%23ff6b6b"/><circle cx="75" cy="25" r="15" fill="%234ecdc4"/><circle cx="125" cy="25" r="15" fill="%2395e1d3"/><circle cx="25" cy="75" r="15" fill="%23ffe66d"/><circle cx="75" cy="75" r="15" fill="%23c0c0c0"/><circle cx="125" cy="75" r="15" fill="%23ffa07a"/><circle cx="25" cy="125" r="15" fill="%23dda0dd"/><circle cx="75" cy="125" r="15" fill="%2398d8c8"/><circle cx="125" cy="125" r="15" fill="%23f4a460"/></svg>'
即使通过设置 border-image-slice: 50,本质上定义了50像素的切片,但实际渲染时切片依然会根据边框区域的尺寸自适应缩放以适配各自的边框。
当我们使用边框图片时,默认情况下图片会填满整个 border-width 定义的区域。但有时候我们希望图片只显示一部分宽度,这时候就需要用到 border-image-width 属性了。
这个属性专门用来控制边框图片本身的显示宽度,它与普通的 border-width 是完全独立的。也就是说,你可以有一个很宽的普通边框,但边框图片只显示很窄的一部分。
让我们来看看它的具体用法。
首先是长度单位,比如像素或em:
|border-image-width: 10px;
这个设置会让边框图片在元素的上、下、左、右四个方向都显示10像素宽。 你也可以使用百分比值:
|border-image-width: 25%;
这里需要特别注意:百分比是相对于整个元素盒子的尺寸(包括边框)来计算的,而不是相对于 border-width。举个例子,如果你的元素总宽度是200像素,border-image-width: 25% 就会让边框图片显示50像素宽。
还有数字值,这种用法很有趣:
|border-width: 5px; border-image-width: 2;
数字值表示的是 border-width 的倍数。上面的代码中,普通边框是5像素宽,border-image-width: 2 就会让边框图片显示10像素宽(5×2=10)。
你还可以为四条边设置不同的宽度:
|border-image-width: 10px 20px 15px 5px; /* 上 右 下 左 */
甚至可以混合使用不同的单位类型:
|border-image-width: 10px 25% auto 2em;
其中 auto 值会根据情况自动选择:如果设置了 border-image-slice,就使用切片的值;如果没有设置,就使用 border-width 的值。
|<!DOCTYPE html> <html> <head> <style> .example-width { border: 5px solid #ccc; border-image-slice: 20; border-image-width: 15px; border-image-repeat: stretch; padding: 20
有时候我们不希望边框图片占用元素内部的空间,而是让它向外扩展显示。这个需求可以通过 border-image-outset 属性来实现。
outset 的意思是“向外偏移”。它可以让边框图片超出元素的正常边界,向外部扩展显示。这样做的好处是不影响内容的布局,也不会覆盖到元素内部的内容。
基本用法是设置一个长度值:
|border-image-outset: 10px;
这会让边框图片向外扩展10像素。
跟 border-image-width 一样,数字值也是普通边框宽度的倍数:
|border-width: 5px; border-image-outset: 2; /* 实际向外扩展10px */
你也可以为四条边设置不同的外扩距离:
|border-image-outset: 5px 10px 8px 3px; /* 上 右 下 左 */
这个属性的主要作用是避免边框图片与内容发生重叠。想象一下,如果你的边框图片很宽,而内容区域又很小,那么图片就可能覆盖到文字或其他内容。通过设置 outset,我们可以让图片向外扩展,腾出内部空间给内容。
|<!DOCTYPE html> <html> <head> <style> .example-outset { border: 3px solid #ccc; border-image-slice: 25; border-image-width: 20px; border-image-outset: 15px; border-image-repeat:
border-image-outset 目前也没有对应的逻辑属性版本。
默认情况下,边框图片会被拉伸来填满边框区域。但有时候我们希望图片以其他方式显示,比如重复或者均匀分布。这个时候就需要用到 border-image-repeat 属性。
这个属性控制着边框图片在边框区域内的显示方式,一共有四种模式可选。
第一种是 stretch(拉伸),这是默认值:
|border-image-repeat: stretch;
图片会被均匀拉伸来填满整个边框区域。无论边框区域有多宽多长,图片都会被拉伸到正好匹配。
第二种是 repeat(重复):
|border-image-repeat: repeat;
图片会保持原始尺寸,沿着边框重复显示。如果空间不够容纳完整的一张图片,最后一张会被裁剪掉一部分。
第三种是 round(适应):
|border-image-repeat: round;
浏览器会先计算出应该重复多少张图片,然后稍微调整每张图片的大小,让它们刚好填满整个边框区域,不会留下空白也不会被裁剪。
第四种是 space(均匀分布):
|border-image-repeat: space;
图片保持原始尺寸,之间均匀分布空隙。如果空间不够,会向下舍入重复次数,然后把剩余空间平均分配到图片之间。
|<!DOCTYPE html> <html> <head> <style> .stretch-example { border: 2px solid #ccc; border-image-slice: 15; border-image-width: 20px; border-image-repeat: stretch; padding: 20
border-image-repeat 目前也没有对应的逻辑属性版本。
CSS 提供了一个简写属性 border-image,可以让你一次性设置所有边框图片相关的属性,大大简化代码。
它的基本语法是:
|border-image: [source] [slice] / [width] / [outset] [repeat];
各个部分用正斜杠 / 分隔开。让我详细解释一下每个部分:
source: 图片的来源,可以是URL或者其他图片值slice: 切片值,决定如何分割图片width: 边框图片的显示宽度outset: 向外扩展的距离repeat: 重复模式值得一提的是,source 和 repeat 的位置比较灵活,可以放在整个声明的任何位置。比如这几种写法都是等价的:
|border-image: url(image.png) 40% 30% 20% fill / 10px 7px / 5px space; border-image: space url(image.png) 40% 30% 20% fill / 10px 7px / 5px; border-image: space 40% 30% 20% fill / 10px 7px / 5px url(image.png);
如果省略某些部分,会使用默认值。比如只设置图片来源:
|border-image: url(my-image.png);
相当于:
|border-image: url(my-image.png) stretch 100% / 1 / 0;
|<!DOCTYPE html> <html> <head> <style> .shorthand-example { border-image-slice: 20 fill; border-image-width: 15px; border-image-outset: 8px; border-image-repeat: round; border: 2px
在CSS中,“轮廓(outline)”是一种特殊的元素装饰方式。通常,轮廓会绘制在元素边框的外侧,但实际情况有时会更灵活。根据规范,轮廓与边框在以下几个关键方面存在本质区别:
:focus)元素时,浏览器常用轮廓来高亮当前元素。由于轮廓不占空间,这样的高亮不会影响原有布局和内容。还有一点需要注意:轮廓是“全有或全无”的。也就是说,你无法像设置边框四边那样,只对某一侧应用轮廓或者单独调整某一边的样式。 接下来,我们将详细了解轮廓的相关CSS属性,并与边框进行对比说明。
和 border-style 类似,outline-style 用于定义轮廓的样式。其可选值基本和边框一致,比如 solid、dashed 等,开发者会觉得很熟悉。但有两个明显例外:
hidden 样式(但边框有)auto 样式auto 是留给浏览器自定义的样式,通常由操作系统或用户代理决定。例如,浏览器可以为不同类型的元素(如超链接、表单控件)渲染不同风格的轮廓,让它们与平台原生UI一致,有时还能展现更复杂的特效(如发光、半透明等)。使用 auto 时,outline-width 的设置可能会被忽略,以配合系统的默认效果。
与 border-style 不同,outline-style 不是简写属性,所以不能针对每一边分别设置。例如,没有 outline-top-style 这样的语法。所有关于轮廓的CSS属性都体现了这个特点:只能整体设置,无法单独控制某一侧。
当你为轮廓定义样式(只要不是 none),就可以用 outline-width 指定轮廓的粗细。它的使用跟 border-width 很像,支持 thin、medium、thick 和具体像素值。不过规范并未硬性规定这些关键字对应的具体像素数,只保证了thick > medium > thin。
outline-width 不是简写属性,只能为整条轮廓整体设定宽度,无法像边框那样单独指定左、右、上、下的宽度。
当轮廓的样式和宽度都已设定后,可以用 outline-color 控制颜色。和边框类似,该属性只能整体设置,不能分别指定每一边的颜色(例如并不存在 outline-left-color)。
值得一提的是,outline-color 的默认值是 invert。理论上,invert 会自动反转轮廓覆盖区域的像素颜色,从而确保无论底色如何,轮廓都足够醒目。但实际上,绝大多数现代浏览器都不再支持 invert,如果你指定 invert,浏览器通常会自动转用 currentcolor,即元素当前的文本颜色。
很多轮廓相关属性看起来像简写,实际上并不是。目前只有 outline 是标准的简写属性。你可以用一条语句同时设置颜色、样式和宽度,顺序不限,类似于 border 的用法,使代码更加简洁。
最关键的区别是,轮廓(outline)完全不影响页面布局。它只是外部的可视装饰,相当于在元素四周额外“画一道线”,既不改变元素尺寸,也不会影响周围其他内容的排布。比如下面这个例子:
|<!DOCTYPE html> <html> <head> <style> h1 { padding: 10px; border: 10px solid green; outline: 10px dashed #99aabb; margin: 10px;
看起来正常,对吧?你看不到的是轮廓完全覆盖了外边距。如果我们放入一条虚线来显示外边距边缘,它们会沿着轮廓的外边缘运行。
这就是轮廓不影响布局的意思。让我们考虑另一个例子,这次有两个 <span> 元素,它们被赋予了轮廓:
|<!DOCTYPE html> <html> <head> <style> span { outline: 1em solid rgba(0, 128, 0, 0.5); } span + span { outline: 0.5
轮廓(outline)不会改变文本行的高度,也不会把 <span> 标签往旁边推开。它更像是一种视觉上的标记,对文本的排版和布局没有影响,文本还是像平时那样排列,好像轮廓不存在一样。
还有一点有趣的是,轮廓并不一定总是矩形,也不总是连续的。比如,当我们给一个跨越两行显示的 <strong> 元素添加轮廓时,你会看到这样的效果:
|<!DOCTYPE html> <html> <head> <style> strong { outline: 2px dotted gray; } </style> </head> <body> <p>这是一段包含<strong>一段很长很长的强调文字,它会跨越两行显示</strong
在第一种情况下,每个 <strong> 元素的行内片段会单独生成一个完整的轮廓框。而在第二种情况下,当 <strong> 的内容很长,跨越多行时,行内片段会堆叠,轮廓则会合并,形成一个将所有片段包裹在内的多边形。这种行为和边框不一样——边框总是沿着盒模型的矩形边界绘制,不会“合并”成多边形。
正因如此,CSS 并没有诸如 outline-right-style 这类针对某一边的轮廓属性。因为一旦轮廓不是简单的矩形,我们就很难再区分哪一边是“右边”。
在普通文档流中,不同元素之间的间隔主要是由外边距(margin)实现的。给元素设置外边距,就是在它四周增加额外的空白空间。这里的空白区通常指,其他元素不会进入且能够显示父元素背景的区域。
最直接定义外边距的方法,就是使用 margin 这个物理属性。它可以接受具体的长度值、百分比,或 auto。需要注意的是,margin 默认值其实是 0,所以如果你没有显式声明外边距,元素之间就不会自动产生间隔。
实际上,浏览器会为很多元素预置默认样式,外边距也包括在内。例如,在支持 CSS 的浏览器中,段落元素 <p> 的上下通常会有默认的外边距,从而在段落之间出现额外的“空行”。无论如何,只要你自己定义了外边距,就会覆盖这些默认样式。
任何合法的长度值都可以被用作外边距的取值。例如,为段落元素四周增加 10 像素的空白空间非常简单。下面的规则会让段落拥有银色背景、10 像素内边距和 10 像素外边距:
|<!DOCTYPE html> <html> <head> <style> p { background-color: #e0e0e0; padding: 10px; margin: 10px; } </style> </head> <body
这为每个段落的每一边添加了10像素的空间,就在外边框边缘之外。你也可以很容易地使用 margin 来设置图片周围的额外空间。假设你想在所有图片周围设置1em的空间,可以这样写:
|<!DOCTYPE html> <html> <head> <style> img { margin: 1em; } </style> </head> <body> <p>这是一段包含<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='50' height='50'%3E%3Crect width='50' height='50' fill='%234ecdc4'/%3E%3C/svg%3E"
在实际开发中,您可能希望为元素的不同边设置不同的外边距。这同样非常简单,得益于 CSS 中的简写属性用法。如果您希望所有 <h1> 元素分别设置上外边距为 10 像素、右外边距为 20 像素、下外边距为 15 像素、左外边距为 5 像素,只需这样写:
|h1 { margin: 10px 20px 15px 5px; background-color: #f5f5f5; }
|<h1>网站主标题</h1> <p>这个标题的四个方向外边距是不同的。</p>
你也可以混合使用不同类型的长度值。你不限于在给定规则中只使用一种长度类型:
|<!DOCTYPE html> <html> <head> <style> h2 { margin: 14px 5em 0.1in 3ex; background-color: #fff5f5; } </style> </head> <body
我们可以为元素的外边距设置百分比值。与内边距一样,百分比外边距值是相对于父元素内容区域的宽度计算的,所以如果父元素的宽度以某种方式改变,它们也会改变。
|<!DOCTYPE html> <html> <head> <style> .container { width: 200px; border: 1px dotted #999; } .narrow-container { width: 100px; border:
需要注意的是,外边距的百分比值无论设置在上下还是左右方向,计算时都是基于元素自身的宽度,而不是高度。也就是说, vertical 和 horizontal 方向上的百分比外边距在实际表现上一致。其实你在“内边距”的学习时已经见过类似行为,这里再回顾一次,有助于加深理解。
正如前面提到的,CSS 提供了专门设置某一侧外边距的属性,不会影响其他边。具体来说,包含了4个物理方向的属性、4个逻辑方向的属性,以及2个逻辑方向的简写属性。 这些属性的用法很简单,下例展示了两种等效的设置方式:
|h2 { margin: 0 0 0 0.25in; } h2 { margin: 0; margin-left: 0.25in; }
类似地,以下两个规则将产生相同的结果:
|/* 方式一 */ h2 { margin-block-start: 0.25in; margin-block-end: 0.5em; margin-inline-start: 0; margin-inline-end: 0; } /* 方式二 */ h2 { margin-block: 0.25in 0.5em; margin-inline: 0; }
块级框的块开始和块结束外边距有一个有趣但常常被忽视的特性:在正常流布局中,它们会发生“外边距折叠”。简单来说,就是两个(或更多)沿块轴方向相邻的外边距,会合并成其中较大的那个。
最典型的例子就是段落之间的间距。通常,这个间距会通过如下样式设置:
|<!DOCTYPE html> <html> <head> <style> p { margin: 1em 0; } </style> </head> <body> <p>这是第一段文字。</p> <p>这是第二段文字。如果外边距不折叠,两段之间会有2em的空间。</
这样,每个段落的块级起始和结束(上下)外边距都被设置为 1em。如果没有外边距折叠,那么两个相邻段落之间的垂直距离应该是 2em——每个段落各自贡献 1em。但实际上,由于外边距折叠,最后只有 1em 的间距,也就是重叠部分只保留了较大的那一个外边距。
为了让原理更直观,我们再看一个百分比外边距的例子,并用虚线标记出外边距的具体位置。比如两段文字之间的间距显示为 60 像素,这是因为两个外边距中较大的那个(如第一个段落的块结束外边距为 60px,第二段落的块起始为 30px),只有 60px 会被保留,30px 的部分被折叠掉了。
所以其实严格按照 CSS 规范,第二段文字的顶部(块起始)外边距会被折叠成 0——它不会再“压”在第一个段落之上,两者只是合并为了一个更大的外边距。虽然页面视觉效果和原来一样,但本质上是第一个段落的块结束外边距起了作用。
外边距折叠也能解释,为什么有时候元素嵌套时会出现一些意料之外的外边距“消失”现象。来看下面的样式和代码示例:
|<!DOCTYPE html> <html> <head> <style> header { background: goldenrod; } h1 { margin: 1em; } </style> </head> <body
我们常以为 <h1> 的外边距会把 <header> 的边界往外推开,从而让标题与 <header> 边缘之间有空隙。然而,实际情况略有不同。虽然水平(内联方向)的外边距会按预期生效,比如左右间距会推开文字,但块级方向(上下)的外边距似乎“消失”了。
其实,这些外边距并没有消失,而是被“折叠”到 <header> 元素之外,与 <header> 的块级起始外边距合并(因为 <header> 没有设置相应的内边距或边框)。
这种块轴外边距的折叠效果,使外边距作用于 <header> 外部,影响 <header> 元素之前或之后的内容,但不会改变 <header> 自身的边界。这是符合 CSS 规范设计初衷的,尽管有时与直觉不符。为何这样设计?想象一下,如果一个段落被放在列表项 <li> 内部,如果块级外边距不会折叠,段落会被大幅向下推,从而导致文本与项目符号严重错位,不利于页面整齐。
需要注意的是,父元素如果设置了内边距或边框,将会阻止外边距折叠。
我们还可以为元素设置负外边距,这可能会让元素的边框“溢出”其父元素,甚至导致元素与周围的其他内容重叠。来看几个相关规则:
|<!DOCTYPE html> <html> <head> <style> div { border: 1px solid gray; margin: 1em; } p { margin: 1em; border:
在第一种情况下,段落的计算宽度加上它的左右(行内起止)负外边距,恰好与父 <div> 的宽度相等。这样,段落实际上比父元素宽出 2em。
在第二种情况下,段落的上下(块起止)负外边距会把它自身的外边界往外推进,因此这个段落会和前面的元素或下一个段落发生重叠。
灵活组合正负外边距其实很有用。比如,你可以巧妙利用正负外边距让段落“突破”父级容器的边界,或者通过几个带负外边距的盒子来创造出蒙德里安式的重叠和随机布局效果:
|<!DOCTYPE html> <html> <head> <style> div { background: hsl(42, 80%, 80%); border: 1px solid; padding: 1em; }
因为 mond 段落使用了负的下外边距,它的父元素底部会被向上拉,这样该段落就能够从父元素底部“突出”出来。
外边距同样可以应用在行内元素上。比如说,如果你想给强调文本设置块级起始和结束的外边距:
|<!DOCTYPE html> <html> <head> <style> strong { margin-block-start: 25px; margin-block-end: 50px; } </style> </head> <body> <p>这是一段包含<
虽然规范允许你这样设置,但给行内非替换元素(比如 <strong>)加上下外边距,对行高没有任何影响(这和内边距、边框是一样的)。而且,因为外边距是透明的,你其实看不到它的存在,说白了就是它并没有什么实际效果。
不过,如果你把外边距加在行内非替换元素的左边或右边(也就是“行内开始”和“行内结束”),那么页面布局就会有所不同了:
|<!DOCTYPE html> <html> <head> <style> strong { margin-inline-start: 25px; background: #c0c0c0; } </style> </head> <body> <p>注意行内元素<strong
注意在行内非替换元素之前,单词的末尾和行内元素背景边缘之间有多余的空间。如果你想,你可以为行内元素的两端都添加这种额外空间:
|<!DOCTYPE html> <html> <head> <style> strong { margin: 25px; background: #c0c0c0; } </style> </head> <body> <p>这段文字包含<strong
就像我们前面提到的一样,为行内元素设置的左右(行内开始和结束)外边距会在元素两侧留出额外空间,而上下(块方向)并没有变化。
然而,当行内非替换元素内容跨越多行时,外边距的表现会有所不同。行内开始边距只作用于元素整体的起始位置,行内结束边距仅作用于元素整体的末尾。在每一行的开头或结尾,外边距并不会重新应用。因此,在没有额外外边距的情况下,文本可能会在不同的位置换行。添加外边距则会影响元素在行内的起始排列,从而间接影响换行点。
如果我们为行内非替换元素设置负值外边距,效果会更加明显。此时,元素的上下(块方向)不会受到影响,行高也保持不变,但元素的左右(行内方向)外边距会导致内容与相邻文本发生重叠:
|<!DOCTYPE html> <html> <head> <style> strong { margin: -25px; background: #c0c0c0; } </style> </head> <body> <p>这是一段包含<strong
对于行内替换元素来说,外边距的处理方式与非替换元素有所不同。设置块方向(上下方向)的外边距时,它不仅会影响元素本身,还会直接改变所在文本行的高度:块起始(margin-top)和块结束(margin-bottom)外边距的数值会分别增加或减少整行的高度。而在行内方向(左右)上的外边距,则与非替换元素表现一致,仅仅在横向上为元素添加间距,不会影响行高。
外边距、边框和内边距可以应用到任意元素,为页面布局和元素样式提供了灵活、精细的控制。理解这些盒模型属性如何相互作用,是进行高效、规范网页设计的基础。熟练掌握它们的工作原理,不仅可以帮助你实现美观且功能完善的布局,同时也有助于代码结构的维护和响应式设计的实现。 每个属性都有其独特的作用和使用场景,只有深入理解并灵活运用,才能成长为专业的 CSS 开发者。
创建一个段落元素,要求四周内边距都是20px,背景色为浅蓝色。
|p { padding: 20px; background-color: lightblue; }
|<p>这是一个有内边距的段落。</p>
创建一个标题,要求上内边距10px,右内边距20px,下内边距15px,左内边距5px,背景色为浅绿色。
|h2 { padding: 10px 20px 15px 5px; background-color: lightgreen; }
|<h2>这是一个标题</h2>
创建一个div元素,设置2px宽的实线黑色边框,内边距10px。
|div { border: 2px solid black; padding: 10px; }
|<div>这是一个有边框的容器</div>
创建一个按钮样式的链接,设置2px宽的虚线红色边框,5px的圆角,内边距8px。
|a { border: 2px dashed red; border-radius: 5px; padding: 8px; text-decoration: none; color: black; }
|<a href="#">按钮链接</a>
创建两个段落元素,每个段落上下外边距都是1em。
|p { margin: 1em 0; }
|<p>第一个段落</p> <p>第二个段落</p>
使用逻辑属性创建一个段落,块方向内边距0.5em,行内方向内边距1em。
|p { padding-block: 0.5em; padding-inline: 1em; background-color: #f0f8ff; }
|<p>使用逻辑属性的段落</p>
创建一个容器宽度为400px的div,里面的段落左右外边距为10%。
|.container { width: 400px; border: 1px solid gray; } p { margin: 0 10%; background-color: #ffe4b5; }
|<div class="container"> <
创建一个元素,具有不同样式的四边边框:上边实线,右边虚线,下边点线,左边双线,边框宽度都是3px。
|.element { border-style: solid dashed dotted double; border-width: 3px; border-color: black; padding: 15px; }
|<div class="element">复杂边框样式示例</div>