# 通过 escape 覆盖组件

> 通过 .htmlpress/ 替换内置的布局、样式、脚本和组件，或添加你自己的。

Htmlpress 附带一个布局、一个主题、两个脚本和一个组件库，全部编译进二进制文件中。每个内置文件都可以被替换：在 `.htmlpress/` 下的相同路径放一个文件，构建就会改用它。

| 要覆盖的内容  | 放置文件的位置                                           |
| ------- | ------------------------------------------------- |
| 内容外围的页面 | `.htmlpress/layout.html`                          |
| 全局主题    | `.htmlpress/styles/<name>.css`                    |
| 某个全局脚本  | `.htmlpress/scripts/<name>.js`                    |
| 单个组件    | `.htmlpress/components/<name>/<name>.css` 或 `.js` |

- .htmlpress

  - layout.html

  - layouts
    - landing.html

  - styles

    - tokens.css
    - layout.css
    - content.css

  - scripts
    - code.js

  - components

    - callout
      - callout.css

    - tabs

      - tabs.css
      - tabs.js

    - credits
      - credits.html

* `layout.html`

  内容外围的页面，是带有由 htmlpress 填充的布局元素的普通 HTML，例如 `<htmlpress-content>` 和 `<htmlpress-nav>`。

* `styles/*.css`

  全局主题，在每个页面上合并为一个 `theme.css`： `tokens.css`（`--htmlpress-*` 的取值）、 `layout.css`（页面的全局样式）和 `content.css`（标题、正文、表格、代码）。

* `scripts/code.js`

  代码块上的复制按钮，以及每个标题上的链接。

* `scripts/hints.js`

  缩写和脚注引用上的悬停提示。

* `components/<name>/<name>.css` 和 `.js`

  每个组件一组文件，包括布局框架中的元素，放在以其标签名（去掉 `htmlpress-` 前缀）命名的文件夹中。其样式位于 `@scope (<tag>)` 中，因此绝不会影响到组件之外，其脚本也只定义该元素。页面只链接它所使用的组件。

无需其他任何改动：htmlpress 的其余部分会自动采用弹出的文件。

## `htmlpress escape` 命令

`htmlpress escape` 会把一份默认文件复制到 `.htmlpress/` 中作为起点。可以按路径指定文件，或按标签指定组件以获取它的所有文件：

```sh
$ htmlpress escape                       # list every file, marking the escaped ones
$ htmlpress escape htmlpress-tabs        # its .css and .js
$ htmlpress escape layout.html styles/tokens.css
$ htmlpress escape htmlpress-tabs -r docs  # in another project folder
```

`htmlpress escape` 绝不会覆盖你已有的文件：如需重新开始，请先删除它。弹出的副本保留的是复制时的代码，因此升级之后它不会包含新的默认内容。

删除弹出的文件即可恢复默认：在 `htmlpress start` 运行时立即生效，否则在下一次 `start` 或 `build` 时生效。

## 示例：署名

在 `components/` 下以新名称创建文件夹，即可添加你自己的组件 `<htmlpress-<name>>`，其中每个文件都以文件夹名命名。它的 `.html` 会在构建时填充布局和页面中的每个此类标签；它的 `.css` 和 `.js` 会在该标签出现的地方加载。

```html
<!-- .htmlpress/components/credits/credits.html -->
<footer><p>Built by my team · 2026</p></footer>

/* .htmlpress/components/credits/credits.css */
@scope (htmlpress-credits) {
  :scope { display: block; }
  p { color: var(--htmlpress-muted); }
}
```

然后把 `<htmlpress-credits></htmlpress-credits>` 放入弹出的 `layout.html` 或任意页面中。保存后，开发服务器会立即采用它。名称由小写字母、数字和连字符组成。

## 示例：重新设计按钮主题

```sh
$ htmlpress escape components/button/button.css
```

编辑 `.htmlpress/components/button/button.css` 即可全局修改按钮样式：每个使用 `<htmlpress-button>` 的页面都会链接你的副本，而不是内置的那份。

## 示例：调整一个令牌

```sh
$ htmlpress escape styles/tokens.css
```

编辑 `.htmlpress/styles/tokens.css` 中的 `:root` 块来更换品牌风格：

```
:root {
  --htmlpress-accent: oklch(0.55 0.24 27); /* red */
}
```

若只针对单个页面，可以改为在该页面自己的 `<style>` 中覆盖令牌；无需弹出。

## 在覆盖中使用其他组件

弹出的 `layout.html`、你自己的布局以及组件的 `.html` 中都可以包含任何 `htmlpress-*` 组件，它会像在页面中一样渲染并链接其样式。组件的 `.html` 中不能包含另一个项目组件的标签：它只会被粘贴一次，不会再次被重写。

### 限定组件样式的作用域

组件的标签会保留在其 `.html` 所填充的标记外层，并保留其属性，因此无论是否有 `.html`，它的 `.css` 都把作用域限定在该标签上： `@scope (htmlpress-credits)`。在 CSS 另行规定之前，自定义元素是行内元素。要从另一个组件中重新设置某个组件的样式，需要胜过它的 `:scope`：使用 `:scope htmlpress-header`，而不是单独的 `htmlpress-header`。

## 页面切换

在页面之间切换时只有内容淡入淡出：`<htmlpress-header>` 和 `<htmlpress-sidebar>` 带有视图过渡名称 `htmlpress-header` 和 `htmlpress-sidebar`，并立即切换到新页面。悬停链接时会预渲染其页面，点击时即已就绪。 页头的按钮在构建时写入每个页面；head 中的脚本会在 `<html>` 上设置 `data-scripted`，没有它时这些按钮保持隐藏，因此只在脚本下可用的内容请放在 `:root[data-scripted]` 下设置样式。

## 无法以这种方式覆盖的内容

- **布局元素的标记**： `<htmlpress-nav>`、`<htmlpress-page-toc>`、 `<htmlpress-pager>` 及其他布局元素内部的标记是根据站点生成的，因此只能弹出它们的 `.css` 和 `.js`。 `components/brand/brand.html` 可以用你自己的静态 HTML 填充其中一个；其内置的 `.css` 仍会作用于该标签，所以如需重新设置样式，也要把它弹出。
- **使用不同布局的页面**：请改用 [`.htmlpress/layouts/`](/zh/customization/custom-routes/)。
- **二进制文件**：页面读取、导航和命令都不可覆盖；如需修改，请 fork 仓库。
- **配置 schema**：`htmlpress.yaml` 的结构是固定的。

> **Warning**
>
> `components/` 下名称不属于 htmlpress 内置组件的文件夹，例如 `calout/`，会新增一个组件，而不是替换已有组件。直接放在 `components/` 中的文件会导致构建失败， `styles/` 或 `scripts/` 下任何没有替换对象的文件同样如此。

## 另请参阅

- [语法主题](/zh/customization/syntax-themes/)：代码块的外观。
- [自定义路由](/zh/customization/custom-routes/)：拥有独立布局的页面，而不是替换默认布局。
