跳转到内容

样式 & CSS

Astro 的设计为了使设计和编写 CSS 变得轻而易举。直接在 Astro 组件中编写你自己的 CSS,或者导入你最喜欢的 CSS 库,如 Tailwind 外。也支持高级样式设计语言,如 SassLess

为 Astro 组件设计样式,就像在你的组件或页面模板上添加 <style> 标签一样容易。当你在 Astro 组件内置了 <style> 标签时,Astro 就会自动检测 CSS 并开始为你处理样式。

src/components/MyComponent.astro
<style>
h1 { color: red; }
</style>

Astro <style> 标签内的 CSS 规则默认自动限定范围。作用域样式在幕后编译,只适用于写在同一组件内的 HTML。你在 Astro 组件中编写的 CSS 会自动封装在该组件中。

现有 CSS:

src/pages/index.astro
<style>
h1 {
color: red;
}
.text {
color: blue;
}
</style>

编译为:

<style>
h1[data-astro-cid-hhnqfkh6] {
color: red;
}
.text[data-astro-cid-hhnqfkh6] {
color: blue;
}
</style>

作用域样式不会泄漏,也不会影响你网站的其他部分。在 Astro 中,可以使用像 h1 {}p {} 这样的低特定性选择器,因为在最终输出中它们与作用域一起被编译。

作用域样式也不适用于模板内的其他 Astro 组件。如果你需要修改子组件的样式,可以考虑将该组件包裹在 <div>(或其他元素)中,然后你就可以对其编写样式了。

作用域样式的特定性被保留,允许它们与其他 CSS 文件或 CSS 库一起工作,同时仍然保留防止样式应用于组件之外的独特边界。

虽然我们推荐大多数组件使用范围化的样式,但有时你可能有必要使用全局的、非限定范围的 CSS。你可以通过 <style is:global> 属性选择不自动限定 CSS 范围。

src/components/GlobalStyles.astro
<style is:global>
/* 无作用域,直接传递给浏览器。
适用于网站上的所有 <h1> 标签。 */
h1 { color: red; }
</style>

你也可以使用 :global() 选择器在同一个 <style> 标签中混合全局和作用域 CSS 规则。这可以将 CSS 样式应用于子组件。

src/components/MixedStyles.astro
<style>
/* 仅限于此组件。 */
h1 { color: red; }
/* 混合:仅限于子组件的 <h1> 标签。 */
article :global(h1) {
color: blue;
}
</style>
<h1>Title</h1>
<article><slot /></article>

它适合用于像博客文章,或由 CMS 驱动的内容文档,这些内容在 Astro 之外的样式。但要注意会因为某个父级组件有无而导致样式不同会使得很难进行错误排查。

应尽可能多的使用作用域样式,而仅在必要时使用全局样式。

class:list 组合类名

段落标题 用 class:list 组合类名

如果你需要在元素上动态组合类名,你可以在 .astro 文件中使用 class:list 工具属性。

src/components/ClassList.astro
---
const { isRed } = Astro.props;
---
<!-- 如果 `isRed` 是真值,class 将是 "box red"。 -->
<!-- 如果 `isRed` 是假值,class 将是 "box"。 -->
<div class:list={['box', { red: isRed }]}><slot /></div>
<style>
.box { border: 1px solid blue; }
.red { border-color: red; }
</style>
参见 指令参考 页面,了解更多关于 class:list 的信息。

添加于: astro@0.21.0

Astro <style> 可以引用页面上任何可用的 CSS 变量。你也可以在组件的 frontmatter 中直接使用 define:vars 指令来传递 CSS 变量。

src/components/DefineVars.astro
---
const foregroundColor = "rgb(221 243 228)";
const backgroundColor = "rgb(24 121 78)";
---
<style define:vars={{ foregroundColor, backgroundColor }}>
h1 {
background-color: var(--backgroundColor);
color: var(--foregroundColor);
}
</style>
<h1>Hello</h1>
参见 指令参考 页面,了解更多关于 define:vars 的信息。

在 Astro 中,像 class 这样的 HTML 属性不会自动传递给子组件。

