张世民跟随创客、开发人员、函数式语言和怪诞字体的粉丝。
# 使用 Sprinkles 构建您自己的类型安全版本的 Tailwind CSS
##
2022 年 7 月 15 日 6 分钟阅读
我是 CSS 库vanilla-extract的忠实粉丝;在我看来,它是自 Tailwind CSS 以来最令人兴奋的 CSS 工具。如果您是vanilla-extract的新手,它是由 Mark Dalgleish 创建的 CSS-in-JS 库,可让您利用 TypeScript 的强大功能来编写 CSS。
我对 vanilla-extract 的杀手级功能Sprinkles API感到特别兴奋。Sprinkles 允许您根据首要原则创建完全定制的设计系统。您可以使用 Sprinkles 创建自己的原子、实用程序优先的 CSS 框架,而不是使用现成的库,例如Tailwind 和 Tachyon。
在本教程中,我们将设置一个新的 Next.js 应用程序并使用 vanilla-extract 的 Sprinkles API 创建一个自定义的、实用程序优先的 CSS 框架,称为 Sprinkles Tailwind。然后,我们将使用我们的新框架在 Tailwind CSS 主页上重新创建产品卡片演示。在此过程中,我们将看到 vanilla-extract 和 Sprinkles 如何处理伪类、响应式设计和暗模式主题。
- 为什么要使用洒水器?
- Next.js 和 vanilla-extract 入门
- 使用 Sprinkles 创建设计系统
- 将我们的 Sprinkles 库与 Tailwind CSS 进行比较
- 实施主题和响应式设计
- 添加深色主题
那么,当我们可以简单地使用 Tailwind 时,我们为什么要开发我们自己的基于 Sprinkles、实用程序优先的 CSS 库,它已经是一个如此流行的工具?三个原因:类型安全、自定义和本地范围。
首先,使用 Sprinkles,您可以获得与使用任何其他 Typescript 变量相同的类型提示和编译时验证,不再需要文档验证和类名拼写错误。
其次,Tailwind 既固执己见,也有一点学习曲线,尤其是在您不使用 Tailwind 预设语法的情况下。使用 Sprinkles,您正在编写 CSS 而不是 Tailwind 的速记类名。由于 Sprinkles 不以任何内置值开头,因此您可以按照自己喜欢的方式创建 CSS 框架。
最后,还有局部范围。vanilla-extract 在底层使用 CSS 模块的好处之一是,您的所有实用程序类现在默认情况下都是组件范围的。这意味着您可以轻松地将多个设计系统整合到单个应用程序中,而不必担心样式冲突。
## Next.js 和 vanilla-extract 入门
让我们首先创建一个新的 Next 应用程序并安装 vanilla-extract。您可以在此处的 GitHub 存储库中查看此演示项目的完整源代码。
首先创建一个新的 TypeScript 版本的 Next,如下所示:
------
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验了解更多 →
------
```typescript
npx create-next-app@latest --typescript 洒-tailwind
```
运行yarn dev以确保我们的安装按预期工作后,我们将安装 vanilla-extract 和 Sprinkles:
```shell
纱线添加 @vanilla-extract/css @vanilla-extract/babel-plugin @vanilla-extract/next-plugin @vanilla-extract/sprinkles
```
接下来,通过更新以下内容,在我们的 webpack 构建过程中包含 vanilla-extract/next-plugin next.config.js:
```js
常量 {
createVanillaExtractPlugin
} = require('@vanilla-extract/next-plugin');
const withVanillaExtract = createVanillaExtractPlugin();
/** @type {import('next').NextConfig} */
常量 nextConfig = {
reactStrictMode:真,
};
module.exports = withVanillaExtract(nextConfig);
```
最后,因为我们希望我们的新 Sprinkles 类型被 TypeScript 编译器和我们的 IDE 的 TypeScript 服务器拾取,我们将添加*.css.ts到我们的项目中tsconfig.json:
```json
{
...
"include": ["next-env.d.ts", "**/*.ts", "**/*.css.ts", "**/*.tsx"],
...
}
```
## 使用 Sprinkles 创建设计系统
Tailwind CSS 的主要卖点之一是它如何迫使用户在定义明确的设计系统的约束下工作,而不是不一致的一次性值。
Tailwind 还预装了一个默认系统,该系统具有合理的值和合理的快捷方式,可满足常见的布局需求。在我们的项目 Sprinkles Tailwind 中,我们将从Open Props项目中获取调色板、排版和尺寸比例。我们还将为布局添加一些常见的 Tailwind 值。
首先,我们将首先colors.css.ts在目录下创建一个文件./styles并定义我们的颜色值:
```typescript
常量调色板 = {
'灰色-0':'#f8f9fa',
'灰色-1':'#f1f3f5',
...
'灰色 9': '#212529',
'red-0': '#fff5f5',
... /* 完整调色板请参见 https://github.com/Shimin-Zhang/TailSprinkles/blob/main/styles/colors.css.ts */
};
导出默认调色板;
我们将为 , 做同样的事情border.css.ts来size.css.ts建立typography.css.ts我们设计系统的基础。
一旦我们设置了我们的设计系统值,我们将像这样定义我们的 Sprinkles 系统:
```typescript
进口 {
定义属性,
创建Sprinkles,
} 来自'@vanilla-extract/sprinkles';
从'./colors.css'导入调色板;
从'./typography.css'导入{fontFamily,fontWeight,lineHeight,fontSize};
从'./size.css'导入{大小,空间};
从'./border.css'导入{borderSize,borderRadius};
const responsiveProperties = defineProperties({
特性: {
显示:['none','flex','block','inline'],
flexDirection:['行','列'],
证明内容:[
'拉紧',
'弹性开始',
'中央',
'弹性端',
'空间周围',
'空间之间'
],
对齐项目:[
'拉紧',
'弹性开始',
'中央',
'弹性端'
],
paddingTop:空间,
paddingBottom:空间,
paddingLeft:空间,
paddingRight:空格,
...
},
速记:{
填充:[
'填充顶部',
'填充底部',
'paddingLeft',
'paddingRight'
],
paddingX: ['paddingLeft', 'paddingRight'],
paddingY: ['paddingTop', 'paddingBottom'],
...
placeItems: ['justifyContent', 'alignItems']
}
});
阿里云盘TV电视盒子,所有资源无限速下载,在线播放无限制!常量系统属性 = 定义属性({
特性: {
颜色:调色板,
背景:调色板,
字体家族:字体家族,
字体大小:字体大小,
...
}
});
出口常量洒= createSprinkles(
响应属性,
系统属性
);
导出类型 Sprinkles = 参数<typeof splashs>[0];
Sprinkles 的defineProperties功能将我们的设计系统标记转换为一组样式值。然后通过 将这些值组合在一起,createSprinkles以便用户可以以 CSS-in-JS 方式使用它们。
请注意,我们将属性分为两个单独的类别。responsiveProperties是我们稍后将以类似于 Tailwind 的视口前缀的响应方式设置样式,即md:. 系统属性将取决于主题,类似于 Tailwind 的dark:前缀。然后,我们导出生成原子类的 Sprinkles 对象及其类型,这为我们提供了类型安全性。
该shorthands键允许我们定义类似于 CSS 速记和 Tailwindpx-*, p-*类的 CSS 速记值。
接下来,我们将创建一个新页面并导入我们新的类型安全实用程序框架:
```typescript
从“下一个/头”导入头
从'../styles/sprinkles.css'导入{洒};
导出默认函数颜色(){
返回 (
<div>
<头部>
</头>
<h1 类名={
小雨({
...风格在这里
})
}>
欢迎来到颜色
</h1>
</div>
);
}
```
## 将我们的 Sprinkles 库与 Tailwind CSS 进行比较
让我们看看 Sprinkles 的类型安全操作。首先,我们将尝试添加一个有效的 CSS 属性 ,backgroundColor它没有在我们的 Sprinkles 应用程序中定义。我们看到它抛出了一个类型错误:
接下来,我们添加color键并看到有效值列表显示为建议:
正如预期的那样,使用不在系统内的颜色值会立即引发类型错误:
这比在文档中查找正确的 Tailwind 类名称、打错字、然后等待浏览器刷新才注意到它要快得多。
设计系统全部设置好后,我们现在可以从 Tailwind 主页中获取产品卡片示例,复制示例 HTML,并将其转换为我们新的 Sprinkles Tailwind 框架:
```html
<h1>
顺风洒水
</h1>
<div
类名 = {
英石({
显示:'弹性',
fontFamily: '无衬线',
宽度:'尺寸内容-3',
marginY: '自动尺寸',
背景:“白色”,
边界半径:'radius-3',
boxShadow:'shadow-2'
})
}>
<div
类名 = {
英石({
弹性:'无',
宽度:“尺寸-12”,
高度:'尺寸-13',
位置:'相对',
})
}
>
<img src="/jacket.jpg" alt=""
加载="懒惰" 类名 = {
英石({
位置:'绝对',
宽度:'满',
高度:“满”,
objectFit: '封面',
插图:'0'
})}/>
</div>
...
/* 完整文件见 https://github.com/Shimin-Zhang/TailSprinkles/blob/main/pages/index.tsx */
因为我们已经在我们的sprinkles.css.ts文件中定义了我们的速记,我们可以marginY在我们的样式中使用而不是marginTop单独marginBottom编写。
我们在设计产品卡时遇到了一些问题,这些问题仅靠 Sprinkles 无法解决。Sprinkles 是纯 CSS 属性之上的最小抽象层,因此我们无法访问实用程序类,例如指示我们的按钮可点击srOnly的伪选择。:hover
我们需要引入 vanilla-extract 来创建这些更复杂的样式:
```typescript
// 首先我们导入 vanilla-extract
从'@vanilla-extract/css'导入{样式};
// 我们创建更复杂的类
导出 const srOnly = style({
位置:'绝对',
宽度:'1px',
高度:'1px',
填充:'0',
边距:'-1px',
溢出:'隐藏',
剪辑:'矩形(0, 0, 0, 0)',
whiteSpace: 'nowrap',
边框宽度:'0'
});
导出 const clickable = style ({
':悬停':{
光标:'指针'
}
})
```
进行了一些配置,但我们设法复制了相同的夹克产品卡!虽然这不是一个精确的复制品(我们的设计系统基于 Open Props),但它是一个合理的复制品,捕捉了原始设计的精神:
------
- 不要错过来自 LogRocket 的精选时事通讯The Replay
- 使用 React 的 useEffect优化应用程序的性能
- 在多个 Node 版本之间切换
- 了解如何使用 AnimXYZ 为您的 React 应用程序制作动画
- 探索 Tauri,一个用于构建二进制文件的新框架
- 比较NestJS 与 Express.js
- 发现TypeScript 领域中使用的流行 ORM
------
仔细查看生成的 HTML,我们看到 Sprinkles 根据我们的设计标记生成了带前缀的实用程序类名称。请注意,每个类都通过 CSS 模块限定为组件:
## 实施主题和响应式设计
我们的卡在桌面上看起来很棒,但是当我们缩小视口时它没有响应。为了解决这个问题,我们将在 Sprinkles 设置中添加基于视口的条件。还记得之前我们命名了布局属性responsiveProperties吗?我们现在将添加该视口信息:
```typescript
const responsiveProperties = defineProperties({
条件: {
移动的: {},
平板电脑:{'@media':'屏幕和(最小宽度:768px)'},
},
默认条件:'平板电脑',
特性: {
... 和之前一样
因为我们从桌面版本开始,我们无法从移动优先设计开始,而是默认使用平板电脑视图。使用 Sprinkles 可以轻松创建响应式样式;而不是property: value我们目前看到的格式,我们将使用property: { condition-name: value }语法来代替。
这是带有附加移动视口布局的组件的外观:
```css
<div
类名 = {
英石({
显示:'弹性',
fontFamily: '无衬线',
宽度: {
平板电脑:'size-content-3',
移动设备:“尺寸自动”
},
marginY: '自动尺寸',
边距X:{
平板电脑:'尺寸自动' ,
手机:'size-3' ,
},
背景:“白色”,
可以做半径:' radius-3 ' ,
boxShadow:' shadow-2 '
})
} >
```
添加其余的移动视口布局后,我们的卡片现在可以响应了:
我们的产品卡真的要凑齐了!
## 添加深色主题
如今,我们组件通常需要的最后一个功能是能够在各种主题之间切换。让我们为我们的产品卡片添加一个深色主题,以展示它是如何使用 Sprinkles 完成的。
与响应式设计一样,我们首先向系统属性添加条件和默认值:
```typescript
常量系统属性 = 定义属性({
条件: {
灯光模式:{},
darkMode: { '@media': '(prefers-color-scheme: dark)' }:{ '@media' :'(首选颜色方案:深色)' }
},},
默认条件:'lightMode',: '光模式' ,
```
与我们的响应式大小不同,这里的默认lightMode值已经是我们想要的。我们只需要在需要时添加深色主题变体。就像响应式道具一样,我们将使用条件名称来区分两个主题。
早期的<div>响应式设计和暗模式现在看起来像这样:
```css
< div
类名= {
st ({
显示:'flex',
fontFamily : '无衬线' ,
宽度:{
平板电脑:'size-content-3',
移动:“尺寸自动”
},
marginY : '尺寸自动' ,
边距X:{
平板电脑:“尺寸自动”,
手机:'size-3',
},
背景:{
lightMode : '白色' ,
darkMode : 'grape-9' ,
},
边界半径:' radius-3 ' ,
boxShadow:'影子-2 '
})
} >
```
现在,我们最终的深色主题产品卡片如下所示:
## 结论
我希望这篇文章能说服你在下次开始一个新项目并在市场上寻找一个新的 CSS 框架时尝试一下 vanilla-extract。
就像 Tailwind CSS 一样,Sprinkles 框架允许您创建基于设计系统基础的实用程序优先的 CSS 框架。
与 Tailwind CSS 不同,使用 Sprinkles,您可以获得与其他 TypeScript 文件相同的类型安全保证,您可以按照您想要的方式对其进行完全自定义,并且您可以获得 CSS 模块的范围安全。
我非常看好 vanilla-extract 引入的类型安全的 CSS 范例。请在下面的评论部分分享您从 Tailwind CSS 迁移到 Sprinkles 的想法和经验。
## 您的前端是否占用了用户的 CPU?
随着 Web 前端变得越来越复杂,资源贪婪的功能对浏览器的要求也越来越高。如果您对监控和跟踪生产环境中所有用户的客户端 CPU 使用情况、内存使用情况等感兴趣,请尝试使用 LogRocket。
LogRocket就像一个用于 Web 和移动应用程序的 DVR,记录您的 Web 应用程序或网站中发生的一切。无需猜测问题发生的原因,您可以汇总和报告关键前端性能指标、重放用户会话以及应用程序状态、记录网络请求并自动显示所有错误。