Skip to content

Commit 267bce8

Browse files
prestonwebdevclaude
andcommitted
feat(mosaic): use 12 and 8 percent border washes and fill outline buttons with the surface
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 60c8af1 commit 267bce8

6 files changed

Lines changed: 28 additions & 35 deletions

File tree

‎.changeset/mosaic-color-adoption.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'@clerk/mosaic': patch
33
---
44

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.
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`. The two are now 12 and 8 percent washes in both modes, which makes light-mode controls firmer and leaves containers close to where they were. The profile opened in a dialog draws a border. Outline buttons fill with the surface color, and their hover and pressed states tint that fill. Banners, badges, toasts and the tag-input error use the `-subtle` tokens, and the neutral badge and banner use `--cl-color-neutral-subtle`.

‎packages/mosaic/ARCHITECTURE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const colorVars = stylex.defineVars({
2525

2626
Each exported `*Vars` const in that file is one group (`colorVars`, `radiusVars`, …); the file is the list. `space` is also declared with `defineVars`, but its keys are not `--cl-*` names, so its steps compile to hashed custom properties that are internal, not part of the contract.
2727

28-
There is no gray scale. Every achromatic token (`--cl-color-foreground`, `-border`, `-surface-subtle`, …) is `--cl-color-neutral` (black on light, white on dark) mixed into `--cl-color-surface` at a fixed percentage per mode, so overriding neutral to a warm gray retints the whole ramp. The exception is the borders: `--cl-color-border` (controls) and `--cl-color-border-subtle` (containers and dividers) are neutral washes, 7 and 4 percent in light mode and 12 and 8 percent in dark mode, so they keep the same step over the surface and the faded surface alike. The `--cl-<color>-alpha-100|200|300` tokens in `colorVars` are washes (6, 8, and 12 percent of `--cl-color-neutral`, `-negative`, `-positive`, and `-warning` over `transparent`); overriding the solid color retints its washes.
28+
There is no gray scale. Every achromatic token (`--cl-color-foreground`, `-border`, `-surface-subtle`, …) is `--cl-color-neutral` (black on light, white on dark) mixed into `--cl-color-surface` at a fixed percentage per mode, so overriding neutral to a warm gray retints the whole ramp. The exception is the borders: `--cl-color-border` (controls) and `--cl-color-border-subtle` (containers and dividers) are neutral washes, 12 and 8 percent in both modes, so they keep the same step over the surface and the faded surface alike. The `--cl-<color>-alpha-100|200|300` tokens in `colorVars` are washes (6, 8, and 12 percent of `--cl-color-neutral`, `-negative`, `-positive`, and `-warning` over `transparent`); overriding the solid color retints its washes.
2929

3030
The seeds are `--cl-color-page`, `--cl-color-neutral`, `--cl-color-brand`, `--cl-color-negative`, `--cl-color-positive` and `--cl-color-warning`. Brand and the three statuses share one shape: the seed, `-foreground` (white in light mode; in dark mode the surface color for brand, positive and warning, where the fills are light, and white for negative), `-subtle` (the 8 percent wash, 12 in dark mode) and `-border` (the 12 percent wash), so both work over the page, the surface or a faded fill and `-alpha-100|200|300` washes. Neutral has the same slots under its own names: `--cl-color-surface-subtle`, `--cl-color-neutral-subtle` (the same wash as the accent `-subtle`, for chips such as the neutral badge) and `--cl-color-border`. Only the seeds and the `-foreground` pairs are set by hand, so retinting a seed retints its companions.
3131

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

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ import { iconScope } from '../icon/icon.markers.stylex';
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.
22+
// Outline variants fill with the surface, so their hover and pressed tint mixes the neutral or
23+
// negative color into it.
2424
const primaryHover = `color-mix(in oklab, ${colorVars['--cl-color-brand']}, ${colorVars['--cl-color-brand-foreground']} 12%)`;
2525
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']}`;
26+
const outlineNeutralHover = `color-mix(in srgb, ${colorVars['--cl-color-neutral']} 6%, ${colorVars['--cl-color-surface']})`;
27+
const outlineNeutralActive = `color-mix(in srgb, ${colorVars['--cl-color-neutral']} 8%, ${colorVars['--cl-color-surface']})`;
28+
const outlineNegativeHover = `color-mix(in srgb, ${colorVars['--cl-color-negative']} 8%, ${colorVars['--cl-color-surface']})`;
29+
const outlineNegativeActive = `color-mix(in srgb, ${colorVars['--cl-color-negative']} 12%, ${colorVars['--cl-color-surface']})`;
3030
const negativeHover = `color-mix(in oklab, ${colorVars['--cl-color-negative']}, ${colorVars['--cl-color-negative-foreground']} 12%)`;
3131
const negativeActive = `color-mix(in oklab, ${colorVars['--cl-color-negative']}, ${colorVars['--cl-color-negative-foreground']} 18%)`;
3232