相反,你需要在子组件中接受一个 class 属性,并将其应用到根元素上。在解构时,你必须重命名它,因为 class 是 JavaScript 中的保留字

使用默认的作用域样式策略时,你还必须传递 data-astro-cid-* 属性。你可以通过将剩余的属性 ...rest 传递给组件来实现这一点。如果你将 scopedStyleStrategy 更改为 'class''where',则不需要 ...rest 属性。

src/components/MyComponent.astro
---
const { class: className, ...rest } = Astro.props;
---
<div class={className} {...rest}>
<slot/>
</div>
src/pages/index.astro
---
import MyComponent from "../components/MyComponent.astro"
---
<style>
.red {
color: red;
}
</style>
<MyComponent class="red">这将是红色!</MyComponent>

你可以使用 style 属性将 HTML 元素设置为内联样式。这可以是 CSS 字符串或 CSS 属性的对象:

src/pages/index.astro
// 这两种写法是等价的:
<p style={{ color: "brown", textDecoration: "underline" }}>My text</p>
<p style="color: brown; text-decoration: underline;">My text</p>

有两种使用外部全局样式表的方法:项目源文件中使用 ESM 导入;使用绝对链接引用 public/ 目录中的文件或托管于别处的文件。

阅读更多关于使用位于 public/src/静态资源 的内容。

你可以在 Astro 组件中使用 ESM 导入语法显式导入样式表。CSS 导入方式与 Astro 组件中的其他 ESM 导入一样,它应该基于组件进行引用,并且与其他导入一样必须位于组件脚本顶层

src/pages/index.astro
---
// Astro 会自动为你捆绑和优化这些CSS。
// 这也适用于预处理器文件,如 .scss、.styl 等。
import '../styles/utils.css';
---
<html><!-- 你的页面在这 --></html>

任何 JavaScript 文件都支持通过 ESM import导入 CSS,包括像 React 和 Preact 这样的 JSX 组件。这有助于为 React 组件编写细化的,具有针对性的样式

从 npm 包中导入样式表

段落标题 从 npm 包中导入样式表

你也可能需要从外部 npm 包中加载样式表。它常用于像导入 Open Props 这样的工具类。如果你的包建议使用文件扩展名(即 package-name/styles.css 而不是 package-name/styles),那么它的行为应该与本地样式表一致:

src/pages/random-page.astro
---
import 'package-name/styles.css';
---
<html><!-- 你的页面在这 --></html>

如果你所用的包不建议使用文件扩展名(即 包名/样式),那么你就需要先更新 Astro 配置

假设你从 package-name 导入名为 normalize 的 CSS 文件(省略文件扩展名)。为了确保我们能正确地预渲染你的页面,需要把package-name 添加到 vite.ssr.noExternal 数组中。

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
vite: {
ssr: {
noExternal: ['package-name'],
}
}
})

现在,你可以导入 package-name/normalize 了。Astro 将对其进行捆绑和优化,就像任何其他本地样式表一样。

src/pages/random-page.astro
---
import 'package-name/normalize';
---
<html><!-- 你的页面在这 --></html>
段落标题 通过 link 标签加载静态样式表

你也可以使用 <link> 元素在页面上加载样式表。它是应该位于 /public 目录下的 CSS 文件的绝对路径,或者是外部网站的链接。不支持使用相对路径的 <link> href 值。

src/pages/index.astro
<head>
<!-- 本地:/public/styles/global.css -->
<link rel="stylesheet" href="/styles/global.css" />
<!-- 外部 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.24.1/themes/prism-tomorrow.css">
</head>

因为这种方法使用 public/ 目录,它跳过了 Astro 提供的 CSS 处理、捆绑和压缩。如果你需要这些转换,请使用上面的导入样式表方法。

Astro 组件有时需要评估多个 CSS 源。例如,你的组件可能会导入 CSS 样式表,包含自己的 <style> 标签,并且被渲染在导入 CSS 的布局中。

当冲突的 CSS 规则应用于同一元素时,浏览器首先使用 特异性出现顺序 来确定要显示哪个值。

如果一个规则比另一个规则更具 特异性,无论 CSS 规则出现在哪里,它的值都将具有优先权:

src/components/MyComponent.astro
<style>
h1 { color: red }
div > h1 {
color: purple
}
</style>
<div>
<h1>
这个 header 将会是紫色的!
</h1>
</div>

如果两个规则具有相同的特异性,那么将评估 出现顺序,并且最后一个规则的值将具有优先权:

src/components/MyComponent.astro
<style>
h1 { color: purple }
h1 { color: red }
</style>
<div>
<h1>
这个 header 将会是红色的!
</h1>
</div>

Astro CSS 规则的评估顺序如下:

  • <link> 标签在 head 中(最低优先级)
  • 导入的样式
  • scoped 样式(最高优先级)

使用 作用域样式 不会增加 CSS 的 特异性,但它们始终在 出现顺序 中最后。因此,它们将优先于具有相同特异性的其他样式。例如,如果你导入了与 scoped 样式冲突的样式表,scoped 样式的值将被应用:

src/components/make-it-purple.css
h1 {
color: purple;
}
src/components/MyComponent.astro
---
import "./make-it-purple.css"
---
<style>
h1 { color: red }
</style>
<div>
<h1>
这个 header 将会是红色的!
</h1>
</div>

如果你导入的样式更具 特异性,那么它将优先于 scoped 样式:

src/components/make-it-purple.css
div > h1 {
color: purple;
}
src/components/MyComponent.astro
---
import "./make-it-purple.css"
---
<style>
h1 { color: red }
</style>
<div>
<h1>
这个 header 将会是紫色的!
</h1>
</div>

当在 Astro 组件中导入多个样式表时,将按导入的顺序评估 CSS 规则。无论何时评估 CSS,特异性总是决定要显示哪些样式。但是,当冲突的样式具有相同的特异性时,最后导入的 样式将获胜:

src/components/make-it-purple.css
div > h1 {
color: purple;
}
src/components/make-it-green.css
div > h1 {
color: green;
}
src/components/MyComponent.astro
---
import "./make-it-green.css"
import "./make-it-purple.css"
---
<style>
h1 { color: red }
</style>
<div>
<h1>
这个 header 将会是紫色的!
</h1>
</div>

虽然 <style> 标签是作用域的,并且只适用于声明它们的组件,但 导入的 CSS 可以“泄漏”。导入组件会应用它导入的任何 CSS,即使该组件从未使用:

src/components/PurpleComponent.astro
---
import "./make-it-purple.css"
---
<div>
<h1>我导入紫色的 CSS。</h1>
</div>
MyComponent.astro
---
import "./make-it-green.css"
import PurpleComponent from "./PurpleComponent.astro";
---
<style>
h1 { color: red }
</style>
<div>
<h1>
这个 header 将会是紫色的!
</h1>
</div>

通过 link 标签 加载的样式表按顺序评估,优先于 Astro 文件中的任何其他样式。因此,这些样式将具有低于导入样式表和作用域样式的优先级:

src/pages/index.astro
---
import "../components/make-it-purple.css"
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
<link rel="stylesheet" href="/styles/make-it-blue.css" />
</head>
<body>
<div>
<h1>这将是紫色的</h1>
</div>
</body>
</html>

Astro 支持在项目中添加流行的 CSS 库、工具和框架,如 Tailwind 和其他库!

要在项目中使用 Tailwind,请使用包管理器的astro add 命令安装官方的 Astro Tailwind 集成

终端窗口
npx astro add tailwind
参见 集成指南,了解安装、导入和配置这些集成的说明。

Astro 支持通过 Vite 使用 CSS 预处理器,如 SassStylusLess

终端窗口
npm install sass

.astro 文件中使用 <style lang="scss"><style lang="sass">

终端窗口
npm install stylus

.astro 文件中使用 <style lang="styl"><style lang="stylus">

终端窗口
npm install less

.astro 文件中使用 <style lang="less">

终端窗口
npm install lightningcss

astro.config.mjs 中更新你的 vite 配置:

astro.config.mjs
import { defineConfig } from 'astro/config'
export default defineConfig({
vite: {
css: {
transformer: "lightningcss",
},
},
})

