”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 将 Tailwind 配置为设计系统

将 Tailwind 配置为设计系统

发布于2024-11-06
浏览:385

对于设计系统来说,一致性和理解性就是一切。一个好的设计系统通过实现它的代码的配置来确保实现的一致性。它需要是:

  • 易于理解,无需放弃良好设计所需的细微差别;
  • 可扩展和可维护,且不影响一致性。

使用我的 React 和 Tailwind 的默认堆栈,我将向您展示如何设置自己的版式、颜色和间距默认值,而不仅仅是区分应用程序外观和感觉的起点。更重要的是,它大大减少了我们必须编写和维护的代码,从而减少了以系统、一致和无错误的方式实现样式的精神负担。

我将从我经常看到的一个主要批评开始,然后分解我用来解决它的一系列配置步骤。

易于使用并不等于易于掌握知识

Tailwind 使开发人员可以轻松编写样式,这对于快速原型设计非常有用。但这种易用性并不能保证良好的设计或可扩展、可维护的设计系统。

像 Tailwind 这样的默认和零配置工具是基础设施速度层,可以为构建创造更多时间。但是,如果您要扩展一个使用设计系统来区分自己的应用程序,则不能仅仅依赖“像午餐一样免费”的开箱即用配置。

如果您使用默认的 Tailwind 配置运行并将样式管理推送到组件上的类应用程序,结果通常是一堆难以推理的类分布在组件中,伪装成设计系统。

Configuring Tailwind as a Design System

上面是一个很好的例子。它几乎难以辨认,需要大量时间才能理解,更不用说操纵了。尝试这样做很可能会导致重复和错误,从而逐渐偏离整个应用程序的设计一致性。

很容易将您的设计类合并到单个类名中。但这样做并不容易。

配置您的系统以方便了解

易用性需要权衡。使用别人的标准意味着依赖他们的专业知识。这可能是有益的,但也可能是一个陷阱。让我们退后一步,思考一下设计系统的基础知识包括哪些内容:

  • 排版
  • 颜色
  • 间距
  • 响应能力(包括颜色模式)

在 React with Tailwind 的上下文中,这些和许多其他设计系统元素都在 Tailwind 配置中设置,我们可以对其进行自定义。

{/* 更漂亮-忽略 */}

const config = {
  theme: {
    fontSize: { /* ... */ },
    colors: { /* ... */ },
    spacing: { /* ... */ },
  },
};

印刷默认值

您是否曾经努力记住小文本的正确字母间距?如果您可以设置一次然后忘记它怎么办?

我们可以直接在 tailwind.config 中将前导(行高)和跟踪(字母间距)设置为每个字体大小元组的参数。这意味着当我们使用字体大小类时,我们不需要设置行距或跟踪。无需记住(或无法查找)小文本的字母间距是多少。

fontSize: {
  small: [
    "13px",
    { lineHeight: 1.5, letterSpacing: "0.015em" },
  ],
  base: [
    "16px",
    { lineHeight: 1.5, letterSpacing: 0 },
  ],
}

现在使用 text-small 设置字体大小、行高和字母间距。将核心排版元组封装在一个类中,将这些值的实现集中到配置中,而不是跨代码库。可维护性的巨大胜利。

/* 13px/1.5 with 0.015em letter-spacing */

颜色默认值

我们可以使用 CSS 变量在 :root 和 html.dark 范围下设置响应颜色。这意味着我们编写和管理一个类,例如 bg-canvas,而不是两个类,例如 bg-gray-100 dark:bg-gray-800。

@import "@radix-ui/colors/gray.css";
@import "@radix-ui/colors/gray-dark.css";

:root {
  --color-gray-base: var(--gray-1);
  --color-gray-bg: var(--gray-3);
  --color-gray-line: var(--gray-4);
  --color-gray-border: var(--gray-5);
  --color-gray-solid: var(--gray-10);
  --color-gray-fill: var(--gray-12);
}

