lang 属性只能与一种语言相关联。这意味着 <html>
属性只能使用一种语言,即使网页上有多个语言也是如此。将 lang
设置为页面的主要语言。
<html lang="ar,en,fr,pt">...</html>
<html lang="ar">...</html>
链接
与按钮类似,链接的可访问名称主要来自其文本内容。创建链接时,一个不错的技巧是将最有意义的文字放入链接本身,而不是放入“此处”或“了解详情”等填充性文字。
Check out our guide to web performance <a href="/guide">here</a>.
Check out <a href="/guide">our guide to web performance</a>.
检查动画是否触发布局
使用 transform
以外的其他方式移动元素的动画可能会运行缓慢。在以下示例中,我通过为 top
和 left
添加动画并使用 transform
实现了相同的视觉效果。
.box { position: absolute; top: 10px; left: 10px; animation: move 3s ease infinite; } @keyframes move { 50% { top: calc(90vh - 160px); left: calc(90vw - 200px); } }
.box { position: absolute; top: 10px; left: 10px; animation: move 3s ease infinite; } @keyframes move { 50% { transform: translate(calc(90vw - 200px), calc(90vh - 160px)); } }
您可以在以下两个 Glitch 示例中进行测试,并使用 DevTools 探索性能。
使用相同的标记,我们可以将 padding-top: 56.25%
替换为 aspect-ratio: 16 / 9
,并将 aspect-ratio
设置为指定的 width
/ height
比率。
.container { width: 100%; padding-top: 56.25%; }
.container { width: 100%; aspect-ratio: 16 / 9; }
使用 aspect-ratio
而非 padding-top
会更清晰,并且不会对内边距属性进行全面改造,以执行超出其常规范围的操作。
是的,没错,我使用 reduce
来链接 Promise 序列。我很聪明。但这种有点很智能的编码还是不要为好。
不过,如果使用异步函数改写以上代码,又容易让代码变得过于顺序:
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); } }
function markHandled(...promises) { Promise.allSettled(promises); } async function logInOrder(urls) { // fetch all the URLs in parallel const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); markHandled(...textPromises); // log them in sequence for (const textPromise of textPromises) { console.log(await textPromise); } }
reduce
位替换成了标准单调乏味但可读性强的 for 循环。
编写 Houdini 自定义属性
下面是一个设置自定义属性(例如 CSS 变量)的示例,但现在添加了语法(类型)、初始值(回退)和继承布尔值(它是否从父级继承值?)。目前,执行此操作的方法是在 JavaScript 中使用 CSS.registerProperty()
,但在 Chromium 85 及更高版本中,CSS 文件将支持 @property
语法:
CSS.registerProperty({ name: '--colorPrimary', syntax: '' , initialValue: 'magenta', inherits: false });
@property --colorPrimary { syntax: '' ; initial-value: magenta; inherits: false; }
现在,您可以像访问任何其他 CSS 自定义属性一样,通过 var(--colorPrimary)
访问 --colorPrimary
。不过,这里的区别在于,--colorPrimary
不仅仅是作为字符串读取。它有数据!
CSS backdrop-filter
会对半透明或透明的元素应用一个或多个效果。为了理解这一点,请参考以下图片。

.frosty-glass-pane { backdrop-filter: blur(2px); }

.frosty-glass-pane { opacity: .9; backdrop-filter: blur(2px); }
左侧的图片显示了如果未使用或不支持 backdrop-filter
,重叠元素将如何呈现。右侧的图片使用 backdrop-filter
应用了模糊处理效果。请注意,它除了使用 backdrop-filter
外,还使用了 opacity
。如果没有 opacity
,就没有可应用模糊处理的内容。毋庸置疑,如果将 opacity
设为 1
(完全不透明),则对背景没有影响。
不过,与 unload
事件不同,beforeunload
有合法用途。例如,当您想要警告用户,如果他们离开页面,未保存的更改将会丢失。在这种情况下,建议您仅在用户有未保存的更改时添加 beforeunload
监听器,然后在未保存的更改保存后立即将其移除。
window.addEventListener('beforeunload', (event) => { if (pageHasUnsavedChanges()) { event.preventDefault(); return event.returnValue = 'Are you sure you want to exit?'; } });
beforeunload
监听器。
function beforeUnloadListener(event) { event.preventDefault(); return event.returnValue = 'Are you sure you want to exit?'; }; // A function that invokes a callback when the page has unsaved changes. onPageHasUnsavedChanges(() => { window.addEventListener('beforeunload', beforeUnloadListener); }); // A function that invokes a callback when the page's unsaved changes are resolved. onAllChangesSaved(() => { window.removeEventListener('beforeunload', beforeUnloadListener); });
beforeunload
监听器(并在不需要时将其移除)。
尽可能减少使用 Cache-Control: no-store
Cache-Control: no-store
是一种 HTTP 标头,Web 服务器可以在响应中设置此标头,以指示浏览器不要将响应存储在任何 HTTP 缓存中。此属性应用于包含敏感用户信息的资源,例如需要登录才能访问的页面。
包含每个输入组 (.fieldset-item
) 的 fieldset
元素使用 gap: 1px
在元素之间创建细线边框。无需复杂的边界解决方案!
.grid { display: grid; gap: 1px; background: var(--bg-surface-1); & > .fieldset-item { background: var(--bg-surface-2); } }
.grid { display: grid; & > .fieldset-item { background: var(--bg-surface-2); &:not(:last-child) { border-bottom: 1px solid var(--bg-surface-1); } } }
自然网格换行
最复杂的布局最终是宏布局,即 <main>
和 <form>
之间的逻辑布局系统。
<input type="checkbox" id="text-notifications" name="text-notifications" >
<label for="text-notifications"> <h3>Text Messages</h3> <small>Get notified about all text messages sent to your device</small> </label>
包含每个输入组 (.fieldset-item
) 的 fieldset
元素使用 gap: 1px
在元素之间创建细线边框。无需复杂的边界解决方案!
.grid { display: grid; gap: 1px; background: var(--bg-surface-1); & > .fieldset-item { background: var(--bg-surface-2); } }
.grid { display: grid; & > .fieldset-item { background: var(--bg-surface-2); &:not(:last-child) { border-bottom: 1px solid var(--bg-surface-1); } } }
标签页 <header>
布局
下一个布局几乎相同:我使用 flex 创建垂直排序。
<snap-tabs> <header> <nav></nav> <span class="snap-indicator"></span> </header> <section></section> </snap-tabs>
header { display: flex; flex-direction: column; }
.snap-indicator
应随一组链接水平移动,此标题布局有助于设置相应阶段。此处没有绝对定位元素!
轻柔弹性是一种更纯粹的仅限居中策略。它柔和细腻,因为与 place-content: center
不同,在居中期间,子级框的大小不会发生变化。尽可能轻柔地将所有内容堆叠、居中和间隔。
.gentle-flex {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1ch;
}
- 仅处理对齐、方向和分布
- 在一个位置集中进行修改和维护
- Gap 可确保 n 个子项之间保持等距
- 代码行数最多
非常适合宏观和微观布局。
用法
.gap-example {
display: grid;
gap: 10px;
gap: 2ch;
gap: 5%;
gap: 1em;
gap: 3vmax;
}
可以为 Gap 传递 1 个长度,该长度将同时用于行和列。
.grid { display: grid; gap: 10px; }
.grid { display: grid; row-gap: 10px; column-gap: 10px; }
Gap 可以传递 2 个长度,分别用于行和列。
.grid { display: grid; gap: 10px 5%; }
.grid { display: grid; row-gap: 10px; column-gap: 5%; }