From 153f3763346d28c24fb9a019703060bbb0e693d7 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Wed, 16 Sep 2026 15:42:10 -0400 Subject: [PATCH 1/3] feat(ui): add foreground text colors to Mosaic Text and Heading Add foreground and foreground-secondary color variants mapped to the --cl-color-foreground tokens, drop the redundant neutral variant, and default both components to foreground. --- .changeset/mosaic-text-foreground-colors.md | 5 +++++ .../swingset/src/stories/heading.stories.tsx | 21 +++++++++++++++---- .../src/stories/select.component.stories.tsx | 7 +------ .../swingset/src/stories/text.stories.tsx | 21 +++++++++++++++---- .../src/stories/visually-hidden.stories.tsx | 2 +- .../components/heading/heading.test.tsx | 4 ++-- .../src/mosaic/components/heading/heading.tsx | 2 +- .../src/mosaic/components/text/text.test.tsx | 16 +++++++++----- .../ui/src/mosaic/components/text/text.tsx | 2 +- .../steps/reverification-method-picker.tsx | 1 - .../ui/src/mosaic/utils/typography.styles.ts | 9 ++++---- 11 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 .changeset/mosaic-text-foreground-colors.md diff --git a/.changeset/mosaic-text-foreground-colors.md b/.changeset/mosaic-text-foreground-colors.md new file mode 100644 index 00000000000..f0c9b7ed6c2 --- /dev/null +++ b/.changeset/mosaic-text-foreground-colors.md @@ -0,0 +1,5 @@ +--- +'@clerk/ui': patch +--- + +The Mosaic `Text` and `Heading` components now accept `foreground` and `foreground-secondary` as `color` variants, mapped to the `--cl-color-foreground` and `--cl-color-foreground-secondary` tokens. The `neutral` variant has been removed in favor of `foreground`, and both components now default to `foreground` instead of `primary`. diff --git a/packages/swingset/src/stories/heading.stories.tsx b/packages/swingset/src/stories/heading.stories.tsx index 9a71ff609e9..ad3410dfbaf 100644 --- a/packages/swingset/src/stories/heading.stories.tsx +++ b/packages/swingset/src/stories/heading.stories.tsx @@ -15,11 +15,18 @@ export const meta: StoryMeta = { styles: { _variants: { size: { xs: {}, sm: {}, base: {}, lg: {}, xl: {}, '2xl': {} }, - color: { primary: {}, neutral: {}, warning: {}, negative: {}, positive: {} }, + color: { + primary: {}, + foreground: {}, + 'foreground-secondary': {}, + warning: {}, + negative: {}, + positive: {}, + }, }, _defaultVariants: { size: 'base', - color: 'primary', + color: 'foreground', }, }, }; @@ -88,9 +95,15 @@ export function Colors(props: Record) { - Neutral heading + Foreground heading + + + Foreground secondary heading
{member.name} - - {member.email} - + {member.email}
) { - Neutral text + Foreground text + + + Foreground secondary text Copy backup code - Copied {copies} times + Copied {copies} times { const heading = screen.getByRole('heading'); expect(heading).toHaveClass('cl-heading'); expect(heading).toHaveAttribute('data-size', 'base'); - expect(heading).toHaveAttribute('data-color', 'primary'); + expect(heading).toHaveAttribute('data-color', 'foreground'); }); it('wires variant props and xstyle atoms through to the element', () => { @@ -61,7 +61,7 @@ describe('Mosaic Heading', () => { it('reads defaults from HeadingContext, with own props winning', () => { render( - + Title , ); diff --git a/packages/ui/src/mosaic/components/heading/heading.tsx b/packages/ui/src/mosaic/components/heading/heading.tsx index 7c2c4a0acd6..c3c68f50dff 100644 --- a/packages/ui/src/mosaic/components/heading/heading.tsx +++ b/packages/ui/src/mosaic/components/heading/heading.tsx @@ -22,7 +22,7 @@ export const HeadingContext = React.createContext | null>( * `size` and `color` variants. Pass `render` for a different heading level. */ export const Heading = React.forwardRef(function MosaicHeading(rawProps, ref) { - const { size = 'base', color = 'primary', render, xstyle, ...rest } = useContextProps(rawProps, HeadingContext); + const { size = 'base', color = 'foreground', render, xstyle, ...rest } = useContextProps(rawProps, HeadingContext); const props = mergeStyleProps( themeProps('heading', { size, color }), diff --git a/packages/ui/src/mosaic/components/text/text.test.tsx b/packages/ui/src/mosaic/components/text/text.test.tsx index fa2d3455895..9a450d8e2e9 100644 --- a/packages/ui/src/mosaic/components/text/text.test.tsx +++ b/packages/ui/src/mosaic/components/text/text.test.tsx @@ -3,6 +3,7 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import { describe, expect, it } from 'vitest'; +import { colors } from '../../utils/typography.styles'; import { Text, TextContext } from './text'; const atoms = stylex.create({ @@ -21,14 +22,14 @@ describe('Mosaic Text', () => { const text = screen.getByText('Body copy'); expect(text).toHaveClass('cl-text'); expect(text).toHaveAttribute('data-size', 'sm'); - expect(text).toHaveAttribute('data-color', 'primary'); + expect(text).toHaveAttribute('data-color', 'foreground'); }); it('wires variant props and xstyle atoms through to the element', () => { render( Body copy @@ -36,10 +37,15 @@ describe('Mosaic Text', () => { ); const text = screen.getByText('Body copy'); expect(text).toHaveAttribute('data-size', 'lg'); - expect(text).toHaveAttribute('data-color', 'neutral'); + expect(text).toHaveAttribute('data-color', 'foreground-secondary'); expect(text).toHaveClass('cl-text', stylex.props(atoms.spaced).className ?? ''); }); + it.each(['foreground', 'foreground-secondary'] as const)('applies the %s color atoms', color => { + render(Body copy); + expect(screen.getByText('Body copy')).toHaveClass(stylex.props(colors[color]).className ?? ''); + }); + it('merges a className carried by the render element instead of clobbering the slot class', () => { render(}>Body copy); expect(screen.getByText('Body copy')).toHaveClass('cl-text', 'from-source'); @@ -64,12 +70,12 @@ describe('Mosaic Text', () => { it('reads defaults from TextContext, with own props winning', () => { render( - + Body copy , ); const text = screen.getByText('Body copy'); - expect(text).toHaveAttribute('data-color', 'neutral'); + expect(text).toHaveAttribute('data-color', 'foreground-secondary'); expect(text).toHaveAttribute('data-size', 'base'); }); diff --git a/packages/ui/src/mosaic/components/text/text.tsx b/packages/ui/src/mosaic/components/text/text.tsx index ab315f5defa..21fc105c554 100644 --- a/packages/ui/src/mosaic/components/text/text.tsx +++ b/packages/ui/src/mosaic/components/text/text.tsx @@ -21,7 +21,7 @@ export const TextContext = React.createContext | null>(null); * `size` and `color` variants. Pass `render` for inline copy (``). */ export const Text = React.forwardRef(function MosaicText(rawProps, ref) { - const { size = 'sm', color = 'primary', render, xstyle, ...rest } = useContextProps(rawProps, TextContext); + const { size = 'sm', color = 'foreground', render, xstyle, ...rest } = useContextProps(rawProps, TextContext); const props = mergeStyleProps( themeProps('text', { size, color }), diff --git a/packages/ui/src/mosaic/features/reverification/steps/reverification-method-picker.tsx b/packages/ui/src/mosaic/features/reverification/steps/reverification-method-picker.tsx index e1b29b2d794..244ccb2bb2a 100644 --- a/packages/ui/src/mosaic/features/reverification/steps/reverification-method-picker.tsx +++ b/packages/ui/src/mosaic/features/reverification/steps/reverification-method-picker.tsx @@ -99,7 +99,6 @@ export function ReverificationMethodPicker({ ) : null} {messages.helpText}{' '} diff --git a/packages/ui/src/mosaic/utils/typography.styles.ts b/packages/ui/src/mosaic/utils/typography.styles.ts index 4077e7bb1ac..aec1bf9f39c 100644 --- a/packages/ui/src/mosaic/utils/typography.styles.ts +++ b/packages/ui/src/mosaic/utils/typography.styles.ts @@ -4,7 +4,7 @@ import { colorVars, fontFamilyVars, typeScaleVars } from '../tokens.stylex'; export type TypographySize = 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl'; -export type TypographyColor = 'primary' | 'neutral' | 'warning' | 'negative' | 'positive'; +export type TypographyColor = 'primary' | 'foreground' | 'foreground-secondary' | 'warning' | 'negative' | 'positive'; export const styles = stylex.create({ base: { @@ -40,11 +40,12 @@ export const sizes = stylex.create({ }); // Text has no fill to sit on, so each color resolves to the readable token of its -// pair: the saturated one for warning/negative/positive, `-foreground` for neutral -// (`--cl-color-foreground` is a surface fill). +// pair: the saturated one for warning/negative/positive, the `-foreground` text tokens +// for the achromatic pair. export const colors = stylex.create({ primary: { color: colorVars['--cl-color-brand'] }, - neutral: { color: colorVars['--cl-color-foreground'] }, + foreground: { color: colorVars['--cl-color-foreground'] }, + 'foreground-secondary': { color: colorVars['--cl-color-foreground-secondary'] }, warning: { color: colorVars['--cl-color-warning'] }, negative: { color: colorVars['--cl-color-negative'] }, positive: { color: colorVars['--cl-color-positive'] }, From 8a9a9da89ca8c071666ce2aceae1464e44ddd2c5 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Wed, 16 Sep 2026 15:42:45 -0400 Subject: [PATCH 2/3] chore(repo): use an empty changeset --- .changeset/mosaic-text-foreground-colors.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/.changeset/mosaic-text-foreground-colors.md b/.changeset/mosaic-text-foreground-colors.md index f0c9b7ed6c2..a845151cc84 100644 --- a/.changeset/mosaic-text-foreground-colors.md +++ b/.changeset/mosaic-text-foreground-colors.md @@ -1,5 +1,2 @@ --- -'@clerk/ui': patch --- - -The Mosaic `Text` and `Heading` components now accept `foreground` and `foreground-secondary` as `color` variants, mapped to the `--cl-color-foreground` and `--cl-color-foreground-secondary` tokens. The `neutral` variant has been removed in favor of `foreground`, and both components now default to `foreground` instead of `primary`. From c44d634d08edf5aadb37f50036d9f1cfbefc98ef Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Wed, 16 Sep 2026 15:45:02 -0400 Subject: [PATCH 3/3] feat(ui): rename the primary typography color to brand --- packages/swingset/src/stories/heading.stories.tsx | 6 +++--- packages/swingset/src/stories/text.stories.tsx | 6 +++--- packages/ui/src/mosaic/utils/typography.styles.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/swingset/src/stories/heading.stories.tsx b/packages/swingset/src/stories/heading.stories.tsx index ad3410dfbaf..18b4f2d7f7f 100644 --- a/packages/swingset/src/stories/heading.stories.tsx +++ b/packages/swingset/src/stories/heading.stories.tsx @@ -16,7 +16,7 @@ export const meta: StoryMeta = { _variants: { size: { xs: {}, sm: {}, base: {}, lg: {}, xl: {}, '2xl': {} }, color: { - primary: {}, + brand: {}, foreground: {}, 'foreground-secondary': {}, warning: {}, @@ -89,9 +89,9 @@ export function Colors(props: Record) {
- Primary heading + Brand heading ) {
- Primary text + Brand text