因为我在这里使用 Radix Colors,所以我不需要设置 .dark 范围,因为这已经为我完成了。如果您不喜欢基数颜色,您可以自定义它们、使用其他库或编写自己的库。

然后在Tailwind配置中设置CSS变量。

colors: {
  canvas: "var(--color-gray-base)",
  background: "var(--color-gray-bg)",
  line: "var(--color-gray-line)",
  border: "var(--color-gray-border)",
  solid: "var(--color-gray-solid)",
  fill: "var(--color-gray-fill-contrast)",
}

现在使用 bg-canvas 在浅色或深色模式下设置适当的颜色。在代码库中删除这种重复可以将颜色管理集中到我们的配置中,而不是将其分散到组件上的类的实现中。认知和可维护性的巨大胜利。

/* sets --gray-1 as #fcfcfc on :root or #111111 on html.dark */

语义命名

我提倡颜色和字体大小的语义名称,因为语义命名是一种将含义与使用联系起来的强制功能。这样做可以消除实现猜测工作并减少错误。

我见过无数的项目,其中不一致的gray-50、gray-100或gray-200都用于背景。通过定义一种名为背景的颜色可以轻松解决这个问题。

同样,当深色和浅色文本颜色称为填充和纯色时,更容易记住它们的名称。当它们被称为gray-900和gray-600时,它会更难,也更容易出错,因为你必须特别记住它不是gray-950和gray-500,或者gray-800和gray-700。

但是命名事物并就命名达成一致是很困难的。本着零配置的精神,我建议采用 Radix Color 的背景、边框、实体和填充范例。或者这个调色板语义。

一旦你在 tailwind.config 中设置了此项,Typescript 就会通过自动完成功能唤起你的记忆。

避免命名空间冲突

如果您要扩展 Tailwind 主题而不是编写自己的主题,请不要使用已经使用过的音阶键。您可能会无意中覆盖您需要使用的类。

您会注意到在前面的颜色配置示例中,我将 --color-gray-base var 设置为 canvas,而不是 base。如果我使用基础,那么使用此色阶作为文本颜色(文本基础)将与默认字体大小基础值发生冲突,该值也是文本基础。

这并不是自定义 Tailwind 配置的缺点,而是其主题命名的遗留问题:在 Tailwind 中设置字体大小或颜色类都使用 text-*.1

间距默认值

我们还可以使用CSS变量来设置间距。

:root {
  --height-nav: 80px;
  --height-tab: 54px;
  --space-inset: 20px;
  --container-text-px: 660px;
  --container-hero-px: 1000px;
}
spacing: {
  em: "1em", /* relate icon size to parent font-size */
  nav: "var(--height-nav)",
  inset: "var(--space-inset)",
  text: "var(--container-text)",
  hero: "var(--container-hero)",
}

有人可能会说这是过度设计。除了计算复杂的交互式布局(如粘性标题、滚动边距等)之外,这种预先配置工作使其在像素方面变得直接且无错误。

/* ... */

再次注意语义命名的使用使其易于记忆和使用。

增强您的 Tailwind 配置

我们现在已经在一个集中的地方以易于理解和维护的方式配置了版式、颜色和间距标记。而且我们不需要编写那么多的类来实现系统。获胜。我们可以采取进一步的措施来减少这种实现开销。

Clamp() 你的类

如果我告诉你有一种方法可以完全避免到处写 text-lg lg:text-xl xl:text-2xl p-2 md:p-4 lg:p-8 呢?

我们可以通过在tailwind.config中使用clamp作为字体大小值来避免设置响应式字体大小类。这是我使用的简单钳位函数。

fontSize: {
  title: [
    /* clamp(17px, 14.1429px   0.5714vw, 21px) */
    generateClampSize(500, 1200, 17, 21),
    { lineHeight: 1.5, letterSpacing: "-0.015em" },
  ];
}

因此,我们可以只写 text-title,而不是写 text-lg lg:text-xl xl:text-2xl。再一次,通过将字体大小响应提升到钳位值,我们再次避免了“实现类”陷阱,节省了脑力劳动、错误和调试时间。

