Skip to content
Lenso UI

主题

使用 CSS 变量和全局样式自定义 HeroUI 的设计系统

HeroUI 使用 CSS 变量和 BEM 类来实现主题化。你可以使用标准 CSS 自定义从颜色到组件样式的所有内容。

想要创建你自己的主题? 试试 主题构建器,以可视化方式自定义颜色、圆角、字体等,然后导出 CSS 用于你的项目。

工作原理

HeroUI 的主题系统构建于 Tailwind CSS v4 的主题之上。当你导入 @lenso/tokens 时,它会使用 Tailwind 的内置调色板,将其映射到语义化变量,自动在浅色和深色主题之间切换,并使用 CSS 层和 @theme 指令进行组织。

命名规则:

  • 不带后缀的颜色用作背景(例如 --accent)
  • 带 -foreground 后缀的颜色用于该背景上的文本(例如 --accent-foreground)

快速开始

应用主题: 将主题类添加到 HTML 并将颜色应用到 body 上:

<html class="light" data-theme="light">  <body class="bg-background text-foreground">    <!-- Your app -->  </body></html>

切换主题:

<!-- Light theme --><html class="light" data-theme="light">
<!-- Dark theme --><html class="dark" data-theme="dark">

使用 next-themes 以编程方式切换主题(适用于 Next.js):

首先,用 ThemeProvider 包装你的应用:

// app/providers.tsx"use client";
import { ThemeProvider } from "next-themes";
export function Providers({ children }: { children: React.ReactNode }) {  return (    <ThemeProvider attribute="class" defaultTheme="light">      {children}    </ThemeProvider>  );}
// app/layout.tsximport { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {  return (    <html lang="en" suppressHydrationWarning>      <body className="bg-background text-foreground">        <Providers>{children}</Providers>      </body>    </html>  );}

然后使用 useTheme 在主题之间切换:

"use client";
import { useTheme } from "next-themes";
export function ThemeSwitch() {  const { theme, setTheme } = useTheme();
  return (    <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>      Toggle {theme === "dark" ? "Light" : "Dark"} Mode    </button>  );}

覆盖颜色:

/* app/globals.css */@import "tailwindcss";@import "@lenso/tokens";
:root {  /* Override any color variable */  --accent: oklch(0.7 0.25 260);  --success: oklch(0.65 0.15 155);}

注意:完整的调色板和视觉参考请参见 颜色。

深色模式:如需基于 next-themes 与 HeroUI useTheme 钩子的完整配置指南,请参见 深色模式。

创建你自己的主题:

View code

使用你的主题:

/* app/globals.css */@layer theme, base, components, utilities;
@import "tailwindcss";@import "@lenso/tokens";
@import "./src/themes/ocean.css" layer(theme); /* [!code highlight]*/

应用你的主题:

<!-- index.html -->
<!-- Light ocean --><html data-theme="ocean">
<!-- Dark ocean --><html data-theme="ocean-dark">

自定义组件

全局组件样式: 使用 BEM 类覆盖任何组件:

@layer components {  /* Customize buttons */  .button {    @apply font-semibold tracking-wide;  }
  .button--primary {    @apply bg-blue-600 hover:bg-blue-700;  }
  /* Customize accordions */  .accordion__trigger {    @apply text-lg font-bold;  }}

注意:完整的样式参考请参见 样式。

查找组件类名: 每个组件文档页面都会列出所有可用的类名(基类、修饰符、元素、状态)。示例:Button 类名

导入策略

完整导入(推荐): 两行代码即可获得全部内容:

@import "tailwindcss";@import "@lenso/tokens";

按需导入: 只导入你需要的内容:

/* Define layers */@layer theme, base, components, utilities;
/* Base requirements */@import "tailwindcss";@import "@lenso/tokens/base" layer(base);/* OR specific base file */@import "@lenso/tokens/base/base.css" layer(base);
/* Theme variables */@import "@lenso/tokens/themes/shared/theme.css" layer(theme);@import "@lenso/tokens/themes/default" layer(theme);/* OR specific theme files */@import "@lenso/tokens/themes/default/index.css" layer(theme);@import "@lenso/tokens/themes/default/variables.css" layer(theme);
/* Components (all components) */@import "@lenso/tokens/components" layer(components);/* OR specific component files */@import "@lenso/tokens/components/index.css" layer(components);@import "@lenso/tokens/components/button.css" layer(components);@import "@lenso/tokens/components/accordion.css" layer(components);
/* Utilities (optional) */@import "@lenso/tokens/utilities" layer(utilities);
/* Variants (optional) */@import "@lenso/tokens/variants" layer(utilities);

注意:目录导入(例如 @lenso/tokens/components)会自动解析为其对应的 index.css 文件。使用显式文件路径(例如 @lenso/tokens/components/button.css)来导入单个组件的样式。

