@@ -138,24 +138,6 @@ const button = style<ButtonRenderProps & ButtonStyleProps & {isStaticColor: bool
138
138
isDisabled : 'GrayText'
139
139
}
140
140
} ,
141
- backgroundImage : {
142
- variant : {
143
- premium : {
144
- default : linearGradient ( '96deg' , [ 'fuchsia-900' , 0 ] , [ 'indigo-900' , 66 ] , [ 'blue-900' , 100 ] ) ,
145
- isHovered : linearGradient ( '96deg' , [ 'fuchsia-1000' , 0 ] , [ 'indigo-1000' , 66 ] , [ 'blue-1000' , 100 ] ) ,
146
- isPressed : linearGradient ( '96deg' , [ 'fuchsia-1000' , 0 ] , [ 'indigo-1000' , 66 ] , [ 'blue-1000' , 100 ] ) ,
147
- isFocusVisible : linearGradient ( '96deg' , [ 'fuchsia-1000' , 0 ] , [ 'indigo-1000' , 66 ] , [ 'blue-1000' , 100 ] )
148
- } ,
149
- genai : {
150
- default : linearGradient ( '96deg' , [ 'red-900' , 0 ] , [ 'magenta-900' , 33 ] , [ 'indigo-900' , 100 ] ) ,
151
- isHovered : linearGradient ( '96deg' , [ 'red-1000' , 0 ] , [ 'magenta-1000' , 33 ] , [ 'indigo-1000' , 100 ] ) ,
152
- isPressed : linearGradient ( '96deg' , [ 'red-1000' , 0 ] , [ 'magenta-1000' , 33 ] , [ 'indigo-1000' , 100 ] ) ,
153
- isFocusVisible : linearGradient ( '96deg' , [ 'red-1000' , 0 ] , [ 'magenta-1000' , 33 ] , [ 'indigo-1000' , 100 ] )
154
- }
155
- } ,
156
- isDisabled : 'none' ,
157
- forcedColors : 'none'
158
- } ,
159
141
backgroundColor : {
160
142
fillStyle : {
161
143
fill : {
@@ -296,6 +278,42 @@ const button = style<ButtonRenderProps & ButtonStyleProps & {isStaticColor: bool
296
278
disableTapHighlight : true
297
279
} , getAllowedOverrides ( ) ) ;
298
280
281
+ // Put the gradient background on a separate element from the button to work around a Safari
282
+ // bug where transitions of custom properties cause layout flickering if any properties use rems. 🤣
283
+ // https://bugs.webkit.org/show_bug.cgi?id=285622
284
+ const gradient = style ( {
285
+ position : 'absolute' ,
286
+ inset : 0 ,
287
+ zIndex : - 1 ,
288
+ transition : 'default' ,
289
+ borderRadius : '[inherit]' ,
290
+ backgroundImage : {
291
+ variant : {
292
+ premium : {
293
+ default : linearGradient ( 'to bottom right' , [ 'fuchsia-900' , 0 ] , [ 'indigo-900' , 66 ] , [ 'blue-900' , 100 ] ) ,
294
+ isHovered : linearGradient ( 'to bottom right' , [ 'fuchsia-1000' , 0 ] , [ 'indigo-1000' , 66 ] , [ 'blue-1000' , 100 ] ) ,
295
+ isPressed : linearGradient ( 'to bottom right' , [ 'fuchsia-1000' , 0 ] , [ 'indigo-1000' , 66 ] , [ 'blue-1000' , 100 ] ) ,
296
+ isFocusVisible : linearGradient ( 'to bottom right' , [ 'fuchsia-1000' , 0 ] , [ 'indigo-1000' , 66 ] , [ 'blue-1000' , 100 ] )
297
+ } ,
298
+ genai : {
299
+ default : linearGradient ( 'to bottom right' , [ 'red-900' , 0 ] , [ 'magenta-900' , 33 ] , [ 'indigo-900' , 100 ] ) ,
300
+ isHovered : linearGradient ( 'to bottom right' , [ 'red-1000' , 0 ] , [ 'magenta-1000' , 33 ] , [ 'indigo-1000' , 100 ] ) ,
301
+ isPressed : linearGradient ( 'to bottom right' , [ 'red-1000' , 0 ] , [ 'magenta-1000' , 33 ] , [ 'indigo-1000' , 100 ] ) ,
302
+ isFocusVisible : linearGradient ( 'to bottom right' , [ 'red-1000' , 0 ] , [ 'magenta-1000' , 33 ] , [ 'indigo-1000' , 100 ] )
303
+ }
304
+ } ,
305
+ isDisabled : 'none' ,
306
+ forcedColors : 'none'
307
+ } ,
308
+ // Force gradient colors to remain static between light and dark theme.
309
+ colorScheme : {
310
+ variant : {
311
+ premium : 'light' ,
312
+ genai : 'light'
313
+ }
314
+ }
315
+ } ) ;
316
+
299
317
/**
300
318
* Buttons allow users to perform an action.
301
319
* They have multiple styles for various needs, and are ideal for calling attention to
@@ -350,65 +368,79 @@ export const Button = forwardRef(function Button(props: ButtonProps, ref: Focusa
350
368
staticColor,
351
369
isStaticColor : ! ! staticColor
352
370
} , props . styles ) } >
353
- < Provider
354
- values = { [
355
- [ SkeletonContext , null ] ,
356
- [ TextContext , {
357
- styles : style ( {
358
- paddingY : '--labelPadding' ,
359
- order : 1 ,
360
- opacity : {
361
- default : 1 ,
362
- isProgressVisible : 0
363
- }
364
- } ) ( { isProgressVisible} ) ,
365
- // @ts -ignore data-attributes allowed on all JSX elements, but adding to DOMProps has been problematic in the past
366
- 'data-rsp-slot' : 'text'
367
- } ] ,
368
- [ IconContext , {
369
- render : centerBaseline ( { slot : 'icon' , styles : style ( { order : 0 } ) } ) ,
370
- styles : style ( {
371
- size : fontRelative ( 20 ) ,
372
- marginStart : '--iconMargin' ,
373
- flexShrink : 0 ,
374
- opacity : {
375
- default : 1 ,
376
- isProgressVisible : 0
377
- }
378
- } ) ( { isProgressVisible} )
379
- } ]
380
- ] } >
381
- { typeof props . children === 'string' ? < Text > { props . children } </ Text > : props . children }
382
- { isPending &&
383
- < div
384
- className = { style ( {
385
- position : 'absolute' ,
386
- top : '[50%]' ,
387
- left : '[50%]' ,
388
- transform : 'translate(-50%, -50%)' ,
389
- opacity : {
390
- default : 0 ,
391
- isProgressVisible : 1
392
- }
393
- } ) ( { isProgressVisible, isPending} ) } >
394
- < ProgressCircle
395
- isIndeterminate
396
- aria-label = { stringFormatter . format ( 'button.pending' ) }
397
- size = "S"
398
- staticColor = { staticColor }
399
- styles = { style ( {
400
- size : {
371
+ { ( renderProps ) => ( < >
372
+ { variant === 'genai' || variant === 'premium'
373
+ ? (
374
+ < span
375
+ className = { gradient ( {
376
+ ...renderProps ,
377
+ // Retain hover styles when an overlay is open.
378
+ isHovered : renderProps . isHovered || overlayTriggerState ?. isOpen || false ,
379
+ isDisabled : renderProps . isDisabled || isProgressVisible ,
380
+ variant
381
+ } ) } />
382
+ )
383
+ : null }
384
+ < Provider
385
+ values = { [
386
+ [ SkeletonContext , null ] ,
387
+ [ TextContext , {
388
+ styles : style ( {
389
+ paddingY : '--labelPadding' ,
390
+ order : 1 ,
391
+ opacity : {
392
+ default : 1 ,
393
+ isProgressVisible : 0
394
+ }
395
+ } ) ( { isProgressVisible} ) ,
396
+ // @ts -ignore data-attributes allowed on all JSX elements, but adding to DOMProps has been problematic in the past
397
+ 'data-rsp-slot' : 'text'
398
+ } ] ,
399
+ [ IconContext , {
400
+ render : centerBaseline ( { slot : 'icon' , styles : style ( { order : 0 } ) } ) ,
401
+ styles : style ( {
402
+ size : fontRelative ( 20 ) ,
403
+ marginStart : '--iconMargin' ,
404
+ flexShrink : 0 ,
405
+ opacity : {
406
+ default : 1 ,
407
+ isProgressVisible : 0
408
+ }
409
+ } ) ( { isProgressVisible} )
410
+ } ]
411
+ ] } >
412
+ { typeof props . children === 'string' ? < Text > { props . children } </ Text > : props . children }
413
+ { isPending &&
414
+ < div
415
+ className = { style ( {
416
+ position : 'absolute' ,
417
+ top : '[50%]' ,
418
+ left : '[50%]' ,
419
+ transform : 'translate(-50%, -50%)' ,
420
+ opacity : {
421
+ default : 0 ,
422
+ isProgressVisible : 1
423
+ }
424
+ } ) ( { isProgressVisible, isPending} ) } >
425
+ < ProgressCircle
426
+ isIndeterminate
427
+ aria-label = { stringFormatter . format ( 'button.pending' ) }
428
+ size = "S"
429
+ staticColor = { staticColor }
430
+ styles = { style ( {
401
431
size : {
402
- S : 14 ,
403
- M : 18 ,
404
- L : 20 ,
405
- XL : 24
432
+ size : {
433
+ S : 14 ,
434
+ M : 18 ,
435
+ L : 20 ,
436
+ XL : 24
437
+ }
406
438
}
407
- }
408
- } ) ( { size } ) } / >
409
- </ div >
410
- }
411
- </ Provider >
439
+ } ) ( { size } ) } />
440
+ </ div >
441
+ }
442
+ </ Provider >
443
+ </ > ) }
412
444
</ RACButton >
413
445
) ;
414
446
} ) ;
0 commit comments