From 54f26ac17e2a7d579539ae11adcaa1912faeef2e Mon Sep 17 00:00:00 2001
From: Josh <44883293+josh-bagwell@users.noreply.github.com>
Date: Wed, 17 Apr 2024 15:25:27 -0600
Subject: [PATCH] chore(button)!: Refactor button to use system tokens (#2666)
Fixes: #2402
- Refactor Button styles to use `system` tokens and remove `base` tokens.
- Refactor Button styles to utilize stencils
[category:Components]
Release Note:
Updated Buttons to use new `system` tokens.
### BREAKING CHANGES
**Visual Breaking Change**
- There may be a small visual change to `SecondaryButton` when the `border` for the `focus` state was updated from `soap400` to `transparent`.
---
modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx | 86 ++--
modules/preview-react/pill/lib/Pill.tsx | 84 ++--
.../preview-react/pill/lib/PillIconButton.tsx | 32 +-
modules/react/button/lib/BaseButton.tsx | 464 ++++++++++--------
modules/react/button/lib/Button.tsx | 5 +-
modules/react/button/lib/DeleteButton.tsx | 80 +--
modules/react/button/lib/PrimaryButton.tsx | 145 +++---
modules/react/button/lib/SecondaryButton.tsx | 172 ++++---
modules/react/button/lib/TertiaryButton.tsx | 346 ++++++++-----
.../visual-testing/stories_ColorsOverride.tsx | 75 +++
package.json | 6 +-
11 files changed, 865 insertions(+), 630 deletions(-)
create mode 100644 modules/react/button/stories/visual-testing/stories_ColorsOverride.tsx
diff --git a/modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx b/modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx
index 196148c262..ef54b99e5e 100644
--- a/modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx
+++ b/modules/docs/mdx/11.0-UPGRADE-GUIDE.mdx
@@ -20,8 +20,8 @@ any questions.
- [Text Area (Preview)](#text-area-preview)
- [Text Input (Preview)](#text-input-preview)
- [Component Style Updates](#component-style-updates)
-- [Component Token Updates](#component-token-updates)
- [Component Updates](#component-updates)
+ - [Buttons](#buttons)
- [Card](#card)
- [Checkbox](#checkbox)
- [Combobox](#combobox)
@@ -243,14 +243,6 @@ See below:
-
- Table (Header)
-
-
- borderColor
- soap400
- soap500
-
Checkbox
@@ -270,6 +262,22 @@ See below:
licorice100
+
+
+ DeleteButton
+
+ disabled
+ backgroundColor
+ error.light
+ error.base
+
+
+ DeleteButton
+
+ disabled
+ opacity
+ 1
+ 0.4
Radio
@@ -285,7 +293,19 @@ See below:
SecondaryButton
Inverse
focus
- color
+ border
+
+ soap400
+
+
+ transparent
+
+
+
+ SecondaryButton
+ Inverse
+ focus
+ boxShdaow (Inner Color)
blackPepper500
@@ -297,7 +317,7 @@ See below:
SecondaryButton
Inverse
focus
- boxShdaow (Inner Color)
+ color
blackPepper500
@@ -317,6 +337,14 @@ See below:
blackPepper400
+
+ Table (Header)
+
+
+ borderColor
+ soap400
+ soap500
+
TertiaryButton
@@ -378,23 +406,7 @@ See below:
- DeleteButton
-
- disabled
- backgroundColor
- error.light
- error.base
-
-
- DeleteButton
-
- disabled
- opacity
- 1
- 0.4
-
-
- TextInput
+ TextArea
disabled
borderColor
@@ -404,7 +416,7 @@ See below:
- TextArea
+ TextInput
disabled
borderColor
@@ -416,13 +428,19 @@ See below:
-### Component Token Updates
+## Component Updates
-The following components have been updated to use our [new system tokens](insert link).
+### Buttons
-- Text
+**PR:** [#2666](https://github.com/Workday/canvas-kit/pull/2666)
-## Component Updates
+`PrimaryButton`, `SecondaryButton`, `TertiaryButton` and `DeleteButton` have been refactored to use our
+[new styling utilities](https://workday.github.io/canvas-kit/?path=/docs/styling-basics--create-modifiers#createstyles-api)
+and [tokens](https://workday.github.io/canvas-tokens/?path=/docs/docs-getting-started--docs). The
+components now support the `cs` prop, but otherwise the API has not changed.
+
+> #### Visual Breaking Change
+> The `border` color for `SecondaryButton` on the `focus` state is now `transparent` and may cause a small visual difference in visual tests.
### Card
@@ -811,3 +829,5 @@ experimental code and is analagous to code in alpha.
Breaking changes can be deployed to Labs at any time without triggering a major version update and
may not be subject to the same rigor in communcation and migration strategies reserved for breaking
changes in [Preview](#preview) and [Main](#main).
+import { opacity } from "@workday/canvas-tokens-web/dist/es6/system"
+
diff --git a/modules/preview-react/pill/lib/Pill.tsx b/modules/preview-react/pill/lib/Pill.tsx
index a6b7c3d574..7d7744c68c 100644
--- a/modules/preview-react/pill/lib/Pill.tsx
+++ b/modules/preview-react/pill/lib/Pill.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {CSSObject} from '@emotion/react';
-import {BaseButton, buttonVars} from '@workday/canvas-kit-react/button';
+import {BaseButton, buttonColorPropVars} from '@workday/canvas-kit-react/button';
import {
createContainer,
focusRing,
@@ -11,7 +11,7 @@ import {
} from '@workday/canvas-kit-react/common';
import {BoxProps, boxStyleFn, Flex} from '@workday/canvas-kit-react/layout';
import {borderRadius, colors, space, type} from '@workday/canvas-kit-react/tokens';
-import {handleCsProp, CSProps} from '@workday/canvas-kit-styling';
+import {handleCsProp, CSProps, px2rem} from '@workday/canvas-kit-styling';
import {usePillModel} from './usePillModel';
@@ -65,50 +65,50 @@ const StyledBasePill = styled(BaseButton.as('button'))(
},
},
}),
- [buttonVars.default.background]: colors.soap300,
- [buttonVars.default.border]: colors.licorice200,
- [buttonVars.default.label]: colors.blackPepper400,
+ [buttonColorPropVars.default.background]: colors.soap300,
+ [buttonColorPropVars.default.border]: colors.licorice200,
+ [buttonColorPropVars.default.label]: colors.blackPepper400,
[systemIconStencil.vars.color]: colors.licorice200,
button: {
[systemIconStencil.vars.color]: colors.licorice200,
},
'&:focus-visible, &.focus': {
- [buttonVars.focus.background]: colors.soap300,
- [buttonVars.focus.border]: colors.blueberry400,
- [buttonVars.focus.label]: colors.blackPepper400,
+ [buttonColorPropVars.focus.background]: colors.soap300,
+ [buttonColorPropVars.focus.border]: colors.blueberry400,
+ [buttonColorPropVars.focus.label]: colors.blackPepper400,
[systemIconStencil.vars.color]: colors.licorice500,
button: {
[systemIconStencil.vars.color]: colors.licorice500,
},
'span[data-count="ck-pill-count"]': {
- borderTop: `1px solid ${colors.blueberry400}`,
- borderBottom: `1px solid ${colors.blueberry400}`,
- borderRight: `1px solid ${colors.blueberry400}`,
+ borderTop: `${px2rem(1)} solid ${colors.blueberry400}`,
+ borderBottom: `${px2rem(1)} solid ${colors.blueberry400}`,
+ borderRight: `${px2rem(1)} solid ${colors.blueberry400}`,
},
},
'&:hover, &.hover': {
- [buttonVars.hover.background]: colors.soap400,
- [buttonVars.hover.border]: colors.licorice400,
- [buttonVars.hover.label]: colors.blackPepper400,
+ [buttonColorPropVars.hover.background]: colors.soap400,
+ [buttonColorPropVars.hover.border]: colors.licorice400,
+ [buttonColorPropVars.hover.label]: colors.blackPepper400,
[systemIconStencil.vars.color]: colors.licorice500,
button: {
[systemIconStencil.vars.color]: colors.licorice500,
},
},
'&:active, &.active': {
- [buttonVars.active.background]: colors.soap500,
- [buttonVars.active.border]: colors.licorice500,
- [buttonVars.active.label]: colors.blackPepper400,
+ [buttonColorPropVars.active.background]: colors.soap500,
+ [buttonColorPropVars.active.border]: colors.licorice500,
+ [buttonColorPropVars.active.label]: colors.blackPepper400,
[systemIconStencil.vars.color]: colors.licorice500,
button: {
[systemIconStencil.vars.color]: colors.licorice500,
},
},
'&:disabled, &.disabled': {
- [buttonVars.disabled.background]: colors.soap100,
- [buttonVars.disabled.border]: colors.licorice100,
- [buttonVars.disabled.label]: colors.licorice100,
- [buttonVars.disabled.opacity]: '1',
+ [buttonColorPropVars.disabled.background]: colors.soap100,
+ [buttonColorPropVars.disabled.border]: colors.licorice100,
+ [buttonColorPropVars.disabled.label]: colors.licorice100,
+ [buttonColorPropVars.disabled.opacity]: '1',
[systemIconStencil.vars.color]: colors.licorice100,
button: {
[systemIconStencil.vars.color]: colors.licorice100,
@@ -132,32 +132,32 @@ const StyledBasePill = styled(BaseButton.as('button'))(
);
const StyledNonInteractivePill = styled(StyledBasePill)({
- [buttonVars.default.background]: colors.soap300,
- [buttonVars.default.border]: colors.licorice200,
- [buttonVars.default.label]: colors.blackPepper400,
+ [buttonColorPropVars.default.background]: colors.soap300,
+ [buttonColorPropVars.default.border]: colors.licorice200,
+ [buttonColorPropVars.default.label]: colors.blackPepper400,
'&:focus-visible, &.focus': {
- [buttonVars.focus.background]: colors.soap300,
- [buttonVars.focus.border]: colors.licorice200,
- [buttonVars.focus.label]: colors.blackPepper400,
+ [buttonColorPropVars.focus.background]: colors.soap300,
+ [buttonColorPropVars.focus.border]: colors.licorice200,
+ [buttonColorPropVars.focus.label]: colors.blackPepper400,
},
'&:hover, &.hover': {
- [buttonVars.hover.background]: colors.soap300,
- [buttonVars.hover.border]: colors.licorice200,
- [buttonVars.hover.label]: colors.blackPepper400,
+ [buttonColorPropVars.hover.background]: colors.soap300,
+ [buttonColorPropVars.hover.border]: colors.licorice200,
+ [buttonColorPropVars.hover.label]: colors.blackPepper400,
},
'&:active, &.active': {
- [buttonVars.active.background]: colors.soap500,
- [buttonVars.active.border]: colors.licorice500,
- [buttonVars.active.label]: colors.blackPepper400,
+ [buttonColorPropVars.active.background]: colors.soap500,
+ [buttonColorPropVars.active.border]: colors.licorice500,
+ [buttonColorPropVars.active.label]: colors.blackPepper400,
},
'&:disabled, &.disabled': {
- [buttonVars.disabled.background]: colors.soap100,
- [buttonVars.disabled.label]: colors.licorice100,
- [buttonVars.disabled.border]: colors.licorice100,
+ [buttonColorPropVars.disabled.background]: colors.soap100,
+ [buttonColorPropVars.disabled.label]: colors.licorice100,
+ [buttonColorPropVars.disabled.border]: colors.licorice100,
},
cursor: 'default',
overflow: 'revert', // override BaseButton overflow styles so the click target exists outside the pill for removable
@@ -172,12 +172,12 @@ const StyledNonInteractivePill = styled(StyledBasePill)({
});
const StyledReadOnlyPill = styled(StyledNonInteractivePill)({
- [buttonVars.default.background]: 'transparent',
- [buttonVars.hover.background]: 'transparent',
- [buttonVars.focus.background]: 'transparent',
- [buttonVars.active.background]: 'transparent',
- [buttonVars.disabled.background]: 'transparent',
- border: `1px solid ${colors.licorice200}`,
+ [buttonColorPropVars.default.background]: 'transparent',
+ [buttonColorPropVars.hover.background]: 'transparent',
+ [buttonColorPropVars.focus.background]: 'transparent',
+ [buttonColorPropVars.active.background]: 'transparent',
+ [buttonColorPropVars.disabled.background]: 'transparent',
+ border: `${px2rem(1)} solid ${colors.licorice200}`,
});
/**
diff --git a/modules/preview-react/pill/lib/PillIconButton.tsx b/modules/preview-react/pill/lib/PillIconButton.tsx
index 7accf045f6..ca7e40d47f 100644
--- a/modules/preview-react/pill/lib/PillIconButton.tsx
+++ b/modules/preview-react/pill/lib/PillIconButton.tsx
@@ -7,7 +7,7 @@ import {usePillModel} from './usePillModel';
import {xSmallIcon} from '@workday/canvas-system-icons-web';
import {CanvasSystemIcon} from '@workday/design-assets-types';
import {colors, space} from '@workday/canvas-kit-react/tokens';
-import {BaseButton, buttonVars} from '@workday/canvas-kit-react/button';
+import {BaseButton, buttonColorPropVars} from '@workday/canvas-kit-react/button';
export interface PillIconButtonProps extends Omit {
/**
@@ -41,32 +41,32 @@ const StyledIconButton = styled(BaseButton)({
innerColor: 'transparent',
}),
},
- [buttonVars.default.background]: colors.soap300,
- [buttonVars.default.border]: 'transparent',
- [buttonVars.default.label]: colors.blackPepper400,
+ [buttonColorPropVars.default.background]: colors.soap300,
+ [buttonColorPropVars.default.border]: 'transparent',
+ [buttonColorPropVars.default.label]: colors.blackPepper400,
'&:focus-visible, &.focus': {
- [buttonVars.focus.background]: colors.soap300,
- [buttonVars.focus.border]: 'transparent',
- [buttonVars.focus.label]: colors.blackPepper400,
+ [buttonColorPropVars.focus.background]: colors.soap300,
+ [buttonColorPropVars.focus.border]: 'transparent',
+ [buttonColorPropVars.focus.label]: colors.blackPepper400,
},
'&:hover, &.hover': {
- [buttonVars.hover.background]: colors.soap300,
- [buttonVars.hover.border]: 'transparent',
- [buttonVars.hover.label]: colors.blackPepper400,
+ [buttonColorPropVars.hover.background]: colors.soap300,
+ [buttonColorPropVars.hover.border]: 'transparent',
+ [buttonColorPropVars.hover.label]: colors.blackPepper400,
},
'&:active, &.active': {
- [buttonVars.active.background]: colors.soap500,
- [buttonVars.active.border]: 'transparent',
- [buttonVars.active.label]: colors.blackPepper400,
+ [buttonColorPropVars.active.background]: colors.soap500,
+ [buttonColorPropVars.active.border]: 'transparent',
+ [buttonColorPropVars.active.label]: colors.blackPepper400,
},
'&:disabled, &.disabled': {
- [buttonVars.disabled.background]: colors.soap100,
- [buttonVars.disabled.label]: colors.licorice100,
- [buttonVars.disabled.border]: 'transparent',
+ [buttonColorPropVars.disabled.background]: colors.soap100,
+ [buttonColorPropVars.disabled.label]: colors.licorice100,
+ [buttonColorPropVars.disabled.border]: 'transparent',
[systemIconStencil.vars.color]: colors.licorice100,
},
});
diff --git a/modules/react/button/lib/BaseButton.tsx b/modules/react/button/lib/BaseButton.tsx
index 015ff46b1b..fbe67bf296 100644
--- a/modules/react/button/lib/BaseButton.tsx
+++ b/modules/react/button/lib/BaseButton.tsx
@@ -4,12 +4,12 @@ import {ButtonLabelIcon} from '../lib/parts/ButtonLabelIcon';
import {ButtonLabel} from '../lib/parts/ButtonLabel';
import {createComponent, GrowthBehavior, focusRing} from '@workday/canvas-kit-react/common';
-import {mergeStyles} from '@workday/canvas-kit-react/layout';
-import {createStyles, createVars, cssVar, createModifiers} from '@workday/canvas-kit-styling';
+import {cssVar, createStencil, px2rem, createVars, calc} from '@workday/canvas-kit-styling';
import {SystemIconProps, systemIconStencil} from '@workday/canvas-kit-react/icon';
-import {base, brand, system} from '@workday/canvas-tokens-web';
+import {brand, system} from '@workday/canvas-tokens-web';
import {ButtonColors, ButtonSizes, IconPositions} from './types';
import {CanvasSystemIcon} from '@workday/design-assets-types';
+import {mergeStyles} from '@workday/canvas-kit-react/layout';
export interface ButtonContainerProps extends Partial, GrowthBehavior {
/**
@@ -25,7 +25,7 @@ export interface ButtonContainerProps extends Partial, GrowthBe
size?: ButtonSizes;
/**
* The icon of the Button.
- * Note: not displayed at `small` size
+ * Note: Not displayed at `small` size
*/
icon?: CanvasSystemIcon;
/**
@@ -56,9 +56,9 @@ export interface ButtonContainerProps extends Partial, GrowthBe
export interface BaseButtonProps extends Omit {}
/**
- * Temporary css variables to be used across all Buttons.
+ * The purpose of this object is for the `colors` prop - to provide backwards compatibility with how we allowed color overrides in Emotion.
*/
-export const buttonVars = {
+export const buttonColorPropVars = {
default: createVars(
'background',
'border',
@@ -69,7 +69,7 @@ export const buttonVars = {
'opacity',
'borderRadius'
),
- hover: createVars(
+ focus: createVars(
'background',
'border',
'boxShadowInner',
@@ -79,7 +79,7 @@ export const buttonVars = {
'opacity',
'borderRadius'
),
- active: createVars(
+ hover: createVars(
'background',
'border',
'boxShadowInner',
@@ -89,7 +89,7 @@ export const buttonVars = {
'opacity',
'borderRadius'
),
- focus: createVars(
+ active: createVars(
'background',
'border',
'boxShadowInner',
@@ -114,200 +114,260 @@ export const buttonVars = {
/**
* Base styles for Buttons.
*/
-const baseButtonStyles = createStyles({
- fontFamily: '"Roboto", "Helvetica Neue", "Helvetica", Arial, sans-serif',
- fontSize: '0.875rem',
- lineHeight: 'normal',
- letterSpacing: '0.015rem',
- fontWeight: 'bold',
- backgroundColor: cssVar(buttonVars.default.background, 'transparent'),
- color: cssVar(buttonVars.default.label, base.blackPepper400),
- borderWidth: '1px',
- borderStyle: 'solid',
- gap: system.space.x2,
- borderColor: cssVar(buttonVars.default.border, 'transparent'),
- cursor: 'pointer',
- display: 'inline-flex',
- boxShadow: 'none',
- alignItems: 'center',
- justifyContent: 'center',
- boxSizing: 'border-box',
- outline: '2px transparent',
- whiteSpace: 'nowrap',
- WebkitFontSmoothing: 'antialiased',
- MozOsxFontSmoothing: 'grayscale',
- borderRadius: cssVar(buttonVars.default.borderRadius, system.shape.round),
- position: 'relative',
- verticalAlign: 'middle',
- overflow: 'hidden',
- [systemIconStencil.vars.color]: cssVar(buttonVars.default.icon, base.blackPepper400),
- transition:
- 'box-shadow 120ms linear, border 120ms linear, background-color 120ms linear, color 120ms linear',
- '&:disabled, &:disabled:active, &.disabled': {
- cursor: 'default',
- boxShadow: 'none',
- opacity: cssVar(buttonVars.disabled.opacity, '1'),
- },
- /*
- '& span .wd-icon-fill, & span .wd-icon-accent, & span .wd-icon-accent2': {
- transitionDuration: '40ms',
- fill: cssVar(buttonVars.default.icon, base.blackPepper400),
+export const buttonStencil = createStencil({
+ vars: {
+ background: '',
+ border: '',
+ boxShadowInner: '',
+ boxShadowOuter: '',
+ label: '',
+ opacity: '',
+ borderRadius: '',
},
- '.wd-icon-background ~ .wd-icon-accent, .wd-icon-background ~ .wd-icon-accent2': {
- fill: cssVar(buttonVars.default.icon, base.blackPepper400),
- },
- */
- '&:focus-visible, &.focus': {
- backgroundColor: cssVar(buttonVars.focus.background, 'transparent'),
- borderColor: cssVar(buttonVars.focus.border, 'transparent'),
- color: cssVar(buttonVars.focus.label, base.blackPepper400),
- [systemIconStencil.vars.color]: cssVar(buttonVars.focus.icon, base.blackPepper400),
- /*
- '& span .wd-icon-fill, & span .wd-icon-accent, & span .wd-icon-accent2': {
- fill: cssVar(buttonVars.focus.icon, base.blackPepper400),
+ base: ({background, border, boxShadowInner, boxShadowOuter, label, opacity, borderRadius}) => ({
+ // Default Styles
+ fontFamily: '"Roboto", "Helvetica Neue", "Helvetica", Arial, sans-serif',
+ fontSize: '0.875rem',
+ lineHeight: 'normal',
+ letterSpacing: '0.015rem',
+ fontWeight: system.fontWeight.bold,
+ backgroundColor: cssVar(
+ buttonColorPropVars.default.background,
+ cssVar(background, 'transparent')
+ ),
+ color: cssVar(buttonColorPropVars.default.label, cssVar(label, system.color.fg.strong)),
+ borderWidth: px2rem(1),
+ borderStyle: 'solid',
+ gap: system.space.x2,
+ borderColor: cssVar(buttonColorPropVars.default.border, cssVar(border, 'transparent')),
+ cursor: 'pointer',
+ display: 'inline-flex',
+ boxShadow: 'none',
+ alignItems: 'center',
+ justifyContent: 'center',
+ boxSizing: 'border-box',
+ outline: `${px2rem(2)} transparent`,
+ whiteSpace: 'nowrap',
+ WebkitFontSmoothing: 'antialiased',
+ MozOsxFontSmoothing: 'grayscale',
+ borderRadius: cssVar(
+ buttonColorPropVars.default.borderRadius,
+ cssVar(borderRadius, system.shape.round)
+ ),
+ position: 'relative',
+ verticalAlign: 'middle',
+ overflow: 'hidden',
+ [systemIconStencil.vars.color]: cssVar(
+ buttonColorPropVars.default.icon,
+ system.color.fg.strong
+ ),
+ transition:
+ 'box-shadow 120ms linear, border 120ms linear, background-color 120ms linear, color 120ms linear',
+ '&:disabled, &:disabled:active, &.disabled': {
+ cursor: 'default',
+ boxShadow: 'none',
+ opacity: cssVar(buttonColorPropVars.default.opacity, cssVar(opacity, system.opacity.full)),
},
- */
- ...focusRing({
- width: 2,
- separation: 2,
- innerColor: cssVar(buttonVars.focus.boxShadowInner, base.frenchVanilla100),
- outerColor: cssVar(buttonVars.focus.boxShadowOuter, brand.primary.base),
- }),
- },
- '&:hover, &.hover': {
- backgroundColor: cssVar(buttonVars.hover.background, base.blackPepper500),
- borderColor: cssVar(buttonVars.hover.border, 'transparent'),
- color: cssVar(buttonVars.hover.label, base.blackPepper500),
- [systemIconStencil.vars.color]: cssVar(buttonVars.hover.icon, base.blackPepper500),
- /*
- '& span .wd-icon-fill, & span .wd-icon-accent, & span .wd-icon-accent2': {
- fill: cssVar(buttonVars.hover.icon, base.blackPepper500),
+ // Focus Styles
+ '&:focus-visible, &.focus': {
+ backgroundColor: cssVar(
+ buttonColorPropVars.focus.background,
+ cssVar(background, 'transparent')
+ ),
+ borderColor: cssVar(buttonColorPropVars.focus.border, cssVar(border, 'transparent')),
+ color: cssVar(buttonColorPropVars.focus.label, cssVar(label, system.color.fg.strong)),
+ [systemIconStencil.vars.color]: cssVar(
+ buttonColorPropVars.focus.icon,
+ system.color.fg.strong
+ ),
+ ...focusRing({
+ width: 2,
+ separation: 2,
+ innerColor: cssVar(
+ buttonColorPropVars.focus.boxShadowInner,
+ cssVar(boxShadowInner, system.color.fg.inverse)
+ ),
+ outerColor: cssVar(
+ buttonColorPropVars.focus.boxShadowOuter,
+ cssVar(boxShadowOuter, brand.primary.base)
+ ),
+ }),
},
- */
- },
- '&:hover:active': {transitionDuration: '40ms'},
- '&:active, &.active': {
- backgroundColor: cssVar(buttonVars.active.background, 'transparent'),
- borderColor: cssVar(buttonVars.active.border, 'transparent'),
- color: cssVar(buttonVars.active.label, base.blackPepper400),
- [systemIconStencil.vars.color]: cssVar(buttonVars.active.icon, base.blackPepper400),
- /*
- '& span .wd-icon-fill, & span .wd-icon-accent, & span .wd-icon-accent2': {
- fill: cssVar(buttonVars.active.icon, base.blackPepper400),
+ // Hover Styles
+ '&:hover, &.hover': {
+ backgroundColor: cssVar(
+ buttonColorPropVars.hover.background,
+ cssVar(background, system.color.bg.contrast.strong)
+ ),
+ borderColor: cssVar(buttonColorPropVars.hover.border, cssVar(border, 'transparent')),
+ color: cssVar(buttonColorPropVars.hover.label, cssVar(label, system.color.fg.stronger)),
+ [systemIconStencil.vars.color]: cssVar(
+ buttonColorPropVars.hover.icon,
+ system.color.fg.stronger
+ ),
},
- */
- },
- '&:disabled, &.disabled': {
- backgroundColor: cssVar(buttonVars.disabled.background, 'transparent'),
- borderColor: cssVar(buttonVars.disabled.border, 'transparent'),
- color: cssVar(buttonVars.disabled.label, base.blackPepper400),
- [systemIconStencil.vars.color]: cssVar(buttonVars.disabled.icon, base.blackPepper400),
- /*
- '& span .wd-icon-fill, & span .wd-icon-accent, & span .wd-icon-accent2': {
- fill: cssVar(buttonVars.disabled.icon, base.blackPepper400),
+ '&:hover:active': {transitionDuration: '40ms'},
+ // Active Styles
+ '&:active, &.active': {
+ backgroundColor: cssVar(
+ buttonColorPropVars.active.background,
+ cssVar(background, 'transparent')
+ ),
+ borderColor: cssVar(buttonColorPropVars.active.border, cssVar(border, 'transparent')),
+ color: cssVar(buttonColorPropVars.active.label, cssVar(label, system.color.fg.strong)),
+ [systemIconStencil.vars.color]: cssVar(
+ buttonColorPropVars.active.icon,
+ system.color.fg.strong
+ ),
+ },
+ // Disabled Styles
+ '&:disabled, &.disabled': {
+ backgroundColor: cssVar(
+ buttonColorPropVars.disabled.background,
+ cssVar(background, 'transparent')
+ ),
+ borderColor: cssVar(buttonColorPropVars.disabled.border, cssVar(border, 'transparent')),
+ color: cssVar(buttonColorPropVars.disabled.label, cssVar(label, system.color.fg.strong)),
+ [systemIconStencil.vars.color]: cssVar(
+ buttonColorPropVars.disabled.icon,
+ system.color.fg.strong
+ ),
+ },
+ }),
+ modifiers: {
+ /**
+ * Button modifiers that will overwrite the base styles of Buttons.
+ * - `Size`: These modifiers will dictate a size of a Button and has a set of styles to associated with it.
+ * - `iconPosition`: These modifiers will override the existing `Size` styles. These are specific to icon locations
+ * within a button or if there is only an icon and no text.
+ */
+ size: {
+ large: {
+ ...system.type.body.small,
+ fontWeight: system.fontWeight.bold,
+ height: px2rem(48),
+ paddingInline: system.space.x8,
+ minWidth: px2rem(112),
+ },
+ medium: {
+ ...system.type.subtext.large,
+ fontWeight: system.fontWeight.bold,
+ minWidth: px2rem(96),
+ paddingInline: system.space.x6,
+ height: system.space.x10,
+ },
+ small: {
+ ...system.type.subtext.large,
+ fontWeight: system.fontWeight.bold,
+ height: system.space.x8,
+ minWidth: system.space.x20,
+ paddingInline: system.space.x4,
+ gap: system.space.x1,
+ },
+ extraSmall: {
+ ...system.type.subtext.medium,
+ fontWeight: system.fontWeight.bold,
+ height: system.space.x6,
+ minWidth: 'auto',
+ paddingInline: system.space.x3,
+ gap: system.space.x1,
+ },
+ },
+ // IconPosition Styles
+ iconPosition: {
+ only: {padding: system.space.zero},
+ start: {},
+ end: {},
},
- */
- },
-});
-
-/**
- * Button modifiers that will overwrite the base styles of Buttons.
- * - `Size`: These modifiers will dictate a size of a Button and has a set of styles to associated with it.
- * - `iconPosition`: These modifiers will override the existing `Size` styles. These are specific to icon locations
- * within a button or if there is only an icon and no text.
- */
-export const buttonModifiers = createModifiers({
- size: {
- large: createStyles({
- fontSize: system.space.x4,
- lineHeight: system.space.x6,
- letterSpacing: '0.01rem',
- height: '48px',
- paddingInline: system.space.x8,
- minWidth: '112px',
- }),
- medium: createStyles({
- fontSize: '0.875rem',
- letterSpacing: '0.015rem',
- minWidth: '96px',
- paddingInline: system.space.x6,
- height: system.space.x10,
- }),
- small: createStyles({
- fontSize: '0.875rem',
- letterSpacing: '0.015rem',
- height: system.space.x8,
- minWidth: system.space.x20,
- paddingInline: system.space.x4,
- gap: system.space.x1,
- }),
- extraSmall: createStyles({
- fontSize: '0.75rem',
- lineHeight: system.space.x4,
- letterSpacing: '0.02rem',
- height: system.space.x6,
- minWidth: 'auto',
- paddingInline: system.space.x3,
- gap: system.space.x1,
- }),
- },
- iconPosition: {
- largeOnly: createStyles({
- padding: '0',
- minWidth: `calc(${system.space.x4} * 3)`,
- }),
- largeStart: createStyles({
- paddingInlineStart: system.space.x6,
- paddingInlineEnd: system.space.x8,
- }),
- largeEnd: createStyles({
- paddingInlineStart: system.space.x8,
- paddingInlineEnd: system.space.x6,
- }),
- mediumOnly: createStyles({padding: '0', minWidth: system.space.x10}),
- mediumStart: createStyles({
- paddingInlineStart: `calc(${system.space.x1} * 5)`,
- paddingInlineEnd: system.space.x6,
- }),
- mediumEnd: createStyles({
- paddingInlineStart: system.space.x6,
- paddingInlineEnd: `calc(${system.space.x1} * 5)`,
- }),
- smallOnly: createStyles({padding: '0', minWidth: system.space.x8}),
- smallStart: createStyles({
- paddingInlineStart: system.space.x3,
- paddingInlineEnd: system.space.x4,
- }),
- smallEnd: createStyles({
- paddingInlineStart: system.space.x4,
- paddingInlineEnd: system.space.x3,
- }),
- extraSmallOnly: createStyles({padding: '0', minWidth: system.space.x6}),
- extraSmallStart: createStyles({
- paddingInlineStart: system.space.x2,
- paddingInlineEnd: system.space.x3,
- }),
- extraSmallEnd: createStyles({
- paddingInlineStart: system.space.x3,
- paddingInlineEnd: system.space.x2,
- }),
},
+ // Compound Modifier Styles
+ compound: [
+ {
+ modifiers: {size: 'large', iconPosition: 'only'},
+ styles: {
+ minWidth: calc.multiply(system.space.x4, 3),
+ },
+ },
+ {
+ modifiers: {size: 'large', iconPosition: 'start'},
+ styles: {
+ paddingInlineStart: system.space.x6,
+ paddingInlineEnd: system.space.x8,
+ },
+ },
+ {
+ modifiers: {size: 'large', iconPosition: 'end'},
+ styles: {
+ paddingInlineStart: system.space.x8,
+ paddingInlineEnd: system.space.x6,
+ },
+ },
+ {
+ modifiers: {size: 'medium', iconPosition: 'only'},
+ styles: {
+ minWidth: system.space.x10,
+ },
+ },
+ {
+ modifiers: {size: 'medium', iconPosition: 'start'},
+ styles: {
+ paddingInlineStart: calc.multiply(system.space.x1, 5),
+ paddingInlineEnd: system.space.x6,
+ },
+ },
+ {
+ modifiers: {size: 'medium', iconPosition: 'end'},
+ styles: {
+ paddingInlineStart: system.space.x6,
+ paddingInlineEnd: calc.multiply(system.space.x1, 5),
+ },
+ },
+ {
+ modifiers: {size: 'small', iconPosition: 'only'},
+ styles: {
+ minWidth: system.space.x8,
+ },
+ },
+ {
+ modifiers: {size: 'small', iconPosition: 'start'},
+ styles: {
+ paddingInlineStart: system.space.x3,
+ paddingInlineEnd: system.space.x4,
+ },
+ },
+ {
+ modifiers: {size: 'small', iconPosition: 'end'},
+ styles: {
+ paddingInlineStart: system.space.x4,
+ paddingInlineEnd: system.space.x3,
+ },
+ },
+ {
+ modifiers: {size: 'extraSmall', iconPosition: 'only'},
+ styles: {
+ minWidth: system.space.x6,
+ },
+ },
+ {
+ modifiers: {size: 'extraSmall', iconPosition: 'start'},
+ styles: {
+ paddingInlineStart: system.space.x2,
+ paddingInlineEnd: system.space.x3,
+ },
+ },
+ {
+ modifiers: {size: 'extraSmall', iconPosition: 'end'},
+ styles: {
+ paddingInlineStart: system.space.x3,
+ paddingInlineEnd: system.space.x2,
+ },
+ },
+ ],
});
-export function capitalize(string: string = '') {
- return string.charAt(0).toUpperCase() + string.substring(1);
-}
-
-export function getIconPosition(
- size?: keyof typeof buttonModifiers.size,
- iconPosition?: IconPositions,
- children?: React.ReactNode
-): keyof typeof buttonModifiers.iconPosition {
- return `${size}${capitalize(iconPosition)}` as keyof typeof buttonModifiers.iconPosition;
-}
-
/**
- * The base button which all other buttons are built.
+ * The base button which which is the foundation of all Button variants (`PrimaryButton`,
+ * `SecondaryButton`, `TertiaryButton`, `DeleteButton`, `ToolbarIconButton` and `ToolbarDropdownButton`).
*/
export const BaseButton = createComponent('button')({
displayName: 'BaseButton',
@@ -330,16 +390,12 @@ export const BaseButton = createComponent('button')({
ref={ref}
type="button"
{...mergeStyles(elemProps, [
- baseButtonStyles,
- buttonModifiers({
- size: size,
- iconPosition: getIconPosition(size, iconPosition, children),
- }),
- buttonVars.default(colors?.default || {}),
- buttonVars.focus(colors?.focus || {}),
- buttonVars.hover(colors?.hover || {}),
- buttonVars.active(colors?.active || {}),
- buttonVars.disabled(colors?.disabled || {}),
+ buttonStencil({size, iconPosition}),
+ buttonColorPropVars.default(colors?.default || {}),
+ buttonColorPropVars.focus(colors?.focus || {}),
+ buttonColorPropVars.hover(colors?.hover || {}),
+ buttonColorPropVars.active(colors?.active || {}),
+ buttonColorPropVars.disabled(colors?.disabled || {}),
])}
>
{children}
diff --git a/modules/react/button/lib/Button.tsx b/modules/react/button/lib/Button.tsx
index 859b663522..cb7e181572 100644
--- a/modules/react/button/lib/Button.tsx
+++ b/modules/react/button/lib/Button.tsx
@@ -56,10 +56,7 @@ export const Button = createComponent('button')({
)}
{children && {children}}
- {icon && baseIconPosition === 'only' && (
-
- )}
- {icon && baseIconPosition === 'end' && (
+ {icon && baseIconPosition && ['only', 'end'].includes(baseIconPosition) && (
)}
diff --git a/modules/react/button/lib/DeleteButton.tsx b/modules/react/button/lib/DeleteButton.tsx
index 43faec85e3..c44882c8f0 100644
--- a/modules/react/button/lib/DeleteButton.tsx
+++ b/modules/react/button/lib/DeleteButton.tsx
@@ -1,12 +1,12 @@
import * as React from 'react';
-import {buttonVars} from './BaseButton';
+import {buttonStencil} from './BaseButton';
import {createComponent} from '@workday/canvas-kit-react/common';
-import {createStyles} from '@workday/canvas-kit-styling';
-import {mergeStyles} from '@workday/canvas-kit-react/layout';
-import {base, brand, system} from '@workday/canvas-tokens-web';
+import {createStencil} from '@workday/canvas-kit-styling';
+import {brand, system} from '@workday/canvas-tokens-web';
import {Button, ButtonProps} from './Button';
-import {systemIconStencil} from '../../icon';
+import {systemIconStencil} from '@workday/canvas-kit-react/icon';
+import {mergeStyles} from '@workday/canvas-kit-react/layout';
/**
* Extends all the style properties from Box to our buttons as well as props from ButtonProps.
@@ -15,50 +15,54 @@ import {systemIconStencil} from '../../icon';
*/
export interface DeleteButtonProps extends ButtonProps {}
-const deleteStyles = createStyles({
- [buttonVars.default.background]: brand.error.base,
- [buttonVars.default.border]: 'transparent',
- [buttonVars.default.borderRadius]: system.shape.round,
- [buttonVars.default.label]: brand.error.accent,
- [systemIconStencil.vars.color]: brand.error.accent,
- '&:focus-visible, &.focus': {
- [buttonVars.focus.background]: brand.error.base,
- [buttonVars.focus.border]: 'transparent',
- [buttonVars.focus.label]: brand.error.accent,
- [systemIconStencil.vars.color]: brand.error.accent,
- [buttonVars.focus.boxShadowInner]: base.frenchVanilla100,
- [buttonVars.focus.boxShadowOuter]: brand.common.focusOutline,
- },
- '&:hover, &.hover': {
- [buttonVars.hover.background]: brand.error.dark,
- [buttonVars.hover.border]: 'transparent',
- [buttonVars.hover.label]: brand.error.accent,
- [systemIconStencil.vars.color]: brand.error.accent,
- },
- '&:active, &.active': {
- [buttonVars.active.background]: brand.error.darkest,
- [buttonVars.active.border]: 'transparent',
- [buttonVars.active.label]: brand.error.accent,
- [systemIconStencil.vars.color]: brand.error.accent,
- },
- '&:disabled, &.disabled': {
- [buttonVars.disabled.background]: brand.error.base,
- [buttonVars.disabled.label]: brand.error.accent,
+const deleteButtonStencil = createStencil({
+ extends: buttonStencil,
+ base: {
+ // Base Styles
+ [buttonStencil.vars.background]: brand.error.base,
+ [buttonStencil.vars.borderRadius]: system.shape.round,
+ [buttonStencil.vars.label]: brand.error.accent,
[systemIconStencil.vars.color]: brand.error.accent,
- [buttonVars.disabled.opacity]: '0.4',
+ // Focus Styles
+ '&:focus-visible, &.focus': {
+ [buttonStencil.vars.background]: brand.error.base,
+ [buttonStencil.vars.label]: brand.error.accent,
+ [systemIconStencil.vars.color]: brand.error.accent,
+ [buttonStencil.vars.boxShadowInner]: system.color.fg.inverse,
+ [buttonStencil.vars.boxShadowOuter]: brand.common.focusOutline,
+ },
+ // Hover Styles
+ '&:hover, &.hover': {
+ [buttonStencil.vars.background]: brand.error.dark,
+ [buttonStencil.vars.label]: brand.error.accent,
+ [systemIconStencil.vars.color]: brand.error.accent,
+ },
+ // Active Styles
+ '&:active, &.active': {
+ [buttonStencil.vars.background]: brand.error.darkest,
+ [buttonStencil.vars.label]: brand.error.accent,
+ [systemIconStencil.vars.color]: brand.error.accent,
+ },
+ // Disabled Styles
+ '&:disabled, &.disabled': {
+ [buttonStencil.vars.background]: brand.error.base,
+ [buttonStencil.vars.label]: brand.error.accent,
+ [systemIconStencil.vars.color]: brand.error.accent,
+ [buttonStencil.vars.opacity]: system.opacity.disabled,
+ },
},
});
/**
* Use sparingly for destructive actions that will result in data loss, can’t be undone, or will
* have significant consequences. They commonly appear in confirmation dialogs as the final
- * confirmation before deleting.
+ * confirmation before being deleted.
*/
export const DeleteButton = createComponent('button')({
displayName: 'DeleteButton',
- Component: ({children, size, ...elemProps}: DeleteButtonProps, ref, Element) => {
+ Component: ({children, ...elemProps}: DeleteButtonProps, ref, Element) => {
return (
-