Headless 模式: 从头开始构建你自己的样式:

@import "tailwindcss";@import "@lenso/tokens/base/base.css";
/* Your custom styles */.button {  /* Your button styles */}

添加自定义颜色

将你自己的语义化颜色添加到主题中:

/* Define in both light and dark themes */:root,[data-theme="light"] {  --info: oklch(0.6 0.15 210);  --info-foreground: oklch(0.98 0 0);}
.dark,[data-theme="dark"] {  --info: oklch(0.7 0.12 210);  --info-foreground: oklch(0.15 0 0);}
/* Make the color available to Tailwind */@theme inline {  --color-info: var(--info);  --color-info-foreground: var(--info-foreground);}

现在你可以在组件中使用它:

<div className="bg-info text-info-foreground">Info message</div>

变量参考

HeroUI 在 variables.css 中定义了三种类型的变量:

  1. 基础变量(Base Variables) — 不会变化的值,例如 --white、--black、间距以及排版
  2. 主题变量(Theme Variables) — 在浅色 / 深色主题之间切换的颜色,以及滚动条令牌(--scrollbar-thumb、--scrollbar-width 等)
  3. 计算变量(Calculated Variables) — 悬停状态、柔和(soft)变体以及边框 / 分隔线层级(每个浅色 / 深色主题中的 Calculated Colors 区块,使用 color-mix() 计算)

如需完整参考,请查阅:颜色文档、默认主题变量、共享主题工具

Tailwind 主题桥接(@theme inline):

themes/shared/theme.css 将语义化变量映射为 Tailwind 令牌(--color-*、--radius-*、--ease-*)。计算颜色引用的是 variables.css 中的底层变量(例如 --surface-hover、--accent-soft)—— 它们并不会在该文件中通过 color-mix() 内联展开:

View code

表单控件依赖 --field-* 主题变量。悬停、聚焦及边框变体定义于 variables.css 的 Calculated Colors 区块中,并在 theme.css 中映射到 Tailwind 令牌(例如 --color-field-hover: var(--field-hover))。在你的主题中覆盖 --field-background、--field-hover 及相关令牌,即可重新设计输入框、复选框、单选框和 OTP 输入槽的样式,而不会影响按钮或卡片等表面(surface)。

滚动条

HeroUI 为组件中的滚动区域(表格、弹出框、抽屉等)应用统一的滚动条样式。滚动条使用标准 CSS 属性(scrollbar-width、scrollbar-color、scrollbar-gutter),不再依赖 ::-webkit-scrollbar 覆盖。

模式 — 在 <html>、组件根元素或滚动插槽上设置 data-scrollbar:

模式data-scrollbar行为
HeroUI 纤细(未设置) 或 thin使用主题令牌呈现的纤细滑块
操作系统 / 浏览器default原生滚动条(auto)
隐藏none不显示滚动条(scrollbar-width: none)
<!-- 全局使用原生滚动条 --><html data-scrollbar="default">
<!-- 在某个子树中恢复 HeroUI 滚动条 --><main data-scrollbar="thin">  ...</main>
<!-- 在某个子树中隐藏滚动条(例如弹出框) --><div data-scrollbar="none">  ...</div>

主题变量 — 在 variables.css 的浅色与深色主题区块中定义:

变量描述
--scrollbar-thumb滑块颜色(默认通过 color-mix 混入 15% 的 --foreground)
--scrollbar-track轨道颜色(默认 transparent)
--scrollbar-gutter滚动条间隙(默认 auto)
--scrollbar-widthscrollbar-width 属性(默认 thin)
--scrollbar-colorscrollbar-color 属性(默认为滑块颜色 + 轨道颜色)
--scrollbar--scrollbar-thumb 的旧版别名

全局自定义:

/* app/globals.css */:root {  --scrollbar-thumb: color-mix(in oklch, var(--accent) 30%, transparent);  --scrollbar-gutter: auto;}

按滚动插槽 — 在组件上传入 data-scrollbar,或在外层包裹元素上覆盖相关令牌:

<Table data-scrollbar="default">  ...</Table>
<Drawer.Content data-scrollbar="thin" />
<Calendar.YearPickerGrid data-scrollbar="none" />

自定义溢出区域 — 在自己的元素上使用 @lenso/tokens 提供的 scrollbar、scrollbar-thin、scrollbar-default 或 scrollbar-none 工具类。基于类的覆盖方式详见 样式。

注意:部分组件默认隐藏滚动条(日期选择器弹出框、颜色选择器、次级标签页)。嵌套的滚动插槽(例如日期选择器内部的日历年份选择器)会保留 HeroUI 滚动条,因为 scrollbar-none 只作用于其所在的元素本身,不会影响使用 @apply scrollbar 的后代元素。

资源