CSS 容器查询的乌鸦技巧:更接近容器查询
我们再次强调:CSS 中需要容器查询!看起来我们正朝着这个方向前进。
在构建网站组件时,你并不总是知道组件将如何使用。它可能与浏览器窗口一样宽。 也许两个组件并排放置。 也许它在一个狭窄的列中。组件的宽度并不总是与浏览器窗口的宽度相关。
通常情况下,你会发现使用基于容器的 CSS 组件查询非常方便。如果你在网上搜索解决方案,你可能会发现几种基于 JavaScript 的解决方案。但这些解决方案是有代价的:额外的依赖项、需要 JavaScript 的样式以及被污染的应用程序逻辑和设计逻辑。
我坚信关注点分离,而布局是 CSS 的关注点。例如,尽管 IntersectionObserver API 很好,但我希望 CSS 中有像 :in-viewport
这样的东西!因此,我继续寻找仅限 CSS 的解决方案,并发现了 Heydon Pickering 的“Flexbox 神圣信天翁”。这对于列来说是一个不错的解决方案,但我想要更多。原始信天翁有一些改进(如“不神圣的信天翁”),但它们仍然有点笨拙,所有发生的事情只是行到列的切换。
我仍然想要更多!我想更接近实际的容器查询!那么,CSS 提供了什么我可以利用的呢?我具有数学背景,因此像 calc()
、min()
、max()
和 clamp()
这样的函数是我喜欢和理解的东西。
下一步:用它们构建一个类似容器查询的解决方案。
目录:
- 为什么是“乌鸦”?
- CSS 中的数学函数
- 步骤 1:创建配置变量
- 步骤 2:创建指示器变量
- 步骤 3:使用指示器变量选择区间值
- 步骤 4:使用
min()
和一个极大的整数来选择任意长度的值 - 步骤 5:将所有内容整合在一起
- 其他?
- 高度呢?
- 显示和隐藏内容呢?
- 关键点
- 附加内容
- 最后的想法
想在继续阅读之前看看可能实现什么吗?这是一个展示本文中讨论的想法所能实现的功能的 CodePen 合集。
为什么是“乌鸦”?
这项工作受到 Heydon 的信天翁的启发,但该技术可以做更多技巧,所以我选择了一只乌鸦,因为乌鸦是非常聪明的鸟。
回顾:CSS 中的数学函数
calc()
函数允许在 CSS 中进行数学运算。另外,可以组合单位,因此像 calc(100vw - 300px)
这样的操作是可能的。
min()
和 max()
函数接受两个或多个参数并返回最小或最大参数(分别)。
clamp()
函数非常有用,它类似于 min()
和 max()
的组合。函数 clamp(a, x, b)
将返回:
- 如果 x 小于 a,则返回 a
- 如果 x 大于 b,则返回 b
- 如果 x 在 a 和 b 之间,则返回 x
所以它有点像 clamp(最小值, 相对值, 最大值)
。可以将其视为 min(max(a,x),b)
的简写。如果你想了解更多信息,这里有更多关于这些的信息。
我们还将在本文中大量使用另一个 CSS 工具:CSS 自定义属性。这些就像 --color: red;
或 --distance: 20px;
一样。本质上是变量。我们将使用它们来使 CSS 更简洁,例如避免过多地重复自己。
让我们开始使用这个乌鸦技巧吧。
步骤 1:创建配置变量
让我们创建一些 CSS 自定义属性来进行设置。
我们希望查询基于什么基本大小?由于我们正在寻求容器查询行为,这将是 100%——使用 100vw 将使其行为类似于媒体查询,因为这是浏览器窗口的宽度,而不是容器!
<code>--base_size: 100%;</code>
现在我们考虑断点。字面意思是容器宽度,我们希望在此处中断以应用新的样式。
<code>--breakpoint_wide: 1500px; /* 大于 1500px 将被视为宽 */ --breakpoint_medium: 800px; /* 从 801px 到 1500px 将被视为中等 */ /* 小于或等于 800px 将被视为小 */</code>
在运行示例中,我们将使用三个区间,但此技术没有限制。
现在让我们定义一些我们希望为断点定义的区间返回的(CSS 长度)值。这些是字面值:
<code>--length_4_small: calc((100% / 1) - 10px); /* 根据你的需求更改 */ --length_4_medium: calc((100% / 2) - 10px); /* 根据你的需求更改 */ --length_4_wide: calc((100% / 3) - 10px); /* 根据你的需求更改 */</code>
这是配置。让我们使用它!
步骤 2:创建指示器变量
我们将为区间创建一些指示器变量。它们有点像布尔值,但带有长度单位(0px 和 1px)。如果我们将这些长度作为最小值和最大值来限制,那么它们就充当一种“真”和“假”指示器。
因此,当且仅当 --base_size 大于 --breakpoint_wide 时,我们想要一个值为 1px 的变量。否则,我们想要 0px。这可以使用 clamp()
来完成:
<code>--is_wide: clamp(0px, var(--base_size) - var(--breakpoint_wide), 1px );</code>
如果 var(--base_size) - var(--breakpoint_wide)
为负数,则 --base_size 小于 --breakpoint_wide,因此 clamp()
在这种情况下将返回 0px。
相反,如果 --base_size 大于 --breakpoint_wide,则计算将给出正长度,该长度大于或等于 1px。这意味着 clamp()
将返回 1px。
bingo!我们得到了一个“宽”的指示器变量。
让我们为“中等”区间执行此操作:
<code>--is_medium: clamp(0px, var(--base_size) - var(--breakpoint_medium), 1px ); /* 不要使用,见下文! */</code>
这将为小区间提供 0px,但为中等和宽区间提供 1px。然而,我们想要的是宽区间为 0px,而中等区间仅为 1px。
我们可以通过减去 --is_wide 值来解决这个问题。在宽区间中,1px - 1px 为 0px;在中等区间中,1px - 0px 为 1px;对于小区间,0px - 0px 给出 0px。完美。
所以我们得到:
<code>--is_medium: calc( clamp(0px, var(--base_size) - var(--breakpoint_medium), 1px) - var(--is_wide) ); </code>
明白了吗?要计算指示器变量,请使用 clamp()
,其中 0px 和 1px 作为边界,而 --base_width 和 --breakpoint_whatever 的差作为受限值。然后减去所有较大区间指示器的总和。对于最小区间指示器,此逻辑产生以下结果:
<code>--is_small: calc( clamp(0px, (var(--base_size) - 0px, 1px) - (var(--is_medium) var(--is_wide)) ); </code>
我们可以跳过这里的 clamp()
,因为小区间的断点为 0px,而 --base_size 为正数,因此 --base_size - 0px 始终大于 1px,clamp()
将始终返回 1px。因此,--is_small 的计算可以简化为:
<code>--is_small: calc(1px - (var(--is_medium) var(--is_wide))); </code>
步骤 3:使用指示器变量选择区间值
现在我们需要从这些“指示器变量”转向一些有用的东西。让我们假设我们正在使用基于像素的布局。别担心,我们稍后会处理其他单位。
这是一个问题。这返回什么?
<code>calc(var(--is_small) * 100);</code>
如果 --is_small 为 1px,它将返回 100px;如果 --is_small 为 0px,它将返回 0px。
这有什么用?看看这个:
<code>calc( (var(--is_small) * 100) (var(--is_medium) * 200) );</code>
这将在小区间(其中 --is_small 为 1px,--is_medium 为 0px)返回 100px 0px = 100px。在中等区间(其中 --is_medium 为 1px,--is_small 为 0px)中,它将返回 0px 200px = 200px。
明白了吗?请参阅 Roman Komarov 的文章,以更深入地了解这里发生了什么,因为它可能难以理解。
你将一个像素值(无单位)乘以相应的指示器变量,并将所有这些项加起来。因此,对于基于像素的布局,类似这样的东西就足够了:
<code>width: calc( (var(--is_small) * 100) (var(--is_medium) * 200) (var(--is_wide) * 500) );</code>
但是大多数时候,我们不想要基于像素的值。我们想要概念,例如“全宽”或“三分之一宽”,甚至可能是其他单位,例如 2rem、65ch 等。对于这些,我们必须继续进行。
步骤 4:使用 min()
和一个极大的整数来选择任意长度的值
在第一步中,我们定义了类似这样的内容,而不是静态像素值:
<code>--length_4_medium: calc((100% / 2) - 10px);</code>
那么我们该如何使用它们呢?min()
函数来救援!
让我们定义一个辅助变量:
<code>--very_big_int: 9999; /* 纯粹的无单位数字。必须大于其他地方出现的任何长度。 */</code>
将此值乘以指示器变量将给出 0px 或 9999px。此值应有多大取决于你的浏览器。Chrome 将接受 999999,但 Firefox 不会接受这么大的数字,因此 9999 是一个在两者中都能工作的数值。周围几乎没有大于 9999px 的视口,所以我们应该没问题。
那么,当我们将它与小于 9999px 但大于 0px 的任何值一起使用 min()
时会发生什么?
<code>min( var(--length_4_small), var(--is_small) * var(--very_big_int) );</code>
当且仅当 --is_small 为 0px 时,它将返回 0px。如果 --is_small 为 1px,则乘法将返回 9999px(大于 --length_4_small),而 min
将返回:--length_4_small。
这就是我们可以根据指示器变量选择任何长度(即小于 9999px 但大于 0px)的方法。
如果你处理的视口大于 9999px,那么你需要调整 --very_big_int 变量。这有点难看,但是一旦纯 CSS 可以删除值中的单位以摆脱指示器变量中的单位(并直接将其乘以任何长度),我们就可以修复这个问题。目前,这有效。
我们现在将组合所有部分并让乌鸦飞翔!
步骤 5:将所有内容整合在一起
我们现在可以像这样计算我们基于动态容器宽度、基于断点的值:
<code>--dyn_length: calc( min(var(--is_wide) * var(--very_big_int), var(--length_4_wide)) min(var(--is_medium) * var(--very_big_int), var(--length_4_medium)) min(var(--is_small) * var(--very_big_int), var(--length_4_small)) );</code>
每一行都是步骤 4 中的 min()
。所有行都像步骤 3 中那样加起来,指示器变量来自步骤 2,所有这些都基于我们在步骤 1 中进行的配置——它们在一个大公式中一起工作!
想试试吗?这是一个可以使用的 Pen(请参阅 CSS 中的注释)。
这个 Pen 不使用 flexbox、grid 或浮动。只是一些 div。这是为了表明在这种布局中辅助程序是不必要的。但是,随意将乌鸦与这些布局一起使用,因为它将帮助你创建更复杂的布局。
其他?
到目前为止,我们已经使用固定像素值作为断点,但是如果容器大于或小于视口的一半减去 10px,我们是否想要更改布局?没问题:
<code>--breakpoint_wide: calc(50vw - 10px);</code>
这有效!其他公式也适用。为了避免奇怪的行为,我们想要使用类似:
<code>--breakpoint_medium: min(var(--breakpoint_wide), 500px);</code>
……在 500px 宽度处设置第二个断点。步骤 2 中的计算取决于 --breakpoint_wide 不小于 --breakpoint_medium 的事实。只需按正确的顺序保持断点:min()
和/或 max()
在这里非常有用!
高度呢?
所有计算的评估都是延迟执行的。也就是说,当将 --dyn_length 分配给任何属性时,计算将基于此位置中 --base_size 的计算结果。因此,如果 --base_size 为 100%,则设置高度将基于 100% 的高度。
我还没有找到一种基于容器宽度设置高度的方法。因此,你可以使用 padding-top
,因为 100% 对填充而言相当于宽度。
显示和隐藏内容呢?
使用乌鸦技巧显示和隐藏内容最简单的方法是在适当的指示器变量处将宽度设置为 100px(或任何其他合适的宽度):
<code>.show_if_small { width: calc(var(--is_small) * 100); } .show_if_medium { width: calc(var(--is_medium) * 100); } .show_if_wide { width: calc(var(--is_wide) * 100); }</code>
你需要设置:
<code>overflow: hidden; display: inline-block; /* 避免难看的空行 */</code>
……或其他一些方法来隐藏宽度为 0px 的框中的内容。完全隐藏框需要将其他框模型属性(包括边距、填充和边框宽度)设置为 0px。乌鸦可以对某些属性执行此操作,但将其修复为 0px 同样有效。
另一种方法是使用 position: absolute;
并通过 left: calc(var(--is_???) * 9999);
将元素绘制到屏幕外。
关键点
我们可能根本不需要 JavaScript,即使对于容器查询行为也是如此!当然,我们希望如果我们在 CSS 语法中实际获得容器查询,它将更容易使用和理解——但今天在 CSS 中能够实现这些事情也非常酷。
在处理此问题时,我对 CSS 可以使用的其他内容产生了一些看法:
- 基于容器的单位,如 conW 和 conH,用于基于宽度设置高度。这些单位可以基于当前堆叠上下文的根元素。
- 某种“评估为值”函数,以克服延迟评估的问题。这将与在渲染时工作的“去除单位”函数一起非常有效。
注意:在早期版本中,我使用了 cw 和 ch 作为单位,但有人指出这些很容易与同名 CSS 单位混淆。感谢评论中的 Mikko Tapionlinna 和 Gilson Nunes Filho 提供的提示!)
如果我们有第二个,它将允许我们使用乌鸦设置颜色(以干净的方式)、边框、盒阴影、flex-grow、背景位置、z-index、scale() 和其他内容。
结合基于组件的单位,甚至可以将子尺寸设置为与父级相同的纵横比。无法除以带单位的值;否则 --indicator / 1px 将作为乌鸦的“去除单位”工作。
附加内容:布尔逻辑
指示器变量看起来像布尔值,对吧?唯一的区别是它们有一个“px”单位。那么这些的逻辑组合呢?想象一下诸如“容器宽度大于屏幕的一半”和“布局处于两列模式”之类的内容。CSS 函数再次来救援!
对于 OR 运算符,我们可以对所有指示器使用 max()
:
<code>--a_OR_b: max( var(--indicator_a) , var(--indicator_b) );</code>
对于 NOT 运算符,我们可以从 1px 中减去指示器:
<code>--NOT_a: calc(1px - var(--indicator_a));</code>
逻辑纯粹主义者可能会停在这里,因为 NOR(a,b) = NOT(OR(a,b)) 是完整的布尔代数。但是,嘿,只是为了好玩,这里还有一些:
AND:
<code>--a_AND_b: min(var(--indicator_a), var(--indicator_b)); </code>
当且仅当两个指示器都为 1px 时,这将评估为 1px。
请注意,min()
和 max()
接受两个以上的参数。它们仍然作为(两个以上)指示器变量的 AND 和 OR 工作。
XOR:
<code>--a_XOR_b: max( var(--indicator_a) - var(--indicator_b), var(--indicator_b) - var(--indicator_a) );</code>
如果(且仅当)两个指示器具有相同的值时,两个差值都为 0px,max()
将返回此值。如果指示器具有不同的值,则一个项将给出 -1px,另一个将给出 1px。在这种情况下,max()
返回 1px。
如果有人对两个指示器相等的情况感兴趣,请使用以下方法:
<code>--a_EQ_b: calc(1px - max( var(--indicator_a) - var(--indicator_b), var(--indicator_b) - var(--indicator_a) ) );</code>
是的,这是 NOT(a XOR b). 我无法找到对此的“更好”的解决方案。
相等性可能对一般的 CSS 长度变量更有趣,而不仅仅是用于指示器变量。通过再次使用 clamp()
,这可能会有所帮助:
<code>--a_EQUALS_b_general: calc( 1px - clamp(0px, max( var(--var_a) - var(--var_b), var(--var_b) - var(--var_a) ), 1px) );</code>
删除 px 单位以获得无单位变量(整数)的一般相等性。
我认为这对于大多数布局来说已经足够布尔逻辑了!
附加内容 2:在网格布局中设置列数
由于乌鸦仅限于返回 CSS 长度值,因此它无法直接为网格选择列数(因为这是一个没有单位的值)。但是有一种方法可以使其工作(假设我们像上面那样声明了指示器变量):
<code>--number_of_cols_4_wide: 4; --number_of_cols_4_medium: 2; --number_of_cols_4_small: 1; --grid_gap: 0px; --grid_columns_width_4_wide: calc( (100% - (var(--number_of_cols_4_wide) - 1) * var(--grid_gap) ) / var(--number_of_cols_4_wide)); --grid_columns_width_4_medium: calc( (100% - (var(--number_of_cols_4_medium) - 1) * var(--grid_gap) ) / var(--number_of_cols_4_medium)); --grid_columns_width_4_small: calc( (100% - (var(--number_of_cols_4_small) - 1) * var(--grid_gap) ) / var(--number_of_cols_4_small)); --raven_grid_columns_width: calc( /* 使用乌鸦组合值 */ min(var(--is_wide) * var(--very_big_int),var(--grid_columns_width_4_wide)) min(var(--is_medium) * var(--very_big_int),var(--grid_columns_width_4_medium)) min(var(--is_small) * var(--very_big_int),var(--grid_columns_width_4_small)) );</code>
并使用以下方法设置你的网格:
<code>.grid_container{ display: grid; grid-template-columns: repeat(auto-fit, var(--raven_grid_columns_width)); gap: var(--grid_gap) };</code>
这是如何工作的?
- 为每个区间定义我们想要的列数(第 1、2、3 行)
- 计算每个区间的完美列宽(第 5、6、7 行)。这里发生了什么?
首先,我们计算列的可用空间。这是 100%,减去间隙将占据的空间。对于 n 列,有 (n-1) 个间隙。然后将此空间除以我们想要的列数。 3. 使用乌鸦计算实际 --base_size 的正确列宽。
在网格容器中,此行:
<code>grid-template-columns: repeat(auto-fit, var(--raven_grid_columns_width));</code>
……然后选择列数以适应乌鸦提供的值(这将导致我们上面的 --number_of_cols4??? 变量)。
乌鸦可能无法直接给出列数,但它可以给出长度,使 repeat
和 autofit
为我们计算我们想要的数字。
但是 auto-fit
与 minmax()
做同样的事情,对吧?不!上面的解决方案永远不会给出三列(或五列),并且列数不需要随着容器的宽度而增加。尝试在此 Pen 中设置以下值以查看乌鸦全力飞行:
<code>--number_of_cols_4_wide: 1; --number_of_cols_4_medium: 2; --number_of_cols_4_small: 4;</code>
附加内容 3:使用 linear-gradient()
更改背景颜色
这个有点更费脑筋。乌鸦完全是关于长度值的,那么我们如何从这些值中获得颜色呢?好吧,线性渐变同时处理两者。它们在由长度值定义的特定区域中定义颜色。让我们在进入代码之前更详细地讨论这个概念。
为了解决实际的渐变部分,一个众所周知的技术是将颜色停止点加倍,有效地使渐变部分在 0px 内发生。查看此代码以了解如何执行此操作:
<code>background-image:linear-gradient( to right, red 0%, red 50%, blue 50%, blue 100% );</code>
这将使你的背景左侧一半为红色,右侧为蓝色。注意第一个参数“to right”。这意味着百分比值是从左到右水平评估的。
通过乌鸦变量控制 50% 的值允许随意移动颜色停止点。我们可以添加更多颜色停止点。在运行示例中,我们需要三种颜色,从而产生两个(加倍的)内部颜色停止点。
添加一些颜色和颜色停止点的变量,这就是我们得到的:
<code>background-image: linear-gradient( to right, var(--color_small) 0px, var(--color_small) var(--first_lgbreak_value), var(--color_medium) var(--first_lgbreak_value), var(--color_medium) var(--second_lgbreak_value), var(--color_wide) var(--second_lgbreak_value), var(--color_wide) 100% );</code>
但是我们如何计算 --first_lgbreak_value 和 --second_lgbreak_value 的值呢?让我们看看。
第一个值控制 --color_small 的可见位置。在小区间中,它应该是 100%,在其他区间中应该是 0px。我们已经了解了如何使用乌鸦做到这一点。第二个变量控制 --color_medium 的可见性。对于小区间,它应该是 100%;对于中等区间,它应该是 100%;但对于宽区间,它应该是 0px。如果容器宽度位于小或中等区间,则相应的指示器必须为 1px。
由于我们可以对指示器进行布尔逻辑运算,因此它是:
<code>max(--is_small, --is_medium)</code>
……来获取正确的指示器。这给出:
<code>--first_lgbreak_value: min(var(--is_small) * var(--very_big_int), 100%); --second_lgbreak_value: min( max(var(--is_small), var(--is_medium)) * var(--very_big_int), 100%);</code>
将所有内容放在一起会产生以下 CSS 代码,该代码会根据宽度更改背景颜色(区间指示器如上所示计算):
<code>--first_lgbreak_value: min( var(--is_small) * var(--very_big_int), 100%); --second_lgbreak_value: min( max(var(--is_small), var(--is_medium)) * var(--very_big_int), 100%); --color_wide: red;/* 根据你的需求更改*/ --color_medium: green;/* 根据你的需求更改*/ --color_small: lightblue;/* 根据你的需求更改*/ background-image: linear-gradient( to right, var(--color_small) 0px, var(--color_small) var(--first_lgbreak_value), var(--color_medium) var(--first_lgbreak_value), var(--color_medium) var(--second_lgbreak_value), var(--color_wide) var(--second_lgbreak_value), var(--color_wide) 100% );</code>
这是一个可以查看其运行情况的 Pen。
附加内容 4:摆脱嵌套变量
在使用乌鸦时,我遇到一个奇怪的问题:calc()
中可以使用嵌套变量的数量有限. 当使用太多断点时,这可能会导致一些问题。据我了解,此限制是为了防止在计算样式时阻塞页面并允许更快地进行循环引用检查。
在我看来,“评估为值”之类的东西将是克服这个问题的好方法。然而,这个限制在突破 CSS 的限制时可能会让你头疼。希望这个问题将来能够得到解决。
有一种方法可以计算乌鸦的指示器变量,而无需使用(深度)嵌套变量。让我们看看 --is_medium 值的原始计算:
<code>--is_medium:calc( clamp(0px, var(--base_size) - var(--breakpoint_medium), 1px) - var(--is_wide) ); </code>
问题出现在减去 --is_wide 的地方。这会导致 CSS 解析器粘贴 --is_wide 的完整公式的定义。--is_small 的计算甚至有更多此类引用。(--is_wide 的定义甚至会被粘贴两次,因为它隐藏在 --is_medium 的定义中,并且也直接使用。)
幸运的是,有一种方法可以计算指示器,而无需引用较大断点的指示器。
当且仅当 --base_size 大于区间的下限断点且小于或等于区间的上限断点时,指示器才为真。此定义为我们提供了以下代码:
<code>--is_medium: min( clamp(0px, var(--base_size) - var(--breakpoint_medium), 1px), clamp(0px, 1px var(--breakpoint_wide) - var(--base_size), 1px) );</code>
-
min()
用作逻辑 AND 运算符 - 第一个
clamp()
是“--base_size 大于 --breakpoint_medium” - 第二个
clamp()
表示“--base_size 小于或等于 --breakpoint_wide。” - 添加 1px 将从“小于”切换到“小于或等于”。这有效,因为我们正在处理整数(像素)数字(a
指示器变量的完整计算可以通过这种方式完成:
<code>--is_wide: clamp(0px, var(--base_size) - var(--breakpoint_wide), 1px); --is_medium: min(clamp(0px, var(--base_size) - var(--breakpoint_medium), 1px), clamp(0px, 1px var(--breakpoint_wide) - var(--base_size), 1px) ); --is_small: clamp(0px,1px var(--breakpoint_medium) - var(--base_size), 1px);</code>
--is_wide 和 --is_small 的计算更简单,因为每个只需要检查一个给定的断点。
这适用于我们到目前为止所看到的所有内容。这是一个结合示例的 Pen。
最后的想法
乌鸦无法完成媒体查询可以完成的所有事情。但我们不需要它那样做,因为我们在 CSS 中有媒体查询。对于“大型”设计更改(例如侧边栏的位置或菜单的重新配置),使用它们是可以的。这些事情发生在整个视口(浏览器窗口的大小)的上下文中。
但是对于组件而言,媒体查询有点错误,因为我们永远不知道组件的大小。
Heydon Pickering 使用此图像演示了此问题:
我希望乌鸦能够帮助你克服为组件创建响应式布局的问题,并将“CSS 可以做什么”的限制进一步推高。
通过展示今天可能实现的功能,也许可以通过添加一些语法糖和一些非常小的新函数(如 conW、conH、“去除单位”或“评估为像素”)来完成“真正的”容器查询。如果 CSS 中有一个函数允许将“1px”重写为空格,而将“0px”重写为“initial”,则乌鸦可以与自定义属性切换技巧结合使用并更改每个 CSS 属性,而不仅仅是长度值。
通过避免使用 JavaScript 来实现这一点,你的布局渲染速度会更快,因为它不依赖于 JavaScript 的下载或运行。即使禁用了 JavaScript 也不重要。这些计算不会阻塞你的主线程,并且你的应用程序逻辑不会因设计逻辑而混乱。
感谢 Chris、Andrés Galante、Cathy Dutton、Marko Ilic 和 David Atanda 的精彩 CSS-Tricks 文章。它们确实帮助我探索了乌鸦可以实现的功能。
以上是乌鸦技术:距离容器查询更近一步的详细内容。更多信息请关注PHP中文网其他相关文章!

建立和维护自己的网站是一个好主意。您不仅拥有自己的平台,而且还可以在此过程中尝试Web技术。

在本周的综述中,可变字体获得倾斜,新的浏览器扩展名以及CSS模块的第一个版本。

您可以从标题中猜到的那样,本教程专用于Mavo:一种创建复杂,反应性,持久性Web应用程序的新的,平易近人的方法

大卫·德桑德罗(David Desandro)拥有大量超级酷的JavaScript库,多年来创建的。他的最新作品是Zdog,这是一个“圆形,平坦,设计师友好的伪-3D”


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

禅工作室 13.0.1
功能强大的PHP集成开发环境

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

Dreamweaver CS6
视觉化网页开发工具

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境