请记住,这意味着通过正确配置 Tailwind,我们已从 text-lg lg:text-xl xl:text-2xlleading-none Tracking-wide 转移到 text-title。赢了!

/* 17px at 500px, 21px at 1200, fluidly calculated inbetween */
/* …with default line-height and letter-spacing also specified */

Heading copy

我们也可以对间距执行此操作。扩展主题时,我在这些键前加上 d 作为“动态”前缀,以将其与默认间距比例区分开来。

spacing: {
  /* lower value is 2/3 of upper value */
  d4: generateClampSize(500, 1200, 10.5, 16),
  d8: generateClampSize(500, 1200, 21, 32),
  d16: generateClampSize(500, 1200, 43, 64),
  d24: generateClampSize(500, 1200, 64, 96),
  d64: generateClampSize(500, 1200, 171, 256),
}

这允许我们编写 py-d24 而不是 py-16 md:py-20 lg:py-24。这减轻了我们在脑海中为每个媒体查询保留一系列网站版本的负担。相反,它鼓励我们想象流畅响应的布局,其中测量并不像一致的关系那么重要。

/* ... */
/* ... */

概括

精心设计的 UI 是您抵御即将到来的粗心人工智能应用程序浪潮的最后一道防线。以下是自定义 Tailwind 如何节省您的时间和麻烦,以便您可以专注于构建眨眼间即可运行的 UI 所需的不合理的关注:

  • 充分利用 tailwind.config 的潜力。集中和分组您的设计令牌,避免“到处实现类”陷阱。
  • 使用clamp()进行流畅的排版和间距。
  • 在 :root 和 .dark 上设置颜色变量以轻松实现深色模式。
  • 从语义上命名颜色和间距:背景每天都会比灰色 100。
  • 使用 size-em 将图标与文本大小相关联。

是的,有前期时间成本。但它确实得到了回报:更少的代码、更少的错误、更高的设计一致性以及真正理解系统的团队。

接下来:我们将探索如何使用 Class Variance Authority 来创建一个防弹样式 API,其中包含从 Tailwind 中提取的语义属性。敬请关注。


  1. 这也是我不喜欢使用 tailwind-merge 删除 JSX 中重复的 Tailwind 类的原因。通常,我发现当两者都需要时,它会删除文本颜色以支持文本字体大小。我很惊讶更多的开发人员没有提出这个问题。 ↩