@@ -81,7 +81,7 @@ export const styles = stylex.create({
8181
':not([data-disabled]):active': durationVars['--cl-duration-instant'],
8282
':not([data-disabled]):hover': durationVars['--cl-duration-instant'],
8383
},
84-
transitionProperty: 'background-color, border-color, box-shadow, color, opacity, text-decoration-color',
84+
transitionProperty: 'background-color, border-color, color, opacity, text-decoration-color',
8585
// Linear, not `--cl-ease-default`: nothing here moves. An ease on already non-uniform
8686
// color interpolation just drags the midpoint, and the house curve's overshoot would
8787
// extrapolate past the target color.
@@ -187,42 +187,39 @@ export const variants = stylex.create({
187187
// fill, and leaves it independently themeable.
188188
'outline-primary': {
189189
borderColor: colorVars['--cl-color-border'],
190-
backgroundColor: colorVars['--cl-color-surface'],
191-
boxShadow: {
192-
default: null,
193-
':not([data-disabled]):active': neutralPressedWash,
194-
':not([data-disabled])[data-open]': neutralPressedWash,
190+
backgroundColor: {
191+
default: colorVars['--cl-color-surface'],
192+
':not([data-disabled]):active': outlineNeutralActive,
193+
':not([data-disabled])[data-open]': outlineNeutralActive,
195194
'@media (hover: hover)': {
196195
default: null,
197-
':not([data-disabled]):hover:not(:active):not([data-open])': neutralHoverWash,
196+
':not([data-disabled]):hover:not(:active):not([data-open])': outlineNeutralHover,
198197
},
199198
},
200199
color: colorVars['--cl-color-brand'],
201200
},
202201
'outline-neutral': {
203202
borderColor: colorVars['--cl-color-border'],
204-
backgroundColor: colorVars['--cl-color-surface'],
205-
boxShadow: {
206-
default: null,
207-
':not([data-disabled]):active': neutralPressedWash,
208-
':not([data-disabled])[data-open]': neutralPressedWash,
203+
backgroundColor: {
204+
default: colorVars['--cl-color-surface'],
205+
':not([data-disabled]):active': outlineNeutralActive,
206+
':not([data-disabled])[data-open]': outlineNeutralActive,
209207
'@media (hover: hover)': {
210208
default: null,
211-
':not([data-disabled]):hover:not(:active):not([data-open])': neutralHoverWash,
209+
':not([data-disabled]):hover:not(:active):not([data-open])': outlineNeutralHover,
212210
},
213211
},
214212
color: colorVars['--cl-color-foreground'],
215213
},
216214
'outline-negative': {
217215
borderColor: colorVars['--cl-color-border'],
218-
backgroundColor: colorVars['--cl-color-surface'],
219-
boxShadow: {
220-
default: null,
221-
':not([data-disabled]):active': negativePressedWash,
222-
':not([data-disabled])[data-open]': negativePressedWash,
216+
backgroundColor: {
217+
default: colorVars['--cl-color-surface'],
218+
':not([data-disabled]):active': outlineNegativeActive,
219+
':not([data-disabled])[data-open]': outlineNegativeActive,
223220
'@media (hover: hover)': {
224221
default: null,
225-
':not([data-disabled]):hover:not(:active):not([data-open])': negativeHoverWash,
222+
':not([data-disabled]):hover:not(:active):not([data-open])': outlineNegativeHover,
226223
},
227224
},
228225
color: colorVars['--cl-color-negative'],

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ 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-
75
export const styles = stylex.create({
86
base: {
97
borderColor: 'transparent',
@@ -22,7 +20,7 @@ export const styles = stylex.create({
2220
borderColor: colorVars['--cl-color-border-subtle'],
2321
},
2422
filled: {
25-
backgroundColor: filledWash,
23+
backgroundColor: colorVars['--cl-color-neutral-alpha-100'],
2624
},
2725
});
2826

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ export const styles = stylex.create({
104104
},
105105

106106
layoutInDialog: {
107-
borderWidth: '0px',
108107
blockSize: 'auto',
109108
boxShadow: {
110109
[compact]: 'none',
@@ -120,6 +119,7 @@ export const styles = stylex.create({
120119

121120
layoutFullscreen: {
122121
borderRadius: 0,
122+
borderWidth: '0px',
123123
boxShadow: 'none',
124124
},
125125

‎packages/mosaic/src/tokens.stylex.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ const colorDefaults = {
4747
'--cl-color-foreground-secondary': neutralMix(55, 64.25),
4848
'--cl-color-foreground-disabled': neutralMix(28.5, 44),
4949

50-
'--cl-color-border':
51-
'light-dark(color-mix(in oklab, var(--cl-color-neutral) 7%, transparent), var(--cl-color-neutral-alpha-300))',
52-
'--cl-color-border-subtle':
53-
'light-dark(color-mix(in oklab, var(--cl-color-neutral) 4%, transparent), var(--cl-color-neutral-alpha-200))',
50+
'--cl-color-border': 'var(--cl-color-neutral-alpha-300)',
51+
'--cl-color-border-subtle': 'var(--cl-color-neutral-alpha-200)',
5452

5553
'--cl-color-brand': 'light-dark(oklch(0.2046 0 0), oklch(0.9851 0 0))',
5654
'--cl-color-brand-foreground': 'light-dark(oklch(1 0 0), var(--cl-color-surface))',

0 commit comments

Comments
 (0)