使用 vanilla-extract 配置 normalize.css

profile image

如何使用 vanilla-extract 在项目中直接配置 normalize.css

本帖由 Jetbrains's Coding Agent Junie junie logo翻译。如有任何翻译错误,请告知我们!

在项目中引入 vanilla-extract 时,我发现了一个遗憾点。

Tailwind CSS v4 默认包含了名为 preflight 的 normalize 样式,因此无需额外配置即可规范化浏览器默认样式,但 vanilla-extract 并没有这种功能。

所以我决定自己动手。

参考 modern-normalize 编写

我参考了 Tailwind 的 preflight 所基于的 modern-normalize

modern-normalize 是一个旨在最大限度减少浏览器间样式不一致,同时尽可能避免删除不必要样式的库。

利用 CSS Cascade Layers

normalize 样式的优先级应低于其他样式。使用 CSS Cascade Layers(以下简称 Layer)可以显式管理样式间的优先级,从而防止 normalize 样式意外覆盖其他样式。

我首先使用 vanilla-extract 的 globalLayer 定义了 Layer。globalLayer 接收 Layer 名称作为参数来创建 Layer,并返回创建的 Layer 引用值。

官方文档建议在 layers.css.tsexport 这个引用值,并在编写样式时使用引用值而不是 Layer 名称字符串。这是因为直接使用字符串形式的 Layer 名称,即使拼写错误也很难发现,而且在嵌套 Layer 等名称自动计算的情况下,使用引用值才能确保名称准确。

typescript
// layers.css.ts
import { globalLayer } from "@vanilla-extract/css";

export const normalize = globalLayer("normalize");

编写 normalize.css

在 vanilla-extract 中为全局选择器应用样式时使用 globalStyle。第一个参数是选择器字符串,第二个参数是样式对象。

typescript
import { globalStyle } from "@vanilla-extract/css";
import { normalize } from "./layers.css.ts";

/**
 * CSS Normalize based on modern-normalize v3.0.1
 * https://github.com/sindresorhus/modern-normalize
 * MIT License
 */

/*
Document
========
*/

/**
使用更好的盒模型 (opinionated)。
*/
globalStyle("*, ::before, ::after", {
  "@layer": {
    [normalize]: {
      boxSizing: "border-box",
    },
  },
});

/**
1. 在所有浏览器中使用一致的默认字体
2. 在所有浏览器中设置正确的 line height
3. 防止 iOS 在方向转换后调整字体大小
4. 使用更易读的 tab size (opinionated)
*/
globalStyle("html", {
  "@layer": {
    [normalize]: {
      fontFamily:
        'system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',
      lineHeight: 1.15,
      WebkitTextSizeAdjust: "100%",
      tabSize: 4,
    },
  },
});

/*
Sections
========
*/

/**
在所有浏览器中移除 margin
*/
globalStyle("body", {
  "@layer": {
    [normalize]: {
      margin: 0,
    },
  },
});

/*
Text-level semantics
====================
*/

/**
在 Chrome 和 Safari 中添加正确的字体粗细
*/
globalStyle("b, strong", {
  "@layer": {
    [normalize]: {
      fontWeight: "bolder",
    },
  },
});

/**
1. 在所有浏览器中使用一致的默认字体
2. 修复所有浏览器中奇怪的 'em' 字体大小
*/
globalStyle("code, kbd, samp, pre", {
  "@layer": {
    [normalize]: {
      fontFamily:
        'ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace',
      fontSize: "1em",
    },
  },
});

/**
在所有浏览器中添加正确的字体大小
*/
globalStyle("small", {
  "@layer": {
    [normalize]: {
      fontSize: "80%",
    },
  },
});

/**
防止 'sub' 和 'sup' 元素在所有浏览器中影响 line height
*/
globalStyle("sub, sup", {
  "@layer": {
    [normalize]: {
      fontSize: "75%",
      lineHeight: 0,
      position: "relative",
      verticalAlign: "baseline",
    },
  },
});

globalStyle("sub", {
  "@layer": {
    [normalize]: {
      bottom: "-0.25em",
    },
  },
});

globalStyle("sup", {
  "@layer": {
    [normalize]: {
      top: "-0.5em",
    },
  },
});

/*
Tabular data
============
*/

/**
修复 Chrome 和 Safari 中表格 border 颜色继承
*/
globalStyle("table", {
  "@layer": {
    [normalize]: {
      borderColor: "currentcolor",
    },
  },
});

/*
Forms
=====
*/

/**
1. 在所有浏览器中更改字体样式
2. 在 Firefox 和 Safari 中移除 margin
*/
globalStyle("button, input, optgroup, select, textarea", {
  "@layer": {
    [normalize]: {
      fontFamily: "inherit",
      fontSize: "100%",
      lineHeight: 1.15,
      margin: 0,
    },
  },
});

/**
修复 iOS 和 Safari 中可点击类型的样式
*/
globalStyle('button, [type="button"], [type="reset"], [type="submit"]', {
  "@layer": {
    [normalize]: {
      WebkitAppearance: "button",
    },
  },
});

/**
在所有浏览器中移除 padding,以免开发者将 'fieldset' 元素的值设为 0 时感到困惑
*/
globalStyle("legend", {
  "@layer": {
    [normalize]: {
      padding: 0,
    },
  },
});

/**
在 Chrome 和 Firefox 中添加正确的垂直对齐
*/
globalStyle("progress", {
  "@layer": {
    [normalize]: {
      verticalAlign: "baseline",
    },
  },
});

/**
修复 Safari 中增量/减量按钮的游标样式
*/
globalStyle("::-webkit-inner-spin-button, ::-webkit-outer-spin-button", {
  "@layer": {
    [normalize]: {
      height: "auto",
    },
  },
});

/**
1. 修复 Chrome 和 Safari 中奇怪的外观
2. 修复 Safari 中的 outline 样式
*/
globalStyle('[type="search"]', {
  "@layer": {
    [normalize]: {
      WebkitAppearance: "textfield",
      outlineOffset: "-2px",
    },
  },
});

/**
移除 macOS 上 Chrome 和 Safari 中的内部 padding
*/
globalStyle("::-webkit-search-decoration", {
  "@layer": {
    [normalize]: {
      WebkitAppearance: "none",
    },
  },
});

/**
1. 修复 iOS 和 Safari 中可点击类型的样式
2. 在 Safari 中将字体属性更改为 'inherit'
*/
globalStyle("::-webkit-file-upload-button", {
  "@layer": {
    [normalize]: {
      WebkitAppearance: "button",
      font: "inherit",
    },
  },
});

/*
Interactive
===========
*/

/**
在 Chrome 和 Safari 中添加正确的 display
*/
globalStyle("summary", {
  "@layer": {
    [normalize]: {
      display: "list-item",
    },
  },
});

我也上传到了 GitHub Gist! -> Modern CSS Normalize with Vanilla Extract & CSS Layers

global.css.ts

最后,必须最先导入 Layer 定义文件。由于 CSS Layer 的声明顺序直接影响优先级,如果不能保证顺序,normalize 样式可能无法按预期工作。

typescript
// global.css.ts
import "./layers.css"; // 必须最先导入
import "./normalize.css";

现在只需在项目入口点导入 global.css 文件即可!

ts
import "../global.css";

参考