diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiCheckboxGroup_High_Contrast.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiCheckboxGroup_High_Contrast.png new file mode 100644 index 00000000000..87e00c86dda Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiCheckboxGroup_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiCheckboxGroup_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiCheckboxGroup_Playground.png index 8993825b30e..ea6dd10f37c 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiCheckboxGroup_Playground.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiCheckboxGroup_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Non_Large_Display.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Non_Large_Display.png index 1163f0498b5..bf372f58344 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Non_Large_Display.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Non_Large_Display.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Playground.png index 05e1f9b465f..06ef2c55a25 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Playground.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiFilePicker_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast.png new file mode 100644 index 00000000000..c9c92b8b15d Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast_Dark_Mode.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast_Dark_Mode.png new file mode 100644 index 00000000000..119148230ad Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast_Dark_Mode.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_Kitchen_Sink.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_Kitchen_Sink.png index 55b1fd76bef..654643b7bb4 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_Kitchen_Sink.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayoutDelimited_Kitchen_Sink.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_Append_Prepend.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_Append_Prepend.png index 8dc5fa3c872..d2f65b31b6c 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_Append_Prepend.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_Append_Prepend.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_High_Contrast.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_High_Contrast.png new file mode 100644 index 00000000000..bdf5c58e762 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_High_Contrast_Dark_Mode.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_High_Contrast_Dark_Mode.png new file mode 100644 index 00000000000..58372c06cc3 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiForm_EuiFormControlLayout_High_Contrast_Dark_Mode.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRadioGroup_High_Contrast.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRadioGroup_High_Contrast.png new file mode 100644 index 00000000000..5fdc7277287 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRadioGroup_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRadioGroup_Playground.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRadioGroup_Playground.png index 8fc344b6542..97ae3cf30d2 100644 Binary files a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRadioGroup_Playground.png and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiRadioGroup_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSwitch_Kitchen_Sink_High_Contrast.png b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSwitch_Kitchen_Sink_High_Contrast.png new file mode 100644 index 00000000000..ecc2976dc1a Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Forms_EuiSwitch_Kitchen_Sink_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiCheckboxGroup_High_Contrast.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiCheckboxGroup_High_Contrast.png new file mode 100644 index 00000000000..ff7ed836818 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiCheckboxGroup_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiCheckboxGroup_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiCheckboxGroup_Playground.png index 5321036ba3c..d6de3f52bd0 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiCheckboxGroup_Playground.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiCheckboxGroup_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Non_Large_Display.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Non_Large_Display.png index 9086ed619f9..8315ba91a1b 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Non_Large_Display.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Non_Large_Display.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Playground.png index 55ea3f9966d..2851ddfcec2 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Playground.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiFilePicker_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast.png new file mode 100644 index 00000000000..2b78c91ef44 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast_Dark_Mode.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast_Dark_Mode.png new file mode 100644 index 00000000000..7067ec49a41 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_High_Contrast_Dark_Mode.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_Kitchen_Sink.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_Kitchen_Sink.png index 338bbf80b3d..9258b2e3400 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_Kitchen_Sink.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayoutDelimited_Kitchen_Sink.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_Append_Prepend.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_Append_Prepend.png index 23ef9c369cf..27cff65facc 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_Append_Prepend.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_Append_Prepend.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_High_Contrast.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_High_Contrast.png new file mode 100644 index 00000000000..d07a2aca101 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_High_Contrast_Dark_Mode.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_High_Contrast_Dark_Mode.png new file mode 100644 index 00000000000..1c0cdd2413f Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiForm_EuiFormControlLayout_High_Contrast_Dark_Mode.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRadioGroup_High_Contrast.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRadioGroup_High_Contrast.png new file mode 100644 index 00000000000..7ead185e985 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRadioGroup_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRadioGroup_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRadioGroup_Playground.png index 86ac51459b6..d360573b499 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRadioGroup_Playground.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiRadioGroup_Playground.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSwitch_Kitchen_Sink_High_Contrast.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSwitch_Kitchen_Sink_High_Contrast.png new file mode 100644 index 00000000000..f08d03583de Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSwitch_Kitchen_Sink_High_Contrast.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSwitch_Playground.png b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSwitch_Playground.png index 6941fed5635..b6f5aea82d4 100644 Binary files a/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSwitch_Playground.png and b/packages/eui/.loki/reference/chrome_mobile_Forms_EuiSwitch_Playground.png differ diff --git a/packages/eui/src/components/card/checkable_card/__snapshots__/checkable_card.test.tsx.snap b/packages/eui/src/components/card/checkable_card/__snapshots__/checkable_card.test.tsx.snap index 138937d7811..55547d8d909 100644 --- a/packages/eui/src/components/card/checkable_card/__snapshots__/checkable_card.test.tsx.snap +++ b/packages/eui/src/components/card/checkable_card/__snapshots__/checkable_card.test.tsx.snap @@ -17,7 +17,7 @@ exports[`EuiCheckableCard is rendered 1`] = ` > { color: ${euiTheme.colors.text}; ${logicalCSS( 'border-bottom', - `${euiTheme.border.width.thin} solid ${euiTheme.colors.lightestShade}` + `${euiTheme.border.width.thin} solid ${transparentize( + euiTheme.border.color, + 0.4 + )}` )} &:hover { diff --git a/packages/eui/src/components/form/checkbox/checkbox_group.stories.tsx b/packages/eui/src/components/form/checkbox/checkbox_group.stories.tsx index 28b8a177620..9f97f3d83f3 100644 --- a/packages/eui/src/components/form/checkbox/checkbox_group.stories.tsx +++ b/packages/eui/src/components/form/checkbox/checkbox_group.stories.tsx @@ -44,3 +44,9 @@ export const Playground: Story = { }, }, }; + +export const HighContrast: Story = { + ...Playground, + tags: ['vrt-only'], + globals: { highContrastMode: true }, +}; diff --git a/packages/eui/src/components/form/file_picker/file_picker.styles.ts b/packages/eui/src/components/form/file_picker/file_picker.styles.ts index 59a9b08cf29..37180266cb1 100644 --- a/packages/eui/src/components/form/file_picker/file_picker.styles.ts +++ b/packages/eui/src/components/form/file_picker/file_picker.styles.ts @@ -98,7 +98,7 @@ export const euiFilePickerStyles = (euiThemeContext: UseEuiTheme) => { ${euiTextTruncate()} color: ${euiTheme.colors.text}; border: ${euiTheme.border.width.thick} dashed - var(--euiFormControlStateColor, ${euiTheme.colors.lightShade}); + var(--euiFormControlStateColor, ${euiTheme.border.color}); ${euiCanAnimate} { transition: border-color ${euiTheme.animation.fast} ease-in, diff --git a/packages/eui/src/components/form/form.styles.test.tsx b/packages/eui/src/components/form/form.styles.test.tsx index 57af8d78585..c2770916b66 100644 --- a/packages/eui/src/components/form/form.styles.test.tsx +++ b/packages/eui/src/components/form/form.styles.test.tsx @@ -54,6 +54,7 @@ describe('euiFormVariables', () => { "iconAffordance": "24px", "iconCompressedAffordance": "18px", "maxWidth": "400px", + "stateUnderlineHeight": "2px", "textColor": "#343741", } `); @@ -123,11 +124,11 @@ describe('euiFormControlStyles', () => { ", "focus": " - --euiFormControlStateColor: #07C; - background-color: #FFF; - background-size: 100% 100%; - outline: none; /* Remove all outlines and rely on our own bottom border gradient */ - ", + --euiFormControlStateColor: #07C; + background-color: #FFF; + background-size: 100% 100%; + outline: none; /* Remove all outlines and rely on our own bottom border gradient */ + ", "formWidth": " max-inline-size: 400px; inline-size: 100%; @@ -142,9 +143,9 @@ describe('euiFormControlStyles', () => { border-radius: 0; ", "invalid": " - --euiFormControlStateColor: #BD271E; - background-size: 100% 100%; - ", + --euiFormControlStateColor: #BD271E; + background-size: 100% 100%; + ", "readOnly": " cursor: default; color: #343741; @@ -152,6 +153,7 @@ describe('euiFormControlStyles', () => { background-color: #FFF; --euiFormControlStateColor: transparent; + ", "shared": " @@ -224,26 +226,40 @@ describe('euiFormCustomControlStyles', () => { "input": { "disabled": { "selected": " - label: disabled; - color: #69707D; - background-color: #D3DAE6; - ", + + label: disabled; + cursor: not-allowed; + background-color: #D3DAE6; + border-color: #D3DAE6; + + color: #69707D; + ", + "shared": " + label: disabled; + cursor: not-allowed; + background-color: #D3DAE6; + border-color: #D3DAE6; + ", "unselected": " - label: disabled; - color: #D3DAE6; - background-color: #D3DAE6; - cursor: not-allowed; - ", + + label: disabled; + cursor: not-allowed; + background-color: #D3DAE6; + border-color: #D3DAE6; + + color: #D3DAE6; + ", }, "enabled": { "selected": " color: #FFF; background-color: #07C; + border-color: #07C; ", "unselected": " color: transparent; background-color: #FFF; - border: 1px solid #919296; + border-color: #919296; &:has(input:focus) { border-color: #07C; @@ -257,6 +273,8 @@ describe('euiFormCustomControlStyles', () => { display: flex; justify-content: center; align-items: center; + /* For Windows high contrast themes, a border must always be rendered, not just a background */ + border: 1px solid transparent; &:has(input:focus-visible) { outline: 2px solid #07C; diff --git a/packages/eui/src/components/form/form.styles.ts b/packages/eui/src/components/form/form.styles.ts index df283452f0d..9bd603ea576 100644 --- a/packages/eui/src/components/form/form.styles.ts +++ b/packages/eui/src/components/form/form.styles.ts @@ -29,9 +29,11 @@ export const euiFormMaxWidth = ({ euiTheme }: UseEuiTheme) => mathWithUnits(euiTheme.size.base, (x) => x * 25); export const euiFormVariables = (euiThemeContext: UseEuiTheme) => { - const { euiTheme, colorMode } = euiThemeContext; + const { euiTheme, colorMode, highContrastMode } = euiThemeContext; const isColorDark = colorMode === 'DARK'; - const backgroundColor = isColorDark + const backgroundColor = highContrastMode + ? euiTheme.colors.emptyShade + : isColorDark ? shade(euiTheme.colors.lightestShade, 0.4) : tint(euiTheme.colors.lightestShade, 0.6); @@ -48,6 +50,9 @@ export const euiFormVariables = (euiThemeContext: UseEuiTheme) => { controlCompressedBorderRadius: euiTheme.border.radius.small, iconAffordance: mathWithUnits(euiTheme.size.base, (x) => x * 1.5), iconCompressedAffordance: mathWithUnits(euiTheme.size.m, (x) => x * 1.5), + stateUnderlineHeight: highContrastMode + ? mathWithUnits(euiTheme.border.width.thick, (x) => x * 2) + : euiTheme.border.width.thick, }; const colors = { @@ -55,12 +60,14 @@ export const euiFormVariables = (euiThemeContext: UseEuiTheme) => { backgroundColor: backgroundColor, backgroundDisabledColor: darken(euiTheme.colors.lightestShade, 0.05), backgroundReadOnlyColor: euiTheme.colors.emptyShade, - borderColor: transparentize( - colorMode === 'DARK' - ? euiTheme.colors.ghost - : darken(euiTheme.border.color, 4), - 0.1 - ), + borderColor: highContrastMode + ? euiTheme.border.color + : transparentize( + colorMode === 'DARK' + ? euiTheme.colors.ghost + : darken(euiTheme.border.color, 4), + 0.1 + ), controlDisabledColor: euiTheme.colors.mediumShade, controlBoxShadow: '0 0 transparent', controlPlaceholderText: makeHighContrastColor(euiTheme.colors.subduedText)( @@ -100,6 +107,7 @@ export const euiFormVariables = (euiThemeContext: UseEuiTheme) => { }; export const euiFormControlStyles = (euiThemeContext: UseEuiTheme) => { + const { highContrastMode } = euiThemeContext; const form = euiFormVariables(euiThemeContext); return { @@ -139,7 +147,7 @@ export const euiFormControlStyles = (euiThemeContext: UseEuiTheme) => { // In group inGroup: ` ${logicalCSS('height', '100%')} - box-shadow: none; + ${highContrastMode ? 'border: none' : 'box-shadow: none'}; border-radius: 0; `, @@ -193,28 +201,40 @@ export const euiFormControlDefaultShadow = ( withBackgroundAnimation?: boolean; } = {} ) => { - const { euiTheme } = euiThemeContext; + const { euiTheme, highContrastMode } = euiThemeContext; const form = euiFormVariables(euiThemeContext); // We use inset box-shadow instead of border to skip extra height calculations - const border = ` + const border = !highContrastMode + ? ` border: none; box-shadow: inset 0 0 0 ${euiTheme.border.width.thin} ${form.borderColor}; - `.trim(); + `.trim() + : // In high contrast mode, this doesn't matter - we need to prioritize visibility + `border: ${euiTheme.border.width.thin} solid ${euiTheme.border.color};`; const backgroundColor = ` background-color: ${form.backgroundColor}; `.trim(); - const backgroundGradient = ` + const backgroundGradient = + // Windows high contrast mode overrides/hides background gradients - we'll need another approach + highContrastMode !== 'forced' + ? ` background-repeat: no-repeat; background-size: 0% 100%; background-image: linear-gradient(to top, var(--euiFormControlStateColor), - var(--euiFormControlStateColor) ${euiTheme.border.width.thick}, - transparent ${euiTheme.border.width.thick}, + var(--euiFormControlStateColor) ${form.stateUnderlineHeight}, + transparent ${form.stateUnderlineHeight}, transparent 100% ); + `.trim() + : ` + background-repeat: no-repeat; + background-size: 0% ${form.stateUnderlineHeight}; + background-position: bottom left; + background-origin: border-box; `.trim(); const backgroundAnimation = ` @@ -234,24 +254,29 @@ export const euiFormControlDefaultShadow = ( `; }; -export const euiFormControlFocusStyles = ({ - euiTheme, - colorMode, -}: UseEuiTheme) => ` - --euiFormControlStateColor: ${euiTheme.colors.primary}; - background-color: ${ +export const euiFormControlFocusStyles = (euiThemeContext: UseEuiTheme) => { + const { euiTheme, colorMode } = euiThemeContext; + const focusColor = euiTheme.colors.primary; + const backgroundColor = colorMode === 'DARK' ? shade(euiTheme.colors.emptyShade, 0.4) - : euiTheme.colors.emptyShade - }; - background-size: 100% 100%; - outline: none; /* Remove all outlines and rely on our own bottom border gradient */ -`; + : euiTheme.colors.emptyShade; + return ` + --euiFormControlStateColor: ${focusColor}; + background-color: ${backgroundColor}; + ${euiFormControlShowBackgroundUnderline(euiThemeContext, focusColor)} + outline: none; /* Remove all outlines and rely on our own bottom border gradient */ + `; +}; -export const euiFormControlInvalidStyles = ({ euiTheme }: UseEuiTheme) => ` - --euiFormControlStateColor: ${euiTheme.colors.danger}; - background-size: 100% 100%; -`; +export const euiFormControlInvalidStyles = (euiThemeContext: UseEuiTheme) => { + const { euiTheme } = euiThemeContext; + const invalidColor = euiTheme.colors.danger; + return ` + --euiFormControlStateColor: ${invalidColor}; + ${euiFormControlShowBackgroundUnderline(euiThemeContext, invalidColor)} + `; +}; export const euiFormControlDisabledStyles = (euiThemeContext: UseEuiTheme) => { const form = euiFormVariables(euiThemeContext); @@ -273,6 +298,7 @@ export const euiFormControlDisabledStyles = (euiThemeContext: UseEuiTheme) => { export const euiFormControlReadOnlyStyles = (euiThemeContext: UseEuiTheme) => { const form = euiFormVariables(euiThemeContext); + const { highContrastMode } = euiThemeContext; return ` cursor: default; @@ -281,6 +307,7 @@ export const euiFormControlReadOnlyStyles = (euiThemeContext: UseEuiTheme) => { background-color: ${form.backgroundReadOnlyColor}; --euiFormControlStateColor: transparent; + ${highContrastMode === 'forced' ? 'background-image: none;' : ''} `; }; @@ -319,6 +346,27 @@ export const euiFormControlAutoFillStyles = (euiThemeContext: UseEuiTheme) => { `; }; +const euiFormControlShowBackgroundUnderline = ( + euiThemeContext: UseEuiTheme, + color: string +) => { + if (euiThemeContext.highContrastMode !== 'forced') { + return 'background-size: 100% 100%;'; + } + + // Windows high contrast themes ignore all background-images that aren't url-based, + // so to restore the linear-gradient that provides important visual information, we're + // using a static inline SVG workaround + const fill = encodeURIComponent(color); + const inlineSVG = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='${fill}' /%3E%3C/svg%3E`; + + const { stateUnderlineHeight } = euiFormVariables(euiThemeContext); + return ` + background-size: 100% ${stateUnderlineHeight}; + background-image: url("${inlineSVG}"); + `; +}; + const euiPlaceholderPerBrowser = (content: string) => ` &::-webkit-input-placeholder { ${content} } &::-moz-placeholder { ${content} } @@ -332,7 +380,7 @@ const euiPlaceholderPerBrowser = (content: string) => ` */ export const euiFormCustomControlVariables = (euiThemeContext: UseEuiTheme) => { - const { euiTheme, colorMode } = euiThemeContext; + const { euiTheme, colorMode, highContrastMode } = euiThemeContext; const sizes = { control: euiTheme.size.base, @@ -342,10 +390,11 @@ export const euiFormCustomControlVariables = (euiThemeContext: UseEuiTheme) => { const colors = { unselected: euiTheme.colors.emptyShade, - unselectedBorder: - colorMode === 'DARK' - ? tint(euiTheme.colors.lightestShade, 0.31) // WCAG AA requirements - : shade(euiTheme.colors.lightestShade, 0.4), + unselectedBorder: highContrastMode + ? euiTheme.border.color + : colorMode === 'DARK' + ? tint(euiTheme.colors.lightestShade, 0.31) // WCAG AA requirements + : shade(euiTheme.colors.lightestShade, 0.4), selected: euiTheme.colors.primary, selectedIcon: euiTheme.colors.emptyShade, disabled: euiTheme.colors.lightShade, @@ -366,7 +415,7 @@ export const euiFormCustomControlVariables = (euiThemeContext: UseEuiTheme) => { }; export const euiFormCustomControlStyles = (euiThemeContext: UseEuiTheme) => { - const { euiTheme } = euiThemeContext; + const { euiTheme, highContrastMode } = euiThemeContext; const controlVars = euiFormCustomControlVariables(euiThemeContext); const centerWithLabel = mathWithUnits( @@ -387,6 +436,8 @@ export const euiFormCustomControlStyles = (euiThemeContext: UseEuiTheme) => { display: flex; justify-content: center; align-items: center; + /* For Windows high contrast themes, a border must always be rendered, not just a background */ + border: ${euiTheme.border.width.thin} solid transparent; &:has(input:focus-visible) { outline: ${euiTheme.focus.width} solid ${controlVars.colors.selected}; @@ -409,11 +460,12 @@ export const euiFormCustomControlStyles = (euiThemeContext: UseEuiTheme) => { selected: ` color: ${controlVars.colors.selectedIcon}; background-color: ${controlVars.colors.selected}; + border-color: ${controlVars.colors.selected}; `, unselected: ` color: transparent; background-color: ${controlVars.colors.unselected}; - border: ${euiTheme.border.width.thin} solid ${controlVars.colors.unselectedBorder}; + border-color: ${controlVars.colors.unselectedBorder}; &:has(input:focus) { border-color: ${controlVars.colors.selected}; @@ -421,17 +473,29 @@ export const euiFormCustomControlStyles = (euiThemeContext: UseEuiTheme) => { `, }, disabled: { - selected: ` - label: disabled; - color: ${controlVars.colors.disabledIcon}; - background-color: ${controlVars.colors.disabled}; - `, - unselected: ` - label: disabled; - color: ${controlVars.colors.disabled}; - background-color: ${controlVars.colors.disabled}; - cursor: not-allowed; - `, + get shared() { + const borderColor = highContrastMode + ? controlVars.colors.disabledIcon + : controlVars.colors.disabled; + return ` + label: disabled; + cursor: not-allowed; + background-color: ${controlVars.colors.disabled}; + border-color: ${borderColor}; + `; + }, + get selected() { + return ` + ${this.shared} + color: ${controlVars.colors.disabledIcon}; + `; + }, + get unselected() { + return ` + ${this.shared} + color: ${controlVars.colors.disabled}; + `; + }, }, // Looks better centered at different zoom levels than just diff --git a/packages/eui/src/components/form/form_control_layout/form_control_layout.stories.tsx b/packages/eui/src/components/form/form_control_layout/form_control_layout.stories.tsx index 405f8031d82..e1841dcd668 100644 --- a/packages/eui/src/components/form/form_control_layout/form_control_layout.stories.tsx +++ b/packages/eui/src/components/form/form_control_layout/form_control_layout.stories.tsx @@ -131,6 +131,7 @@ export const AppendPrepend: Story = { Tooltip } + autoFocus /> { export const euiFormControlLayoutSideNodeStyles = ( euiThemeContext: UseEuiTheme ) => { - const { euiTheme } = euiThemeContext; + const { euiTheme, highContrastMode } = euiThemeContext; const form = euiFormVariables(euiThemeContext); const uncompressedHeight = mathWithUnits( @@ -141,8 +141,14 @@ export const euiFormControlLayoutSideNodeStyles = ( ${logicalCSS('padding-right', euiTheme.size.s)} } `, - append: css``, - prepend: css``, + append: css` + ${highContrastMode ? logicalCSS('border-left', euiTheme.border.thin) : ''} + `, + prepend: css` + ${highContrastMode + ? logicalCSS('border-right', euiTheme.border.thin) + : ''} + `, uncompressed: ` ${text} { ${logicalCSS('padding-horizontal', euiTheme.size.xs)} diff --git a/packages/eui/src/components/form/form_control_layout/form_control_layout_clear_button.styles.ts b/packages/eui/src/components/form/form_control_layout/form_control_layout_clear_button.styles.ts index 4da1965775f..0e448e18ba5 100644 --- a/packages/eui/src/components/form/form_control_layout/form_control_layout_clear_button.styles.ts +++ b/packages/eui/src/components/form/form_control_layout/form_control_layout_clear_button.styles.ts @@ -8,22 +8,29 @@ import { css } from '@emotion/react'; +import { logicalSizeCSS, mathWithUnits } from '../../../global_styling'; import { UseEuiTheme } from '../../../services'; export const EuiFormControlLayoutClearButtonStyles = ({ euiTheme, colorMode, + highContrastMode, }: UseEuiTheme) => { const backgroundColor = - colorMode === 'DARK' + colorMode === 'DARK' || highContrastMode // mediumShade is not sufficient WCAG contrast ? euiTheme.colors.darkShade : euiTheme.colors.mediumShade; + return { euiFormControlLayoutClearButton: css` pointer-events: all; - background-color: ${backgroundColor}; + display: flex; + justify-content: center; + align-items: center; border-radius: 50%; - line-height: 0; /* ensures the icon stays vertically centered */ + /* Windows high contrast themes ignore background-color, so we use border to color the button instead for better support */ + border-style: solid; + border-color: ${backgroundColor}; &:disabled { cursor: not-allowed; @@ -31,18 +38,31 @@ export const EuiFormControlLayoutClearButtonStyles = ({ } `, - euiFormControlLayoutClearButton__icon: css` - transform: scale(0.5); - fill: ${euiTheme.colors.emptyShade}; - stroke: ${euiTheme.colors.emptyShade}; - `, size: { - s: css` - stroke-width: ${euiTheme.size.xs}; + s: ` + ${logicalSizeCSS(euiTheme.size.m)} + border-width: ${mathWithUnits(euiTheme.size.m, (x) => x / 2)}; + `, + m: ` + ${logicalSizeCSS(euiTheme.size.base)} + border-width: ${mathWithUnits(euiTheme.size.base, (x) => x / 2)}; `, - m: css` - stroke-width: ${euiTheme.size.xxs}; + }, + + icon: { + euiFormControlLayoutClearButton__icon: css` + transform: scale(0.5); + fill: ${euiTheme.colors.emptyShade}; + stroke: ${euiTheme.colors.emptyShade}; `, + size: { + s: css` + stroke-width: ${euiTheme.size.xs}; + `, + m: css` + stroke-width: ${euiTheme.size.xxs}; + `, + }, }, }; }; diff --git a/packages/eui/src/components/form/form_control_layout/form_control_layout_clear_button.tsx b/packages/eui/src/components/form/form_control_layout/form_control_layout_clear_button.tsx index 072da96221d..4e37cf77cec 100644 --- a/packages/eui/src/components/form/form_control_layout/form_control_layout_clear_button.tsx +++ b/packages/eui/src/components/form/form_control_layout/form_control_layout_clear_button.tsx @@ -26,9 +26,10 @@ export const EuiFormControlLayoutClearButton: FunctionComponent< > = ({ className, onClick, size = 'm', ...rest }) => { const classes = classNames('euiFormControlLayoutClearButton', className); const styles = useEuiMemoizedStyles(EuiFormControlLayoutClearButtonStyles); + const cssStyles = [styles.euiFormControlLayoutClearButton, styles.size[size]]; const iconStyles = [ - styles.euiFormControlLayoutClearButton__icon, - styles.size[size], + styles.icon.euiFormControlLayoutClearButton__icon, + styles.icon.size[size], ]; const ariaLabel = useEuiI18n( @@ -39,7 +40,7 @@ export const EuiFormControlLayoutClearButton: FunctionComponent< return (