Skip to content

Commit 39fa9d9

Browse files
prestonwebdevclaude
andcommitted
feat(mosaic): split control and container borders and use washes for states
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent cf438ed commit 39fa9d9

24 files changed

Lines changed: 76 additions & 63 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/mosaic': patch
3+
---
4+
5+
Controls such as inputs, selects, checkboxes, radios and outline buttons keep the stronger `--cl-color-border`, while containers and dividers (cards, sections, tables, menus, the profile nav) use `--cl-color-border-subtle`. Outline buttons fill with the surface color and lay their hover and pressed tint over it, so the tint works on the page, the surface or a faded fill. Banners, badges, toasts and the tag-input error use the `-subtle` and `-border` tokens, and the neutral badge and banner use `--cl-color-neutral-subtle`. The negative color is slightly deeper in light mode for contrast.

‎packages/mosaic/src/components/action-bar/action-bar.styles.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export const styles = stylex.create({
7676
marginBlock: space['1'],
7777
marginInline: space['0.5'],
7878
alignSelf: 'stretch',
79-
backgroundColor: colorVars['--cl-color-border'],
79+
backgroundColor: colorVars['--cl-color-border-subtle'],
8080
width: '1px',
8181
},
8282
});

‎packages/mosaic/src/components/avatar/avatar.styles.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const styles = stylex.create({
6767
},
6868

6969
icon: {
70-
borderColor: colorVars['--cl-color-border'],
70+
borderColor: colorVars['--cl-color-border-subtle'],
7171
borderRadius: radiusVars['--cl-radius-full'],
7272
borderStyle: 'solid',
7373
borderWidth: '1px',

‎packages/mosaic/src/components/badge/badge.styles.ts‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@ import * as stylex from '@stylexjs/stylex';
22

33
import { colorVars, fontFamilyVars, fontWeightVars, radiusVars, space, typeScaleVars } from '../../tokens.stylex';
44

5-
// warning/negative/positive fill with the subtle surface and use the saturated token as text;
6-
// primary fills with the solid token and uses its `-foreground` for text.
7-
//
8-
// Neutral has no subtle surface to fill — `--cl-color-foreground-secondary` is a text gray, and its
9-
// `-foreground` is a text color rather than an on-fill one, so it is unreadable against the solid
10-
// 900. It rides the same neutral scrim the button's neutral fill does.
5+
// Every color but primary fills with its `-subtle` token and uses the saturated token as text;
6+
// primary fills with the solid token and uses its `-foreground` for text. Every `-subtle` is a
7+
// wash, so the chip stays visible on the page, the surface and the faded surface alike.
118
export const styles = stylex.create({
129
base: {
1310
borderRadius: radiusVars['--cl-radius-full'],
@@ -33,7 +30,7 @@ export const colors = stylex.create({
3330
color: colorVars['--cl-color-brand-foreground'],
3431
},
3532
neutral: {
36-
backgroundColor: colorVars['--cl-color-neutral-alpha-100'],
33+
backgroundColor: colorVars['--cl-color-neutral-subtle'],
3734
color: colorVars['--cl-color-foreground'],
3835
},
3936
warning: {

‎packages/mosaic/src/components/banner/banner.styles.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const styles = stylex.create({
3939
export const rootColors = stylex.create({
4040
neutral: {
4141
borderColor: colorVars['--cl-color-border'],
42-
backgroundColor: colorVars['--cl-color-neutral-alpha-100'],
42+
backgroundColor: colorVars['--cl-color-neutral-subtle'],
4343
},
4444
warning: {
4545
borderColor: colorVars['--cl-color-warning-border'],

‎packages/mosaic/src/components/button/button.styles.ts‎

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@ import {
1313
import { iconScope } from '../icon/icon.markers.stylex';
1414

1515
// Neutral fills are the three neutral alpha steps, shared across variants:
16-
// 100 — `filled-neutral` rest, `outline-*`/`ghost-*` hover
17-
// 200 — `filled-neutral` hover, `outline-*`/`ghost-*` pressed
16+
// 100 — `filled-neutral` rest, `ghost-*` hover
17+
// 200 — `filled-neutral` hover, `ghost-*` pressed
1818
// 300 — `filled-neutral` pressed
1919

2020
// Opaque fills blend toward their own on-fill at the same 12%/18%. Neutral has no fill
2121
// to blend from, so it rides the opacity ramp above.
22+
// Outline variants keep the surface as their fill and lay the hover and pressed wash over it as an
23+
// inset shadow, so the state tint works on any backdrop and still transitions.
2224
const primaryHover = `color-mix(in oklab, ${colorVars['--cl-color-brand']}, ${colorVars['--cl-color-brand-foreground']} 12%)`;
2325
const primaryActive = `color-mix(in oklab, ${colorVars['--cl-color-brand']}, ${colorVars['--cl-color-brand-foreground']} 18%)`;
26+
const neutralHoverWash = `inset 0 0 0 100vmax ${colorVars['--cl-color-neutral-alpha-100']}`;
27+
const neutralPressedWash = `inset 0 0 0 100vmax ${colorVars['--cl-color-neutral-alpha-200']}`;
28+
const negativeHoverWash = `inset 0 0 0 100vmax ${colorVars['--cl-color-negative-alpha-200']}`;
29+
const negativePressedWash = `inset 0 0 0 100vmax ${colorVars['--cl-color-negative-alpha-300']}`;
2430
const negativeHover = `color-mix(in oklab, ${colorVars['--cl-color-negative']}, ${colorVars['--cl-color-negative-foreground']} 12%)`;
2531
const negativeActive = `color-mix(in oklab, ${colorVars['--cl-color-negative']}, ${colorVars['--cl-color-negative-foreground']} 18%)`;
2632

@@ -75,7 +81,7 @@ export const styles = stylex.create({
7581
':not([data-disabled]):active': durationVars['--cl-duration-instant'],
7682
':not([data-disabled]):hover': durationVars['--cl-duration-instant'],
7783
},
78-
transitionProperty: 'background-color, border-color, color, opacity, text-decoration-color',
84+
transitionProperty: 'background-color, border-color, box-shadow, color, opacity, text-decoration-color',
7985
// Linear, not `--cl-ease-default`: nothing here moves. An ease on already non-uniform
8086
// color interpolation just drags the midpoint, and the house curve's overshoot would
8187
// extrapolate past the target color.
@@ -181,39 +187,42 @@ export const variants = stylex.create({
181187
// fill, and leaves it independently themeable.
182188
'outline-primary': {
183189
borderColor: colorVars['--cl-color-border'],
184-
backgroundColor: {
185-
default: 'transparent',
186-
':not([data-disabled]):active': colorVars['--cl-color-neutral-alpha-200'],
187-
':not([data-disabled])[data-open]': colorVars['--cl-color-neutral-alpha-200'],
190+
backgroundColor: colorVars['--cl-color-surface'],
191+
boxShadow: {
192+
default: null,
193+
':not([data-disabled]):active': neutralPressedWash,
194+
':not([data-disabled])[data-open]': neutralPressedWash,
188195
'@media (hover: hover)': {
189196
default: null,
190-
':not([data-disabled]):hover:not(:active):not([data-open])': colorVars['--cl-color-neutral-alpha-100'],
197+
':not([data-disabled]):hover:not(:active):not([data-open])': neutralHoverWash,
191198
},
192199
},
193200
color: colorVars['--cl-color-brand'],
194201
},
195202
'outline-neutral': {
196203
borderColor: colorVars['--cl-color-border'],
197-
backgroundColor: {
198-
default: 'transparent',
199-
':not([data-disabled]):active': colorVars['--cl-color-neutral-alpha-200'],
200-
':not([data-disabled])[data-open]': colorVars['--cl-color-neutral-alpha-200'],
204+
backgroundColor: colorVars['--cl-color-surface'],
205+
boxShadow: {
206+
default: null,
207+
':not([data-disabled]):active': neutralPressedWash,
208+
':not([data-disabled])[data-open]': neutralPressedWash,
201209
'@media (hover: hover)': {
202210
default: null,
203-
':not([data-disabled]):hover:not(:active):not([data-open])': colorVars['--cl-color-neutral-alpha-100'],
211+
':not([data-disabled]):hover:not(:active):not([data-open])': neutralHoverWash,
204212
},
205213
},
206214
color: colorVars['--cl-color-foreground'],
207215
},
208216
'outline-negative': {
209217
borderColor: colorVars['--cl-color-border'],
210-
backgroundColor: {
211-
default: 'transparent',
212-
':not([data-disabled]):active': colorVars['--cl-color-neutral-alpha-200'],
213-
':not([data-disabled])[data-open]': colorVars['--cl-color-neutral-alpha-200'],
218+
backgroundColor: colorVars['--cl-color-surface'],
219+
boxShadow: {
220+
default: null,
221+
':not([data-disabled]):active': negativePressedWash,
222+
':not([data-disabled])[data-open]': negativePressedWash,
214223
'@media (hover: hover)': {
215224
default: null,
216-
':not([data-disabled]):hover:not(:active):not([data-open])': colorVars['--cl-color-neutral-alpha-100'],
225+
':not([data-disabled]):hover:not(:active):not([data-open])': negativeHoverWash,
217226
},
218227
},
219228
color: colorVars['--cl-color-negative'],
@@ -248,11 +257,11 @@ export const variants = stylex.create({
248257
'ghost-negative': {
249258
backgroundColor: {
250259
default: 'transparent',
251-
':not([data-disabled]):active': `color-mix(in oklab, ${colorVars['--cl-color-negative-subtle']}, ${colorVars['--cl-color-negative']} 8%)`,
252-
':not([data-disabled])[data-open]': `color-mix(in oklab, ${colorVars['--cl-color-negative-subtle']}, ${colorVars['--cl-color-negative']} 8%)`,
260+
':not([data-disabled]):active': colorVars['--cl-color-negative-alpha-300'],
261+
':not([data-disabled])[data-open]': colorVars['--cl-color-negative-alpha-300'],
253262
'@media (hover: hover)': {
254263
default: null,
255-
':not([data-disabled]):hover:not(:active):not([data-open])': colorVars['--cl-color-negative-subtle'],
264+
':not([data-disabled]):hover:not(:active):not([data-open])': colorVars['--cl-color-negative-alpha-200'],
256265
},
257266
},
258267
color: colorVars['--cl-color-negative'],

‎packages/mosaic/src/components/card/card.styles.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export const footer = stylex.create({
187187
flexShrink: 0,
188188
gridTemplateColumns: { [compactCard]: 'minmax(0, 1fr)', default: null },
189189
justifyContent: 'space-between',
190-
borderTopColor: colorVars['--cl-color-border'],
190+
borderTopColor: colorVars['--cl-color-border-subtle'],
191191
borderTopStyle: 'solid',
192192
borderTopWidth: {
193193
default: '0px',
@@ -202,7 +202,7 @@ export const branding = stylex.create({
202202
base: {
203203
paddingBlock: space['3'],
204204
paddingInline: space['6'],
205-
borderBlockStartColor: colorVars['--cl-color-border'],
205+
borderBlockStartColor: colorVars['--cl-color-border-subtle'],
206206
borderBlockStartStyle: 'solid',
207207
borderBlockStartWidth: '1px',
208208
display: 'flex',

‎packages/mosaic/src/components/data-list/data-list.styles.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { colorVars, fontWeightVars, radiusVars, space, typeScaleVars } from '../
44

55
export const list = stylex.create({
66
base: {
7-
borderColor: colorVars['--cl-color-border'],
7+
borderColor: colorVars['--cl-color-border-subtle'],
88
borderRadius: radiusVars['--cl-radius-lg'],
99
borderStyle: 'solid',
1010
borderWidth: '1px',
@@ -25,7 +25,7 @@ export const item = stylex.create({
2525
justifyContent: 'space-between',
2626
},
2727
divided: {
28-
borderBlockEndColor: colorVars['--cl-color-border'],
28+
borderBlockEndColor: colorVars['--cl-color-border-subtle'],
2929
borderBlockEndStyle: 'solid',
3030
borderBlockEndWidth: {
3131
default: '1px',

‎packages/mosaic/src/components/drawer/drawer.styles.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ export const styles = stylex.create({
140140
// the pointer is captured on it, so `:active` survives the drag. A press elsewhere in the sheet
141141
// is not a press on the handle, and leaves it alone.
142142
'--_cl-grip-color': {
143-
default: colorVars['--cl-color-border'],
144-
':active': `color-mix(in oklab, ${colorVars['--cl-color-border']}, ${colorVars['--cl-color-foreground-secondary']} 35%)`,
143+
default: colorVars['--cl-color-border-subtle'],
144+
':active': `color-mix(in oklab, ${colorVars['--cl-color-border-subtle']}, ${colorVars['--cl-color-foreground-secondary']} 35%)`,
145145
},
146146
placeItems: 'center',
147147
display: 'grid',

‎packages/mosaic/src/components/icon/icon-frame.styles.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import * as stylex from '@stylexjs/stylex';
22

33
import { colorVars, radiusVars, space } from '../../tokens.stylex';
44

5+
const filledWash = `light-dark(color-mix(in oklab, ${colorVars['--cl-color-neutral']} 3.5%, transparent), color-mix(in oklab, ${colorVars['--cl-color-neutral']} 4.5%, transparent))`;
6+
57
export const styles = stylex.create({
68
base: {
79
borderColor: 'transparent',
@@ -17,10 +19,10 @@ export const styles = stylex.create({
1719
justifyContent: 'center',
1820
},
1921
bordered: {
20-
borderColor: colorVars['--cl-color-border'],
22+
borderColor: colorVars['--cl-color-border-subtle'],
2123
},
2224
filled: {
23-
backgroundColor: colorVars['--cl-color-neutral-alpha-100'],
25+
backgroundColor: filledWash,
2426
},
2527
});
2628

0 commit comments

Comments
 (0)