Skip to content
Lenso UI

v3.0.5

Text renamed to Typography (breaking), color tokens refactored to unprefixed source variables, Checkbox + Radio border fixes, and a new CLI docs page.

May 15, 2026

Patch release: Text is renamed to Typography to resolve a tailwind-merge collision that silently dropped variant classes. Derived color tokens (hover, soft, border-secondary, …) moved from @theme inline aliases into variables.css as unprefixed source tokens, so component CSS can reference them directly. Plus Checkbox and Radio border alignment fixes, an accent-soft-foreground hover refinement for Calendar, and a new CLI docs page.

⚠️ Breaking change: Text → Typography. The BEM block changes from text to typography (e.g. text--body-sm → typography--body-sm).

Installation

Update to the latest version:

npm i @lenso/tokens@latest @lenso/ui@latest

Using AI assistants? Simply prompt "Hey Cursor, update HeroUI to the latest version" and your AI assistant will automatically compare versions and apply the necessary changes. Learn more about the HeroUI MCP Server.

⚠️ Breaking Changes

Text → Typography

The component shipped in v3.0.4 used text-* BEM modifiers, which collide with Tailwind's text-* utility family. tailwind-variants runs tailwind-merge on every variant composition and dedupes text--body-sm, text--color-muted, text--weight-normal to a single class — silently dropping the others.

Renaming the block to typography permanently fixes the collision (#6505, closes #6497).

Before:

import {Text} from "@lenso/ui";
<Text type="body-sm" color="muted" weight="medium">  Hello world</Text>;

After:

import {Typography} from "@lenso/ui";
<Typography type="body-sm" color="muted" weight="medium">  Hello world</Typography>;

Sub-components rename the same way:

BeforeAfter
TextTypography
Text.HeadingTypography.Heading
Text.ParagraphTypography.Paragraph
Text.CodeTypography.Code
Text.ProseTypography.Prose

CSS BEM classes change too: .text → .typography, .text--body-sm → .typography--body-sm, etc. See the Typography docs for the full class reference.

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Typography } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const scale = [  { label: "h1", meta: "36px / 600 / 1.11 / tight", sample: "Build better interfaces", type: "h1" },  {    label: "h2",    meta: "30px / 600 / 1.17 / tight",    sample: "Built for the intelligence age",    type: "h2",  },  { label: "h3", meta: "24px / 600 / 1.25 / tight", sample: "Pricing on your terms", type: "h3" },  {    label: "h4",    meta: "20px / 600 / 1.33 / tight",    sample: "Apply to the startup program",    type: "h4",  },  { label: "h5", meta: "18px / 600 / 1.39 / tight", sample: "Card titles", type: "h5" },  { label: "h6", meta: "16px / 600 / 1.50 / tight", sample: "Smaller feature headers", type: "h6" },  {    label: "body",    meta: "16px / 400 / 1.75",    sample: "Primary body text used across documentation, marketing copy, and descriptions.",    type: "body",  },  {    label: "body-sm",    meta: "14px / 400 / 1.50",    sample: "Secondary body, table cells, navigation, and sidebar items.",    type: "body-sm",  },  {    label: "body-xs",    meta: "12px / 400 / 1.25",    sample: "Captions, badges, helper text, and fine print.",    type: "body-xs",  },  { label: "code", meta: "14px / mono", sample: "pnpm add @lenso/ui", type: "code" },] as const;const styles = stylex.create({  column: { display: "flex", width: "100%", flexDirection: "column" },  row: {    display: "grid",    gridTemplateColumns: "160px 1fr",    alignItems: "center",    gap: 32,    paddingBlock: 20,    borderBlockEndWidth: { default: 1, ":last-child": 0 },    borderBlockEndStyle: "solid",    borderBlockEndColor: "var(--border)",  },  caption: { display: "flex", flexShrink: 0, flexDirection: "column", gap: 2 },  label: { fontSize: 14, lineHeight: "20px", fontWeight: 600, color: "var(--foreground)" },  meta: { fontSize: 12, lineHeight: "16px", whiteSpace: "nowrap", color: "var(--muted)" },});export function TypographyScale() {  return (    <div {...stylex.props(styles.column)}>      {scale.map((row) => (        <div key={row.label} {...stylex.props(styles.row)}>          <div {...stylex.props(styles.caption)}>            <span {...stylex.props(styles.label)}>{row.label}</span>            <span {...stylex.props(styles.meta)}>{row.meta}</span>          </div>          <Typography type={row.type}>{row.sample}</Typography>        </div>      ))}    </div>  );}export default TypographyScale;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Style Refactor

