Theme Presets
The five built-in presets are lh-ui’s default aesthetic statement: each takes a primary color from traditional Chinese colorways, paired with a complete light and dark palette.
Preset overview
Section titled “Preset overview”| Preset | name |
Identifier | labelEn | Description | Light primary |
|---|---|---|---|---|---|
| cerblue porcelain | 'cerblue' |
LhCerblue |
Ceramic Blue | Underglaze blue-and-white porcelain, elegant and pristine | No |
| songhua green | 'songhua' |
LhSonghua |
Pine Green | Tall bamboo groves, full of vitality | Yes |
| zhusha red | 'zhusha' |
LhZhusha |
Vermilion | Traditional seal cinnabar red, festive and solemn | No |
| ink wash | 'shuimo' |
LhShuimo |
Ink Wash | Ink-wash painting, elegantly minimal | No |
| golden | 'golden' |
LhGolden |
Golden Autumn | Late-autumn maple gold, warm and golden | Yes |
cerblue · Ceramic Blue青花瓷釉下彩,清雅素净
--lh-primary-color#2B4C7C--lh-primary-color-hover#3D649E--lh-primary-color-active#1F3A5F--lh-primary-color-bg#E8EDF3--lh-primary-color-border#D4DCE8
songhua · Pine Green竹林修竹,生机勃勃
--lh-primary-color#5B8C5A--lh-primary-color-hover#7BB079--lh-primary-color-active#4F8F4C--lh-primary-color-bg#E8F5E8--lh-primary-color-border#D0E8CF--lh-color-on-primary#101410--lh-selected-color#456F44
zhusha · Vermilion传统印章朱砂红,喜庆庄重
--lh-primary-color#B23A3A--lh-primary-color-hover#C45050--lh-primary-color-active#9A3030--lh-primary-color-bg#FDF0F0--lh-primary-color-border#F0D8D8
shuimo · Ink Wash水墨丹青,素雅极简
--lh-primary-color#2C2C2C--lh-primary-color-hover#5A5A5A--lh-primary-color-active#1A1A1A--lh-primary-color-bg#F5F5F5--lh-primary-color-border#E0E0E0
golden · Golden Autumn深秋枫黄,温暖金黄
--lh-primary-color#B8860B--lh-primary-color-hover#D4A843--lh-primary-color-active#A87F0A--lh-primary-color-bg#FBF6EA--lh-primary-color-border#F0E8D0--lh-color-on-primary#1E1A16--lh-selected-color#82600A
Vars of each preset
Section titled “Vars of each preset”The base five keys (every preset has them)
Section titled “The base five keys (every preset has them)”{ '--lh-primary-color': '...', '--lh-primary-color-hover': '...', '--lh-primary-color-active': '...', '--lh-primary-color-bg': '...', '--lh-primary-color-border': '...',}Two extra keys for light-primary presets
Section titled “Two extra keys for light-primary presets”songhua (songhua green) and golden (golden) are light primaries — white text on them fails AA contrast, so two extra keys are required:
| Key | Purpose |
|---|---|
--lh-color-on-primary |
Text color on primary-colored buttons (light primaries switch to ink text) |
--lh-selected-color |
Text color of the selected state (the active color as selected text on a hover background has insufficient contrast, so it is deepened separately) |
Why they are required: a light-primary button with white text only reaches about 3.x:1, below the AA requirement of 4.5:1. The verify:theme gate will fail it.
Taking songhua green as an example, the source comments record the reasoning behind the fixes:
--lh-color-on-primary: songhua is a light primary; white text is only 3.93:1, so ink text is used at 4.73:1--lh-selected-color: the active color as selected text on a hover background is only 3.48:1, so the selected state is deepened separately (the button active background is unaffected)--lh-primary-color-active: the original#4B7A4Aat 3.70:1 →#4F8F4Cat 4.75:1
Golden is the same: white text 3.25:1 → ink text 5.31:1; selected text 3.41:1 → deepened to #82600A; active #9A7208 at 3.94:1 → #A87F0A at 4.70:1.
Hard requirement for new light-primary presets: you must provide --lh-color-on-primary and --lh-selected-color, otherwise the AA 4.5:1 gate in verify:theme will fail.
Using presets
Section titled “Using presets”Getting a preset
Section titled “Getting a preset”import { LH_THEME_PRESETS, getThemePreset, getThemeVars } from '@lh-ui/share/theme'
LH_THEME_PRESETS // all fivegetThemePreset('cerblue') // the LhCerblue objectgetThemePreset('not-exists') // undefinedgetThemeVars('cerblue')?.['--lh-primary-color']Injecting a preset (UI layer)
Section titled “Injecting a preset (UI layer)”import { buildPresetStyleText, LH_PRESET_THEME_STYLE_ID } from '@lh-ui/share/theme'
function applyPreset(name: LhThemePresetName) { const vars = getThemeVars(name) if (!vars) return
const text = buildPresetStyleText(vars) let style = document.getElementById(LH_PRESET_THEME_STYLE_ID) if (!style) { style = document.createElement('style') style.id = LH_PRESET_THEME_STYLE_ID document.head.appendChild(style) } style.textContent = text // replace, not append, so no stale custom styles linger when switching themes}Key point (see the injection constraint in the theme overview): you may only write a <style> element, never documentElement’s inline style, or the dark-mode palette will be distorted.
Each UI library’s ConfigProvider
Section titled “Each UI library’s ConfigProvider”<!-- lh-ui-plus / lh-ui-vue2 --><lh-config-provider theme="cerblue"> <App /></lh-config-provider>The theme prop accepts a LhThemePresetName, and the component runs the injection logic above internally. The docs theme-customization page and ThemeStudio iterate LH_THEME_PRESETS to list every preset automatically — after adding a preset, no manual edits to each library’s docs pages are needed.
Custom presets
Section titled “Custom presets”import { registerTheme, getThemePreset } from '@lh-ui/share/theme'
registerTheme({ name: 'my-brand', label: 'Brand Color', labelEn: 'Brand', description: 'The company brand primary color', vars: { '--lh-primary-color': '#722ED1', '--lh-primary-color-hover': '#9254DE', '--lh-primary-color-active': '#531DAB', '--lh-primary-color-bg': '#F9F0FF', '--lh-primary-color-border': '#D3ADF7', // Light primaries additionally need these two: // '--lh-color-on-primary': '#101410', // '--lh-selected-color': '#..., },})
getThemePreset('my-brand') // available as soon as it is registeredSemantics of registerTheme:
- Same-name override:
registerTheme({ name: 'cerblue', ... })overrides the built-in cerblue porcelain — this is intentional, letting the business side adjust the default theme without touching components. - New names are pushed to the end of the array: the order of
LH_THEME_PRESETSis “five built-ins + registered ones”. - The registry is module-level state:
importit and it is shared — one copy for the whole application.
Adding a new built-in preset
Section titled “Adding a new built-in preset”- Add a
LhThemePresetinsrc/theme/themes/index.ts(at least the five--lh-primary-*keys; a light-primary preset must additionally provide--lh-color-on-primaryand--lh-selected-color). - Add it to the
LH_THEME_PRESETSarray, and add the name to the re-exports insrc/theme/index.ts. pnpm test(theme.spechas a preset-consistency regression) +pnpm verify:theme(AA 4.5:1 gate).- Attach a changeset; each library’s docs theme-customization page will list the new preset automatically.
Note: vars only serve JS runtime consumption; the primary colors used by component SCSS still live in styles/theme/common/variables.scss — when overriding a built-in theme’s primary color, both sides must be updated in sync (theme.spec has a regression).
Naming conventions
Section titled “Naming conventions”The string value of a preset (name: 'cerblue') is what users write in ConfigProvider’s theme; changing it would break every downstream configuration — this is one of the entries in the naming exemption registry. The corresponding TS identifier still carries the prefix (LhCerblue).
- Unregistered names return
undefined:getThemePreset/getThemeVarsdo not throw; the UI layer should remember to null-check. --lh-primary-color-activeand--lh-selected-colorare two different things: the former is the background color of a pressed button, the latter is the text color of list / selected items. In light-primary presets the active color has insufficient contrast as selected text, which is why a separate--lh-selected-coloris needed.- Dark-primary presets are auto-lightened in dark mode:
buildPresetDarkVarslightens a primary when its contrast against the dark page background#1A1A2Efalls below 3 (see Theme System Overview).
Related docs
Section titled “Related docs”Copyright © 2024-2026 FengMo WenXue