Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8ad3aff
feat: Update TextInput border radius
jamesfan Jun 15, 2026
b3f1b31
feat: Remove background color from Caution and Error states for TextI…
jamesfan Jun 15, 2026
bf299e6
fix: Preserve use of legacy tokens for TextInput
jamesfan Jun 16, 2026
cfea945
feat: Update styles for FormFieldGroupList
jamesfan Jun 22, 2026
459c285
Merge branch 'prerelease/major' into feat/2026-06-15-sc-text-input-ar…
jamesfan Jun 22, 2026
f07ad88
docs: Add visual changes to v16 Upgrade Guide
jamesfan Jun 24, 2026
fcac97f
Merge branch 'prerelease/major' of github.com:Workday/canvas-kit into…
jamesfan Jun 24, 2026
bcdef56
feat: Add cornerShapeStencil to TextInput, FormFieldGroupList, and Co…
jamesfan Jun 24, 2026
37afba8
docs: Update Upgrade Guide
jamesfan Jun 24, 2026
f7805dd
chore: Add Sana Theme Color Picker visual tests
jamesfan Jun 24, 2026
92518ad
docs: Fix Upgrade Guide
jamesfan Jun 24, 2026
ae9ae1e
chore: Revert update to Color Picker visual testing stories
jamesfan Jun 24, 2026
6f6ed4b
chore: Default Color Picker stories to Sana Canvas theme
jamesfan Jun 24, 2026
07db652
chore: Default Form Field, Text Area, and Text Input stories to Sana …
jamesfan Jun 24, 2026
9bde3e3
chore: Add v16 Upgrade Guide to routes file
jamesfan Jun 24, 2026
92b234c
docs: Add Sana Canvas example to Text Input
jamesfan Jun 24, 2026
646df83
docs: Add Sana Canvas example to Text Area
jamesfan Jun 24, 2026
d38721c
docs: Add Sana Canvas example to Form Field
jamesfan Jun 24, 2026
4188f45
docs: Add Sana Canvas example to Color Preview
jamesfan Jun 24, 2026
09245e9
docs: Add Sana Canvas example to Color Input
jamesfan Jun 24, 2026
8be501d
fix: Fix Color Preview stories
jamesfan Jun 25, 2026
4bce17f
chore: Revert addition of SanaCanvas examples
jamesfan Jun 25, 2026
7b5ab04
fix: Remove unnecessary data-theme from ColorPicker story
jamesfan Jun 25, 2026
2510d65
fix: a11y fixes for FormFieldGroupList
jamesfan Jun 26, 2026
3572695
fix: a11y fixes for Color Picker components
jamesfan Jun 26, 2026
a6e4df6
fix: Adjust forced-color mode outline for FormFieldGroupList
jamesfan Jun 29, 2026
654bbed
fix: Fix outlines for TextInput and TextArea for Windows High Contras…
jamesfan Jun 29, 2026
5310fcb
Merge branch 'prerelease/major' of github.com:Workday/canvas-kit into…
jamesfan Jun 29, 2026
2ba7152
chore: Remove data-theme from StaticStates in visual testing stories
jamesfan Jun 29, 2026
e007095
chore: Revert padding changes to FormFieldGroupList
jamesfan Jun 30, 2026
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
3 changes: 3 additions & 0 deletions .storybook/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ const routes = {
'/help/upgrade-guides/canvas-v15-upgrade-guide/': 'guides-upgrade-guides-v15-0-overview--docs',
'/help/upgrade-guides/canvas-v15-upgrade-guide/#tab=visual-changes':
'guides-upgrade-guides-v15-0-visual-changes--docs',
'/help/upgrade-guides/canvas-v16-upgrade-guide/': 'guides-upgrade-guides-v16-0-overview--docs',
'/help/upgrade-guides/canvas-v16-upgrade-guide/#tab=visual-changes':
'guides-upgrade-guides-v16-0-visual-changes--docs',
Comment thread
jamesfan marked this conversation as resolved.
};

export default routes;
50 changes: 50 additions & 0 deletions modules/docs/mdx/16.0-UPGRADE-GUIDE.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,56 @@ functional shortcuts, refer to the storybook documentation and our example stori

## Component Updates

> **Note:** All visual updates apply to both the Default Canvas theme and new Sana Canvas theme
> unless specified otherwise.

### Color Input

