Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mosaic-concentric-radii.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/mosaic': patch
---

Align the corners of `Select`, `Combobox`, `Menu` and `PhoneInput` options and `ActionBar` buttons with their surrounding surface. These radii now follow `--cl-radius-lg` and `--cl-spacing` rather than `--cl-radius-md`, so an override of `--cl-radius-md` no longer changes them.
9 changes: 9 additions & 0 deletions packages/mosaic/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ Stop and check the table if you are writing any of these in a feature:
- `setTimeout` to debounce or to delay a spinner
- `stylex.create` inside a `.tsx` file

## Nested radii

A part inset from a rounded surface gets its radius from that surface: `max(0px, calc(outer - border - padding))`. Do not pick a fixed step, or it stops matching when `--cl-radius-*` or `--cl-spacing` is overridden. This applies to small insets. When the inset is about as large as the outer radius or larger (Card content, TagInput tags), the part keeps its own radius.

- **Same component:** declare the outer radius, border and padding as local consts, and use them in both the surface and the calc, so changing any of them updates the inner radius (`components/select/select.styles.ts`).
- **Different component:** the parent computes the radius from the same consts and sets a `--_cl-*-radius` var, and the child reads it with a fallback (`--_cl-button-radius` in `components/action-bar/action-bar.styles.ts` and `components/button/button.styles.ts`).
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

Do not add a shared `concentric()` helper or a global surface-radius var. A global var inherits into nested surfaces and gives them the wrong value.

## Readability before complexity scores

Do not extract a helper or introduce a machine only to reduce a lint score.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
} from '../../tokens.stylex';

const reduceMotion = '@media (prefers-reduced-motion: reduce)';
const barRadius = radiusVars['--cl-radius-lg'];
const inset = space['1'];

