diff --git a/.changeset/shy-peas-count-2.md b/.changeset/shy-peas-count-2.md new file mode 100644 index 0000000000..f304ff3515 --- /dev/null +++ b/.changeset/shy-peas-count-2.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-styles': minor +--- + +Introduced new focus style for text input and select, and added new wrapper element `.focus-control-wrapper`, `.form-range-wrapper`, `.form-select-wrapper`. diff --git a/.changeset/shy-peas-count.md b/.changeset/shy-peas-count.md new file mode 100644 index 0000000000..0c17785184 --- /dev/null +++ b/.changeset/shy-peas-count.md @@ -0,0 +1,5 @@ +--- +'@swisspost/design-system-styles': patch +--- + +Made styling more coherent for forms elements with High contrast mode and focus/hover state. diff --git a/packages/documentation/.storybook/styles/preview.scss b/packages/documentation/.storybook/styles/preview.scss index fb4aeef83d..8e8414de8c 100644 --- a/packages/documentation/.storybook/styles/preview.scss +++ b/packages/documentation/.storybook/styles/preview.scss @@ -70,7 +70,9 @@ // General styles for content a { - @include utilities.focus-style(); + @include utilities.focus-style() { + border-radius: post.$border-radius; + } } // Style buttons related to the current page next to the tile diff --git a/packages/documentation/src/shared/tile/tile.component.scss b/packages/documentation/src/shared/tile/tile.component.scss index fae7f73886..e59f6fa2f7 100644 --- a/packages/documentation/src/shared/tile/tile.component.scss +++ b/packages/documentation/src/shared/tile/tile.component.scss @@ -7,7 +7,9 @@ $tile-rg-size: var(--post-docs-tile-rg-size, calc($tile-size / 1.2)); :host{ a { - @include utilities.focus-style(); + @include utilities.focus-style() { + border-radius: post.$border-radius; + } } .tile { diff --git a/packages/documentation/src/stories/components/datepicker/datepicker-floating-label.sample.html b/packages/documentation/src/stories/components/datepicker/datepicker-floating-label.sample.html index 801d007aec..c6819bb97c 100644 --- a/packages/documentation/src/stories/components/datepicker/datepicker-floating-label.sample.html +++ b/packages/documentation/src/stories/components/datepicker/datepicker-floating-label.sample.html @@ -1,4 +1,4 @@ -
+
+
+
${[control, label, ...contextual].filter(el => el !== null)}
+
+ ${[control, label, ...contextual].filter(el => el !== null)} +
`; } else { - return html` ${[label, control, ...contextual].filter(el => el !== null)} `; + return html`
+ ${[label, control, ...contextual].filter(el => el !== null)} +
`; } } diff --git a/packages/documentation/src/stories/components/forms/select/select.stories.ts b/packages/documentation/src/stories/components/forms/select/select.stories.ts index 0843424c5e..1f3b2f2624 100644 --- a/packages/documentation/src/stories/components/forms/select/select.stories.ts +++ b/packages/documentation/src/stories/components/forms/select/select.stories.ts @@ -278,12 +278,14 @@ const Template: Story = { if (args.floatingLabel) { return html` -
+
${[control, label, ...contextuals].filter(el => el !== null)}
`; } else { - return html` ${[label, control, ...contextuals].filter(el => el !== null)} `; + return html`
+ ${[label, control, ...contextuals].filter(el => el !== null)} +
`; } }, }; diff --git a/packages/documentation/src/stories/components/forms/slider/slider.stories.ts b/packages/documentation/src/stories/components/forms/slider/slider.stories.ts index 41c912e78e..1487e259d0 100644 --- a/packages/documentation/src/stories/components/forms/slider/slider.stories.ts +++ b/packages/documentation/src/stories/components/forms/slider/slider.stories.ts @@ -228,12 +228,18 @@ function render(args: Args, context: StoryContext) { if (args.showValue === 'input') { return html`
-
${[label, control, ...contextual].filter(el => el !== null)}
+
+
+ ${[label, control, ...contextual].filter(el => el !== null)} +
+
${valueElement}
`; } else { - return html` ${[label, control, valueElement, ...contextual].filter(el => el !== null)} `; + return html`
+ ${[label, control, valueElement, ...contextual].filter(el => el !== null)} +
`; } } diff --git a/packages/documentation/src/stories/components/forms/textarea/textarea.stories.ts b/packages/documentation/src/stories/components/forms/textarea/textarea.stories.ts index 12cae94515..182d33fef2 100644 --- a/packages/documentation/src/stories/components/forms/textarea/textarea.stories.ts +++ b/packages/documentation/src/stories/components/forms/textarea/textarea.stories.ts @@ -195,9 +195,14 @@ ${args.textInside ?? nothing}${[control, label, ...contextual].filter(el => el !== null)}
+
+ ${[control, label, ...contextual].filter(el => el !== null)} +
`; - } else return html` ${[label, control, ...contextual].filter(el => el !== null)} `; + } else + return html`
+ ${[label, control, ...contextual].filter(el => el !== null)} +
`; } export const Default: Story = {}; diff --git a/packages/documentation/src/stories/components/typeahead/typeahead-basic.sample.html b/packages/documentation/src/stories/components/typeahead/typeahead-basic.sample.html index 053bde3e8a..382e9effe3 100644 --- a/packages/documentation/src/stories/components/typeahead/typeahead-basic.sample.html +++ b/packages/documentation/src/stories/components/typeahead/typeahead-basic.sample.html @@ -1,4 +1,4 @@ -
+
+
html` - - +
+ + +
`, }; @@ -25,12 +27,16 @@ export const RowSimple: Story = { render: () => html`
- - +
+ + +
- - +
+ + +
`, @@ -40,12 +46,16 @@ export const ColSimple: Story = { render: () => html`
- - +
+ + +
- - +
+ + +
`, @@ -55,12 +65,16 @@ export const CustomWidth: Story = { render: () => html`
- - +
+ + +
- - +
+ + +
`, @@ -69,12 +83,16 @@ export const CustomWidth: Story = { export const VerticalSpacing: Story = { render: () => html`
- - +
+ + +
- - +
+ + +
`, }; @@ -82,12 +100,16 @@ export const VerticalSpacing: Story = { export const Buttons: Story = { render: () => html`
- - +
+ + +
- - +
+ + +
`, }; @@ -96,13 +118,13 @@ export const FloatingLabels: Story = { render: () => html`
-
+
-
+
@@ -117,7 +139,7 @@ export const Validation: Story = {
-
+
-
+
-
+

Error message

-
+
-
+

Error message

-
+

Success message (optional)

@@ -215,7 +237,7 @@ export const Validation: Story = {
-
+
- - -

Error message

+
+ + +

Error message

+
- - -

Success message (optional)

+
+ + +

Success message (optional)

+
@@ -332,7 +358,7 @@ Value html` -
+
.form-range) { +.form-range-wrapper { @include utilities.focus-style() { - border-radius: commons.$border-radius !important; + border-radius: commons.$border-radius; } } -@supports not selector(:has(> .form-range)) { +:not(.form-range-wrapper) > { .form-range { @include utilities.focus-style() { - border-radius: commons.$border-radius !important; + border-radius: commons.$border-radius; } } } @@ -87,8 +87,11 @@ $webkit-thumb-width: 32px; } } - @include utilities.focus-style-custom('::-moz-range-thumb') { + &:focus::-moz-range-thumb { box-shadow: none; // Remove default style + } + + @include utilities.focus-style-custom('::-moz-range-thumb') { outline: none; } } @@ -149,13 +152,29 @@ $webkit-thumb-width: 32px; background-color: ButtonFace; border-color: ButtonText; } + + &:hover { + &::-webkit-slider-thumb { + border-color: Highlight; + } + + &::-moz-range-thumb { + border-color: Highlight; + } + } } &:disabled, &.disabled { + &::-moz-range-progress { + background-color: GrayText !important; + } + &::-webkit-slider-thumb { background-color: ButtonFace; border-color: GrayText; + + box-shadow: calc(-100vw - $webkit-thumb-width) 0 0 100vw GrayText !important; } &::-moz-range-thumb { diff --git a/packages/styles/src/components/form-select.scss b/packages/styles/src/components/form-select.scss index acf709a07d..b2ce1812e1 100644 --- a/packages/styles/src/components/form-select.scss +++ b/packages/styles/src/components/form-select.scss @@ -6,17 +6,59 @@ @use './../mixins/forms' as forms-mixins; @use './../mixins/utilities'; +@use './../variables/commons'; @use './../variables/components/forms'; @use './../variables/components/form-select' as form-select; +.form-select-wrapper { + @include utilities.focus-style() { + border-radius: commons.$border-radius; + } +} + +:not(.form-select-wrapper) > { + .form-select { + @include utilities.focus-style() { + border-radius: commons.$border-radius; + } + } +} + .form-select { --form-select-indicator: #{form-select.$form-select-indicator}; --form-select-indicator-success: #{b.escape-svg(form-select.$form-select-indicator-success)}; --form-select-indicator-error: #{b.escape-svg(form-select.$form-select-indicator-error)}; + &:focus { + box-shadow: none; // Override bootstrap + + @include utilities.high-contrast-mode { + border-color: FieldText !important; + } + } + &:not(:disabled) { - &:hover { + &:hover, + &:focus { border-color: var(--post-contrast-color); + + @include utilities.high-contrast-mode { + border-color: FieldText; + } + } + + &:hover { + @include utilities.high-contrast-mode { + border-color: Highlight; + } + } + } + + &:disabled { + @include utilities.high-contrast-mode { + border-color: GrayText !important; + background-color: Field !important; + color: GrayText; } } @@ -91,11 +133,6 @@ &:focus-visible { color: FieldText; } - - &:focus, - &:hover:not(:disabled) { - border-color: Highlight; - } } } diff --git a/packages/styles/src/components/form-validation.scss b/packages/styles/src/components/form-validation.scss index 6969c2e9ba..a43207e9c0 100644 --- a/packages/styles/src/components/form-validation.scss +++ b/packages/styles/src/components/form-validation.scss @@ -23,9 +23,7 @@ font-size: form-validation.$form-feedback-font-size; @include utilities-mx.high-contrast-mode() { - color: MarkText !important; - background-color: Mark !important; - forced-color-adjust: none; + padding-inline: 0; } } @@ -102,37 +100,41 @@ .form-select { &.is-invalid { border-color: form-validation.$form-feedback-invalid-color; - background-image: b.escape-svg(form-validation.$form-feedback-icon-invalid); - - @include utilities-mx.high-contrast-mode() { - background-image: b.escape-svg(form-validation.$form-feedback-icon-invalid-hcm-dark); - - @media (prefers-color-scheme: light) { - background-image: b.escape-svg(form-validation.$form-feedback-icon-invalid-hcm-light); - } - } &:focus { border-color: form-validation.$form-feedback-invalid-color; - box-shadow: form-validation.$form-feedback-invalid-box-shadow; } - } - &.is-valid { - border-color: var(--post-success); - background-image: b.escape-svg(form-validation.$form-feedback-icon-valid); + &:not(&[type='file']) { + background-image: b.escape-svg(form-validation.$form-feedback-icon-invalid); - @include utilities-mx.high-contrast-mode() { - background-image: b.escape-svg(form-validation.$form-feedback-icon-valid-hcm-dark); + @include utilities-mx.high-contrast-mode() { + background-image: b.escape-svg(form-validation.$form-feedback-icon-invalid-hcm-dark); - @media (prefers-color-scheme: light) { - background-image: b.escape-svg(form-validation.$form-feedback-icon-valid-hcm-light); + @media (prefers-color-scheme: light) { + background-image: b.escape-svg(form-validation.$form-feedback-icon-invalid-hcm-light); + } } } + } + + &.is-valid { + border-color: var(--post-success); &:focus { border-color: form-validation.$form-feedback-valid-color; - box-shadow: form-validation.$form-feedback-valid-box-shadow; + } + + &:not(&[type='file']) { + background-image: b.escape-svg(form-validation.$form-feedback-icon-valid); + + @include utilities-mx.high-contrast-mode() { + background-image: b.escape-svg(form-validation.$form-feedback-icon-valid-hcm-dark); + + @media (prefers-color-scheme: light) { + background-image: b.escape-svg(form-validation.$form-feedback-icon-valid-hcm-light); + } + } } } @@ -161,10 +163,6 @@ @include form-validation-mx.form-validation-state-selector(invalid) { border-color: form-validation.$form-feedback-invalid-color; - &:focus { - box-shadow: form-validation.$form-feedback-invalid-box-shadow; - } - ~ .form-check-label { color: form-validation.$form-feedback-invalid-color; } @@ -176,5 +174,9 @@ color: color.$white; } } + + @include utilities-mx.high-contrast-mode { + border-color: FieldText; // For blink-browser as otherwise the invalid color will be converted to Highlight + } } } diff --git a/packages/styles/src/components/forms.scss b/packages/styles/src/components/forms.scss index 9e3feb498f..f9aecc81a1 100644 --- a/packages/styles/src/components/forms.scss +++ b/packages/styles/src/components/forms.scss @@ -2,6 +2,7 @@ @use './../lic/bootstrap-license'; +@use './../variables/commons'; @use './../variables/components/forms'; @use './../mixins/utilities'; @@ -28,11 +29,54 @@ select.form-control-rg:not([size]):not([multiple]) { height: forms.$input-height-rg; } +.form-control-wrapper { + @include utilities.focus-style() { + border-radius: commons.$border-radius; + } +} + +:not(.form-control-wrapper) > { + .form-control { + @include utilities.focus-style() { + border-radius: commons.$border-radius; + } + } +} + .form-control { position: relative; + &:focus { + box-shadow: none; // Override bootstrap + + @include utilities.high-contrast-mode { + border-color: FieldText !important; // On blink browser valid and invalid border colors are converted to Highlight color + } + } + + &:not(:disabled) { + &:hover, + &:focus { + @include utilities.high-contrast-mode { + border-color: FieldText; + } + } + + &:hover { + @include utilities.high-contrast-mode { + border-color: Highlight; + } + } + } + &:disabled { color: forms.$input-disabled-color; + + @include utilities.high-contrast-mode { + border-color: GrayText !important; + background-color: Field !important; + color: GrayText; + } } &[readonly]:not(:disabled) { @@ -43,6 +87,13 @@ select.form-control-rg:not([size]):not([multiple]) { &[type='file'] { position: relative; + &:disabled { + @include utilities.high-contrast-mode { + color: GrayText; // Needed for the button on blink browser, even if we define it on .form-control:disabled + border-color: GrayText; + } + } + &::file-selector-button { display: block; position: absolute; @@ -133,17 +184,4 @@ select.form-control-rg:not([size]):not([multiple]) { } } } - - @include utilities.high-contrast-mode() { - &:hover, - &:focus { - &:not(:disabled) { - border-color: Highlight; - - &::file-selector-button { - border-left-color: Highlight; - } - } - } - } } diff --git a/packages/styles/src/components/intranet-header/_sidebar.scss b/packages/styles/src/components/intranet-header/_sidebar.scss index 15f427393e..6bc9cbfa81 100644 --- a/packages/styles/src/components/intranet-header/_sidebar.scss +++ b/packages/styles/src/components/intranet-header/_sidebar.scss @@ -1,7 +1,7 @@ @use './../../mixins/utilities'; @use './../../variables/color'; -@use './../../variables/spacing'; @use './../../variables/commons'; +@use './../../variables/spacing'; @use './../../variables/components/intranet-header'; @use './../../themes/bootstrap/core' as *; @@ -27,7 +27,9 @@ background: none; border: 0; - @include utilities.focus-style(); + @include utilities.focus-style() { + border-radius: commons.$border-radius; + } } } diff --git a/packages/styles/src/mixins/_utilities.scss b/packages/styles/src/mixins/_utilities.scss index b28b1d2ae9..a48ebb623e 100644 --- a/packages/styles/src/mixins/_utilities.scss +++ b/packages/styles/src/mixins/_utilities.scss @@ -89,18 +89,18 @@ @mixin focus-style($additional-selector: '') { &#{$additional-selector} { - outline-style: none; - outline-offset: spacing.$size-line; - outline-width: spacing.$size-line; - outline-color: var(--post-focus-color); + outline-style: none !important; // !important is needed to override bootstrap .form-control:focus + outline-offset: spacing.$size-line !important; + outline-width: spacing.$size-line !important; + outline-color: var(--post-focus-color) !important; } // :has(:focus-visible) mimic a focus-visible-within pseudo-class &:is(:focus-visible, :has(:focus-visible), .pretend-focus)#{$additional-selector} { - outline-style: solid; + outline-style: solid !important; @include high-contrast-mode() { - outline-color: Highlight; + outline-color: Highlight !important; } // In case rules need to be slightly adjusted @@ -110,10 +110,10 @@ // When a browser doesn't support :has, use focus-within as a fallback. This means that focus state is displayed on focus and not on focus-visible only (except some browsers like Safari). @supports not selector(:has(:focus-visible)) { &:is(:focus-visible, :focus-within, .pretend-focus)#{$additional-selector} { - outline-style: solid; + outline-style: solid !important; @include high-contrast-mode() { - outline-color: Highlight; + outline-color: Highlight !important; } // In case rules need to be slightly adjusted diff --git a/packages/styles/src/placeholders/_close.scss b/packages/styles/src/placeholders/_close.scss index 19dfbc6c4f..08b424ab08 100644 --- a/packages/styles/src/placeholders/_close.scss +++ b/packages/styles/src/placeholders/_close.scss @@ -6,11 +6,14 @@ @use './../variables/animation'; @use './../variables/color'; +@use './../variables/commons'; @use './../variables/components/close'; %btn-close { @include button-mx.reset-button; - @include utilities-mx.focus-style(); + @include utilities-mx.focus-style() { + border-radius: commons.$border-radius; + } border-radius: close.$close-border-radius; color: close.$close-color; transition: close.$close-transition; diff --git a/packages/styles/src/placeholders/_color.scss b/packages/styles/src/placeholders/_color.scss index 7b9e5e7bed..62b23abbc2 100644 --- a/packages/styles/src/placeholders/_color.scss +++ b/packages/styles/src/placeholders/_color.scss @@ -54,7 +54,7 @@ // dark background variables %color-background-dark-variables { --post-contrast-color: #{color.$white}; - --post-focus-color: #{color.$white}; + --post-focus-color: #{color.$focus-dark}; --post-contrast-color-inverted: #{color.$black}; --post-dark: #{color.$light}; --post-light: #{color.$dark};