版本声明 本文转载于:https://dev.to/callumflack/configuring-tailwind-as-a-design-system-2f5h?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • Python读取CSV文件UnicodeDecodeError终极解决方法
    Python读取CSV文件UnicodeDecodeError终极解决方法
    在试图使用已内置的CSV模块读取Python中时,CSV文件中的Unicode Decode Decode Decode Decode decode Error读取,您可能会遇到错误的错误:无法解码字节 在位置2-3中:截断\ uxxxxxxxx逃脱当CSV文件包含特殊字符或Unicode的路径逃...
    编程 发布于2025-04-27
  • 如何在JavaScript对象中动态设置键?
    如何在JavaScript对象中动态设置键?
    在尝试为JavaScript对象创建动态键时,如何使用此Syntax jsObj['key' i] = 'example' 1;不工作。正确的方法采用方括号: jsobj ['key''i] ='example'1; 在JavaScript中,数组是一...
    编程 发布于2025-04-27
  • 如何在Java中正确显示“ DD/MM/YYYY HH:MM:SS.SS”格式的当前日期和时间?
    如何在Java中正确显示“ DD/MM/YYYY HH:MM:SS.SS”格式的当前日期和时间?
    如何在“ dd/mm/yyyy hh:mm:mm:ss.ss”格式“ gormat 解决方案: args)抛出异常{ 日历cal = calendar.getInstance(); SimpleDateFormat SDF =新的SimpleDateFormat(“...
    编程 发布于2025-04-27
  • 如何避免Go语言切片时的内存泄漏?
    如何避免Go语言切片时的内存泄漏?
    ,a [j:] ...虽然通常有效,但如果使用指针,可能会导致内存泄漏。这是因为原始的备份阵列保持完整,这意味着新切片外部指针引用的任何对象仍然可能占据内存。 copy(a [i:] 对于k,n:= len(a)-j i,len(a); k
    编程 发布于2025-04-27
  • 在Java中如何为PNG文件添加坐标轴和标签?
    在Java中如何为PNG文件添加坐标轴和标签?
    如何用java 在现有png映像中添加轴和标签的axes和labels如何注释png文件可能具有挑战性。与其尝试可能导致错误和不一致的修改,不如建议在图表创建过程中集成注释。使用JFReechArt import java.awt.color; 导入java.awt.eventqueue; 导入...
    编程 发布于2025-04-27
  • 为什么不使用CSS`content'属性显示图像?
    为什么不使用CSS`content'属性显示图像?
    在Firefox extemers属性为某些图像很大,&& && && &&华倍华倍[华氏华倍华氏度]很少见,却是某些浏览属性很少,尤其是特定于Firefox的某些浏览器未能在使用内容属性引用时未能显示图像的情况。这可以在提供的CSS类中看到:。googlepic { 内容:url(&#...
    编程 发布于2025-04-27
  • 找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    如何在mySQL中使用mySql 检索最大计数,您可能会遇到一个问题,您可能会在尝试使用以下命令:理解错误正确找到由名称列分组的值的最大计数,请使用以下修改后的查询: 计数(*)为c 来自EMP1 按名称组 c desc订购 限制1 查询说明 select语句提取名称列和每个名称...
    编程 发布于2025-04-27
  • 如何在鼠标单击时编程选择DIV中的所有文本?
    如何在鼠标单击时编程选择DIV中的所有文本?
    在鼠标上选择div文本单击单击单个鼠标单击单击单击?这允许用户轻松拖放所选的文本或直接复制它。 在单个鼠标上单击的div元素中选择文本,您可以使用以下Javascript函数: function selecttext(canduterid){ if(document.Selection){...
    编程 发布于2025-04-27
  • 为什么尽管有效代码,为什么在PHP中捕获输入?
    为什么尽管有效代码,为什么在PHP中捕获输入?
    在php ;?>" method="post">The intention is to capture the input from the text box and display it when the submit button is clicked.但是,输出...
    编程 发布于2025-04-27
  • 哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    在Python Matplotlib's path.contains_points FunctionMatplotlib's path.contains_points function employs a path object to represent the polygon.它...
    编程 发布于2025-04-27
  • 在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在C中的显式删除 在C中的动态内存分配时,开发人员通常会想知道是否有必要在heap-procal extrable exit exit上进行手动调用“ delete”操作员,但开发人员通常会想知道是否需要手动调用“ delete”操作员。本文深入研究了这个主题。 在C主函数中,使用了动态分配变量(H...
    编程 发布于2025-04-27
  • `console.log`显示修改后对象值异常的原因
    `console.log`显示修改后对象值异常的原因
    foo = [{id:1},{id:2},{id:3},{id:4},{id:id:5},],]; console.log('foo1',foo,foo.length); foo.splice(2,1); console.log('foo2', foo, foo....
    编程 发布于2025-04-27
  • 如何使用“ JSON”软件包解析JSON阵列?
    如何使用“ JSON”软件包解析JSON阵列?
    parsing JSON与JSON软件包 QUALDALS:考虑以下go代码:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    编程 发布于2025-04-27
  • HTML格式标签
    HTML格式标签
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    编程 发布于2025-04-27
  • Java中如何使用观察者模式实现自定义事件?
    Java中如何使用观察者模式实现自定义事件?
    在Java 中创建自定义事件的自定义事件在许多编程场景中都是无关紧要的,使组件能够基于特定的触发器相互通信。本文旨在解决以下内容:问题语句我们如何在Java中实现自定义事件以促进基于特定事件的对象之间的交互,定义了管理订阅者的类界面。以下代码片段演示了如何使用观察者模式创建自定义事件: args)...
    编程 发布于2025-04-27

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3