export const styles = stylex.create({
positioner: {
Expand All @@ -25,10 +27,11 @@ export const styles = stylex.create({
'--_cl-icon-color': colorVars['--cl-color-negative'],
},
bar: {
borderRadius: radiusVars['--cl-radius-lg'],
'--_cl-button-radius': `max(0px, calc(${barRadius} - ${inset}))`,
borderRadius: barRadius,
gap: space['1'],
paddingBlock: space['1'],
paddingInline: space['1'],
paddingBlock: inset,
paddingInline: inset,
alignItems: 'center',
backgroundColor: colorVars['--cl-color-surface'],
blockSize: space['10'],
Expand Down
12 changes: 8 additions & 4 deletions packages/mosaic/src/components/combobox/combobox.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ import {
typeScaleVars,
} from '../../tokens.stylex';

const popupRadius = radiusVars['--cl-radius-lg'];
const inset = space['1'];
const optionRadius = `max(0px, calc(${popupRadius} - ${inset}))`;

export const styles = stylex.create({
positioner: {
outline: 'none',
},
popup: {
borderRadius: radiusVars['--cl-radius-lg'],
borderRadius: popupRadius,
outline: 'none',
overflow: 'hidden',
backgroundColor: colorVars['--cl-color-surface'],
Expand Down Expand Up @@ -53,21 +57,21 @@ export const styles = stylex.create({
minWidth: '12.5rem',
},
viewport: {
padding: space['1'],
padding: inset,
gap: space['0.5'],
display: 'flex',
flexDirection: 'column',
maxHeight: '16rem',
},
list: {
padding: space['1'],
padding: inset,
gap: space['0.5'],
display: 'flex',
flexDirection: 'column',
maxHeight: '16rem',
},
option: {
borderRadius: radiusVars['--cl-radius-md'],
borderRadius: optionRadius,
gap: space['2'],
outline: {
default: 'none',
Expand Down
12 changes: 8 additions & 4 deletions packages/mosaic/src/components/menu/menu.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
typeScaleVars,
} from '../../tokens.stylex';

const popupRadius = radiusVars['--cl-radius-lg'];
const inset = space['0.5'];
const itemRadius = `max(0px, calc(${popupRadius} - ${inset}))`;

// Positioning is applied inline by the headless positioner; this only clears the
// focus outline it receives. No z-index: the portalled, fixed positioner already
// paints above page content, and consumers own their own stacking order.
Expand All @@ -23,7 +27,7 @@ export const positioner = stylex.create({

export const popup = stylex.create({
base: {
borderRadius: radiusVars['--cl-radius-lg'],
borderRadius: popupRadius,
outline: 'none',
backgroundColor: colorVars['--cl-color-surface'],
boxShadow: shadowVars['--cl-shadow-md'],
Expand Down Expand Up @@ -73,7 +77,7 @@ export const viewport = stylex.create({
// The inset belongs to the scrolling box, not the popup: `Menu.Separator` bleeds through it
// with a negative margin, and from inside a viewport that clips its inline axis a bleed past
// the popup's own padding would be cut off instead.
padding: space['0.5'],
padding: inset,
gap: space['0.5'],
display: 'flex',
flexDirection: 'column',
Expand All @@ -82,7 +86,7 @@ export const viewport = stylex.create({

export const item = stylex.create({
base: {
borderRadius: '0.375rem',
borderRadius: itemRadius,
borderStyle: 'none',
gap: space['0.5'],
outline: 'none',
Expand Down Expand Up @@ -175,7 +179,7 @@ export const separator = stylex.create({
base: {
// Full-bleed across the popup: cancel the viewport's inline padding.
marginBlock: space['0.5'],
marginInline: `calc(-1 * ${space['0.5']})`,
marginInline: `calc(-1 * ${inset})`,
backgroundColor: colorVars['--cl-color-border'],
blockSize: '1px',
flexShrink: 0,
Expand Down
11 changes: 7 additions & 4 deletions packages/mosaic/src/components/phone-input/phone-input.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
typeScaleVars,
} from '../../tokens.stylex';

const popupRadius = radiusVars['--cl-radius-lg'];
const listInset = space['1'];
const optionRadius = `max(0px, calc(${popupRadius} - ${listInset}))`;

export const styles = stylex.create({
triggerStart: {
paddingInlineStart: 'var(--_cl-input-group-inset)',
Expand Down Expand Up @@ -64,7 +68,7 @@ export const styles = stylex.create({
paddingInlineStart: space['1.5'],
},
popup: {
borderRadius: radiusVars['--cl-radius-lg'],
borderRadius: popupRadius,
overflow: 'hidden',
backgroundColor: colorVars['--cl-color-surface'],
boxShadow: shadowVars['--cl-shadow-md'],
Expand Down Expand Up @@ -114,15 +118,14 @@ export const styles = stylex.create({
},
},
countryList: {
padding: listInset,
gap: space['0.5'],
paddingBlock: space['0.5'],
paddingInline: space['1'],
display: 'flex',
flexDirection: 'column',
maxHeight: '16rem',
},
option: {
borderRadius: radiusVars['--cl-radius-md'],
borderRadius: optionRadius,
gap: space['1'],
outline: {
default: 'none',
Expand Down
13 changes: 9 additions & 4 deletions packages/mosaic/src/components/select/select.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import {
} from '../../tokens.stylex';
import { selectOptionScope } from './select.markers.stylex';

const popupRadius = radiusVars['--cl-radius-lg'];
const popupBorder = '1px';
const inset = space['0.5'];
const optionRadius = `max(0px, calc(${popupRadius} - ${popupBorder} - ${inset}))`;

// With `alignItemWithTrigger` the selected option's box lands over the trigger's, so the two
// boxes must inset their text identically: the trigger is a `md` Button (32px, 1px border, 12px
// inline padding, sm text), and the option gets the same 13px from the popup's 1px border, the
Expand All @@ -36,9 +41,9 @@ export const popup = stylex.create({
base: {
// Transparent, not removed: it holds the 1px of layout the alignment counts on, and forced colors paints it.
borderColor: 'transparent',
borderRadius: radiusVars['--cl-radius-lg'],
borderRadius: popupRadius,
borderStyle: 'solid',
borderWidth: '1px',
borderWidth: popupBorder,
outline: 'none',
backgroundColor: colorVars['--cl-color-surface'],
boxShadow: shadowVars['--cl-shadow-md'],
Expand Down Expand Up @@ -77,7 +82,7 @@ export const popup = stylex.create({

export const viewport = stylex.create({
base: {
padding: space['0.5'],
padding: inset,
gap: space['0.5'],
display: 'flex',
flexDirection: 'column',
Expand All @@ -86,7 +91,7 @@ export const viewport = stylex.create({

export const option = stylex.create({
base: {
borderRadius: radiusVars['--cl-radius-md'],
borderRadius: optionRadius,
borderStyle: 'none',
gap: space['2'],
outline: 'none',
Expand Down
Loading