你也可以在 JS 框架内使用上述所有的 CSS 预处理程序!请务必遵循每个框架推荐的模式。

  • React / Preact: import Styles from './styles.module.scss';
  • Vue: <style lang="scss">
  • Svelte: <style lang="scss">

Astro 内置了 PostCSS,作为 Vite 的一部分。要为你的项目配置 PostCSS,在项目根部创建 postcss.config.cjs 文件。然后你就可以在安装插件后使用 require() 导入插件(例如 npm install autoprefixer)。

postcss.config.cjs
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano'),
],
};

.jsx 文件同时支持全局 CSS 和 CSS 模块。要启用后者,请使用 .module.css 扩展名(如果使用 Sass 则是 .module.scss/.module.sass )。

src/components/MyReactComponent.jsx
import './global.css'; // 包括全局 CSS
import Styles from './styles.module.css'; // 使用 CSS 模块(必须以 `.module.css`、`.module.scss` 或 `.module.sass` 结尾!)

Astro 中的 Vue 支持与 vue-loader 一样的方法。

Astro 中的 Svelte 也完全按照预期工作。Svelte Styling Docs

任何 Astro 样式方法都可以用于 Markdown 布局组件。但是不同的方式可能会对你的页面产生不同的样式效果。

你可以通过向包装页面内容的布局中添加 导入的样式表 来将全局样式应用于 Markdown 内容。你还可以在布局组件中使用 <style is:global> 标签 来为你的 Markdown 格式设置样式。请注意,添加的任何样式都会受到 Astro 的级联顺序的影响,因此你应该仔细检查渲染后的页面,确保你的样式能够按照预期生效。

你也可以添加包括 Tailwind 在内的 CSS 集成。如果你使用 Tailwind,typography 插件 可以用于样式化 Markdown。

当 Astro 为你的网站构建生产部署时,它会将你的 CSS 压缩并合并到 chunks 中。你网站上的每个页面都有自己的 chunk,此外,多个页面之间共享的 CSS 还会被进一步拆分为自己的 chunk 以供重用。

然而,当你有多个页面共享样式时,一些共享的 chunks 可能会变得非常小。如果它们都被单独发送,那么将会导致许多样式表请求,并影响网站的性能。因此,默认情况下,Astro 仅将 HTML 中大小超过 4kB 的样式表链接为 <link rel="stylesheet"> 标签,而将较小的样式表内联到 <style type="text/css"> 中。这种方法在额外请求的数量和可以在页面之间缓存的 CSS 量之间提供了一个平衡。

你可以使用 assetsInlineLimit vite 构建选项来配置样式表在多大的情况下会被链接到外部(以字节为单位)。请注意,此选项也会影响内联的脚本和图像。

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
vite: {
build: {
assetsInlineLimit: 1024,
}
};
});

如果你希望所有项目样式都保持外部样式表,可以配置 inlineStylesheets 构建选项。

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
build: {
inlineStylesheets: 'never'
}
});

你也可以将该选项设置为 'always' ,这样就可以内联所有的样式表了。

对于高级用例,CSS 可以直接从磁盘上读取,而 Astro 不进行捆绑或压缩。当你需要完全控制某些 CSS 片段,并需要绕过 Astro 的自动 CSS 处理时,这可能有用的。

对于大多数用户来说,不建议这样做:

src/components/RawInlineStyles.astro
---
// 高级用例! 不建议大多数用户使用
import rawStylesCSS from '../styles/main.css?raw';
---
<style is:inline set:html={rawStylesCSS}></style>

完整的细节见 Vite 的文档

一个高级用例,你可以在你的项目 src/ 目录中直接导入 CSS 文件链接。当你需要完全控制 CSS 文件在页面上的加载方式时可能有用。然而,这将阻止该 CSS 文件和你的页面上的其他 CSS 进行优化。

对于大多数用户来说,不建议这样做。相反,将 CSS 文件放在 public/ 内,你可以获得一致的链接。

src/components/RawStylesUrl.astro
---
// 高级用例! 不建议大多数用户使用
import stylesUrl from '../styles/main.css?url';
---
<link rel="preload" href={stylesUrl} as="style">
<link rel="stylesheet" href={stylesUrl}>

完整的细节见 Vite 文档

贡献

你有什么想法?

社区