Unprefixed source color tokens

All derived color tokens — *-hover, *-soft, *-soft-foreground, border-secondary, etc. — moved from @theme inline aliases in theme.css into variables.css as unprefixed source variables, and 24 component CSS files now reference source tokens directly (#6499).

1. color-mix formulas moved from theme.css to variables.css

Before — the calculation lived inside the @theme inline block:

/* packages/styles/themes/shared/theme.css */--color-accent-hover: color-mix(in oklab, var(--accent) 90%, var(--accent-foreground) 10%);--color-accent-soft: var(--accent-soft, color-mix(in oklab, var(--accent) 15%, transparent));--color-accent-soft-foreground: var(--accent-soft-foreground, var(--accent));--color-border-secondary: color-mix(in oklab, var(--surface) 78%, var(--surface-foreground) 22%);

After — theme.css is pure aliases; the formulas live in variables.css:

/* packages/styles/themes/default/variables.css */--accent-hover: color-mix(in oklab, var(--accent) 90%, var(--accent-foreground) 10%);--accent-soft: color-mix(in oklab, var(--accent) 15%, transparent);--accent-soft-foreground: var(--accent);--border-secondary: color-mix(in oklab, var(--surface) 78%, var(--surface-foreground) 22%);
/* packages/styles/themes/shared/theme.css */--color-accent-hover: var(--accent-hover);--color-accent-soft: var(--accent-soft);--color-accent-soft-foreground: var(--accent-soft-foreground);--color-border-secondary: var(--border-secondary);

2. Component CSS uses source tokens directly

Before — var(--color-*) references everywhere:

/* packages/styles/components/button.css */.button--primary {  --button-bg: var(--color-accent);  --button-bg-hover: var(--color-accent-hover);  --button-fg: var(--color-accent-foreground);}
.button--secondary {  --button-bg: var(--color-default);  --button-bg-hover: var(--color-default-hover);  --button-fg: var(--color-accent-soft-foreground);}

After — unprefixed source tokens:

/* packages/styles/components/button.css */.button--primary {  --button-bg: var(--accent);  --button-bg-hover: var(--accent-hover);  --button-fg: var(--accent-foreground);}
.button--secondary {  --button-bg: var(--default);  --button-bg-hover: var(--default-hover);  --button-fg: var(--accent-soft-foreground);}

The @theme inline block still exposes every --color-* Tailwind utility as an alias, so user-land class usage (bg-accent, text-accent, …) is unaffected.

3. Theme builder consolidated

Three derived-token helpers (getAccentDerivedVariables, getSemanticDerivedVariables, getFieldDerivedVariables) collapsed into a single getDerivedColorVariables() that emits the full unprefixed set, including the darkenForSoftForeground logic that keeps soft-foreground text readable on light accent themes.

Style Fixes

  • Calendar / Range Calendar: default day-cell hover now uses accent-soft-foreground instead of accent, so hovered cells stay legible on light accent themes. The docs search tag selected state was aligned the same way (#6500).
  • Checkbox: .checkbox__control now carries the same border, border-field-border, and [border-width:var(--border-width-field)] base styling that .radio__control already used, with border-color added to the transition list (#6521).
  • Radio: .radio__control default border now uses border-field-border instead of the generic Tailwind border color, matching Input, Select, TextArea, and NumberField (#6522).

Docs

CLI page

New CLI docs page covers installation, init, install, upgrade, uninstall, list, doctor, and env with example outputs (#6498).

Dependencies

Bumped across packages (#6529):

  • react / react-dom: 19.2.3 → 19.2.6
  • @types/react: 19.2.7 → 19.2.14
  • next (docs): 16.1.1 → 16.2.6
  • CI actions: actions/checkout@v6, actions/setup-node@v6, actions/cache@v5, pnpm/action-setup@v6

Contributors

Thanks to everyone who contributed to this release!

HeroUI contributors