**PR:** [#3992](https://github.com/Workday/canvas-kit/pull/3992)

#### Visual Updates

- Shape is now **12px** (previously 8px).

### Color Picker

**PR:** [#3992](https://github.com/Workday/canvas-kit/pull/3992)

#### Visual Updates

- **Sana Canvas:** swatch shape is now **6px** (previously **4px**).

### Form Field

**PR:** [#3992](https://github.com/Workday/canvas-kit/pull/3992)

#### Visual Updates

- Shape for grouped inputs (Radio groups and Checkbox groups) is now **12px** (previously 8px). As
in v15, this shape is primarily visible for error and caution states.
- Error and caution states no longer display a status background color; only the standard background
is shown.

### Text Area

**PR:** [#3992](https://github.com/Workday/canvas-kit/pull/3992)

#### Visual Updates

- Shape is now **12px** (previously 8px).
- Error and caution states no longer display a status background color; only the standard background
is shown.

### Text Input

**PR:** [#3992](https://github.com/Workday/canvas-kit/pull/3992)

#### Visual Updates

- Shape is now **12px** (previously 8px).
- Error and caution states no longer display a status background color; only the standard background
is shown.

### Card

**PR:** [#4014](https://github.com/Workday/canvas-kit/pull/4014)
Expand Down
3 changes: 3 additions & 0 deletions modules/react/color-picker/lib/ColorInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ export const colorPickerHexInputStencil = createStencil({
left: system.legacy.gap.sm,
boxShadow: `inset 0 0 0 ${px2rem(1)} rgba(0,0,0,0.25)`,
pointerEvents: 'none',
'@media (forced-colors: active)': {
outline: `solid ${px2rem(1)} SelectedItemText`,
},
},

':dir(ltr)': {
Expand Down
11 changes: 8 additions & 3 deletions modules/react/color-picker/lib/parts/ColorSwatch.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';

import {pickForegroundColor} from '@workday/canvas-kit-react/common';
import {cornerShapeStencil, pickForegroundColor} from '@workday/canvas-kit-react/common';
import {SystemIcon, systemIconStencil} from '@workday/canvas-kit-react/icon';
import {calc, createStencil, handleCsProp, px2rem} from '@workday/canvas-kit-styling';
import {checkSmallIcon} from '@workday/canvas-system-icons-web';
Expand All @@ -12,19 +12,21 @@ export interface ColorSwatchProps extends React.HTMLAttributes<HTMLDivElement> {
}

export const colorPickerColorSwatchStencil = createStencil({
extends: cornerShapeStencil,
vars: {
color: '',
iconColor: '',
},
base: ({color, iconColor}) => ({
[systemIconStencil.vars.color]: iconColor,
[cornerShapeStencil.vars.shape]: system.legacy.shape.sm,
width: system.legacy.size.xxs,
height: system.legacy.size.xxs,
borderRadius: system.legacy.shape.sm,
Comment thread
jamesfan marked this conversation as resolved.
backgroundColor: color,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: ColorSwatch does not preserve color meaning in Windows High Contrast / forced-colors mode. Since the swatch’s semantic value is its actual color, backgroundColor: color will likely be remapped by forced-colors, causing swatches to appear identical. Consider forcedColorAdjust: 'none' for the swatch color surface, plus a real border/outline fallback for white/selected states since box-shadow is forced to none.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williamjstanton This should be addressed now. I've added forcedColorAdjust: 'none' to the swatch color surface as well as the outline fallback.

display: 'flex',
alignItems: 'center',
justifyContent: 'center',
forcedColorAdjust: 'none',
'& > *': {
// Account for 24px icon
margin: calc.negate(px2rem(2)),
Expand All @@ -34,6 +36,9 @@ export const colorPickerColorSwatchStencil = createStencil({
withShadow: {
true: {
boxShadow: `inset 0px 0px 0px ${px2rem(1)} rgba(0, 0, 0, 0.25)`,
'@media (forced-colors: active)': {
outline: `solid ${px2rem(1)} SelectedItemText`,
},
},
},
},
Expand All @@ -50,7 +55,7 @@ export const ColorSwatch = ({color, showCheck = false, ...elemProps}: ColorSwatc
colorPickerColorSwatchStencil({
color,
iconColor: pickForegroundColor(color),
withShadow: showCheck || lowerCasedColor === '#ffffff',
withShadow: showCheck || lowerCasedColor === '#ffffff' ? 'true' : undefined,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix for same issue described here.

})
)}
>
Expand Down
13 changes: 9 additions & 4 deletions modules/react/form-field/lib/FormFieldGroupList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {createSubcomponent} from '@workday/canvas-kit-react/common';
import {cornerShapeStencil, createSubcomponent} from '@workday/canvas-kit-react/common';
import {FlexProps} from '@workday/canvas-kit-react/layout';
import {CSProps, calc, createStencil, handleCsProp, px2rem} from '@workday/canvas-kit-styling';
import {base, system} from '@workday/canvas-tokens-web';
Expand All @@ -8,10 +8,11 @@ import {useFormFieldModel} from './hooks';
export interface FormFieldGroupListProps extends CSProps, FlexProps {}

const formFieldGroupListStencil = createStencil({
extends: cornerShapeStencil,
base: {
[cornerShapeStencil.vars.shape]: system.legacy.shape.lg,
display: 'flex',
flexDirection: 'column',
borderRadius: system.legacy.shape.md,
gap: system.legacy.gap.sm,
padding: `${px2rem(10)} ${base.legacy.size150} ${system.legacy.padding.xs}`,
margin: `0 ${calc.negate(base.legacy.size150)}`,
Expand All @@ -21,12 +22,16 @@ const formFieldGroupListStencil = createStencil({
modifiers: {
error: {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: Error/caution visual affordances rely heavily on boxShadow, which is removed in forced-colors mode. For TextInput, TextArea inherits this behavior; for grouped fields, the group ring is entirely shadow-based. Add @media (forced-colors: active) fallbacks using real border/outline and system colors so Windows High Contrast users can still perceive invalid/caution states.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williamjstanton I'm consulting the MDN docs for system colors. Which color would you recommend I use for error and caution states?

For example, ActiveText is visually similar to our standard error color (red), but doesn't line up semantically (i.e., the form field error has nothing to do with the "text of active links").

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williamjstanton Also, it wasn't quite clear from your original comment, but is it ok to leave TextInput and TextArea as-is given they both still have a border in the error and caution states?

@williamjstanton williamjstanton Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williamjstanton I'm consulting the MDN docs for system colors. Which color would you recommend I use for error and caution states?

For example, ActiveText is visually similar to our standard error color (red), but doesn't line up semantically (i.e., the form field error has nothing to do with the "text of active links").

This is a really good question, and I don't know. There isn't any semantically correct option available at all. The recommendation is to use border differentiation like double but I don't think it is necessary in the case of FormFieldGroupList. I think we could try a simple transparent border or outline as a fallback?

.form-field.error {
  border: 2px solid red; /* Standard mode color */
}
@media (forced-colors: active) {
  .form-field.error {
    /* Use a thicker or distinct border style with standard system colors */
    border: 4px double ButtonBorder; 
  }

@jamesfan jamesfan Jun 26, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using outline to prevent layout shifts since FormFieldGroupList doesn't have a border to begin with. My plan was to maintain a similar thickness to the boxShadow for the outline:

error: {
  boxShadow: `inset 0 0 0 ${px2rem(2)} ${system.legacy.color.brand.border.critical}`,
  '@media (forced-colors: active)': {
    outline: `solid ${px2rem(2)} ActiveText`,
  },
},

I can double the thickness, however. Out of curiosity, what's the rationale behind differentiating the border if forced-colors is active?

Also, did you mean to use ButtonBorder (presents as black to me in macOS) in your example snippet?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, did you mean to use ButtonBorder (presents as black to me in macOS) in your example snippet?

Really? Presents as black? I'd assumed it would present as white on a dark background. I'll definitely need to test on a real Windows machine.

Rationale: Since we can't use color to differentiate a border on an input that already uses a border in a valid state (e.g. TextInput), that's where it is useful to use more thickness or a double style to differentiate the error states from valid states.

I don't think that rationale applies in this situation with the group list. (Because the border / outline / boxShadow is absent in valid state.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williamjstanton Apologies, I was confused. I didn't realize Windows High Contrast mode was an example of a forced colors mode (I thought the two could be toggled independently of one another. ButtonBorder presented as black to me in macOS because I obviously wasn't on Windows High Contrast mode.

Appreciate the explanation. The original issue should be resolved:

  • I've added the fallback outline to the grouped fields error and caution states. I used the same width as the boxShadow used in the error/caution states since there is no border/outline/boxShadow in the valid state.
  • I've added the fallback outline to TextInput and TextArea. It displays at double the width of the focus ring to differentiate it from the focus ring (I did not want to modify the focus ring styling).

error: {
backgroundColor: system.legacy.color.brand.surface.critical.default,
boxShadow: `inset 0 0 0 ${px2rem(2)} ${system.legacy.color.brand.border.critical}`,
'@media (forced-colors: active)': {
outline: `solid ${px2rem(2)} ButtonBorder`,
},
},
caution: {
backgroundColor: system.legacy.color.brand.surface.caution.default,
boxShadow: `inset 0 0 0 ${px2rem(1)} ${system.legacy.color.brand.border.caution}, inset 0 0 0 ${px2rem(3)} ${system.legacy.color.brand.focus.caution.inner}`,
'@media (forced-colors: active)': {
outline: `solid ${px2rem(2)} ButtonBorder`,
},
},
},
},
Expand Down
34 changes: 29 additions & 5 deletions modules/react/text-input/lib/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import {ErrorType, GrowthBehavior, createComponent} from '@workday/canvas-kit-react/common';
import {
ErrorType,
GrowthBehavior,
cornerShapeStencil,
createComponent,
} from '@workday/canvas-kit-react/common';
import {mergeStyles} from '@workday/canvas-kit-react/layout';
import {CSProps, createStencil, cssVar, px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
Expand All @@ -15,10 +20,12 @@ export interface TextInputProps extends GrowthBehavior, CSProps {
}

export const textInputStencil = createStencil({
extends: cornerShapeStencil,
vars: {
width: '',
},
base: ({width}) => ({
[cornerShapeStencil.vars.shape]: system.legacy.shape.lg,
fontFamily: system.fontFamily.default,
fontSize: system.legacy.fontSize.subtext.lg,
fontWeight: system.fontWeight.normal,
Expand All @@ -27,7 +34,6 @@ export const textInputStencil = createStencil({
display: 'block',
border: `${px2rem(1)} solid ${system.color.border.input.default}`,
backgroundColor: system.legacy.color.surface.default,
borderRadius: system.legacy.shape.md,
height: system.legacy.size.md,
transition: '0.2s box-shadow, 0.2s border-color',
padding: system.legacy.padding.xs, // Compensate for border
Expand Down Expand Up @@ -74,7 +80,6 @@ export const textInputStencil = createStencil({
borderColor: system.legacy.color.brand.border.critical,
// borderWidth: px2rem(2),
boxShadow: `inset 0 0 0 ${px2rem(2)} ${system.legacy.color.brand.border.critical}`,
backgroundColor: system.legacy.color.brand.surface.critical.default,
'&:is(:hover, .hover, :disabled, .disabled, :focus-visible:not([disabled]), .focus:not([disabled]))':
{
borderColor: system.legacy.color.brand.border.critical,
Expand All @@ -85,11 +90,17 @@ export const textInputStencil = createStencil({
0 0 0 4px ${system.legacy.color.brand.border.primary}`,
outlineOffset: px2rem(2),
},
// For Windows High Contrast mode: display error outline at double the
// width of the focus ring outline (to differentiate the error outline
// from the focus ring outline since both outlines will be the same
// color in WHC mode).
'@media (forced-colors: active)': {
outline: `solid ${px2rem(4)} ButtonBorder`,
},
},
caution: {
borderColor: system.legacy.color.brand.border.caution,
boxShadow: `inset 0 0 0 ${px2rem(2)} ${system.legacy.color.brand.focus.caution.inner}`,
backgroundColor: system.legacy.color.brand.surface.caution.default,
'&:is(:hover, .hover, :disabled, .disabled, :focus-visible:not([disabled]), .focus:not([disabled]))':
{
borderColor: system.legacy.color.brand.border.caution,
Expand All @@ -100,6 +111,15 @@ export const textInputStencil = createStencil({
0 0 0 4px ${system.legacy.color.brand.border.primary}`,
},
outlineOffset: px2rem(2),
// For Windows High Contrast mode: see comment above regarding the
// error outline. Unlike the error state, outlineOffset for the
// caution state is defined outside of the `(:focus-visible, .focus)`
// selector so we must override it here to prevent the caution outline
// from being separated too far from the input.
'@media (forced-colors: active)': {
outline: `solid ${px2rem(4)} ButtonBorder`,
outlineOffset: 0,
},
},
},
},
Expand All @@ -117,7 +137,11 @@ export const TextInput = createComponent('input')({
ref={ref}
{...mergeStyles(
elemProps,
textInputStencil({width: typeof width === 'number' ? px2rem(width) : width, grow, error})
textInputStencil({
width: typeof width === 'number' ? px2rem(width) : width,
grow: grow === true ? 'true' : grow === false ? 'false' : undefined,
error,
})
Comment on lines +140 to +144

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fix addresses a TypeScript issue which appeared after introducing extends cornerShapeStencil:

Image

Explanation of the fix from Cursor:

Image

And why the error only happened after introducing extends cornerShapeStencil:

Image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connected to #3896, I'll talk to Alan about that the next week ☺️

Comment on lines +140 to +144

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to fix that issue finally :D I'll talk to Alan about that when he is back

)}
/>
);
Expand Down
Loading