diff --git a/packages/web/src/scss/components/TextField/README.md b/packages/web/src/scss/components/TextField/README.md
index 1a289d6dfa..1b9a5a3e15 100644
--- a/packages/web/src/scss/components/TextField/README.md
+++ b/packages/web/src/scss/components/TextField/README.md
@@ -93,7 +93,7 @@ The `size` attribute is supported on inputs of the following types: `email`,
This option is generally recommended for inputs with a limited value length
(e.g. numeric representation of day, month, year). Supported values are `2`, `3`
and `4` (characters). If you need any other value or prefer using `em` unit
-instead of default `ch`, define a `--width` CSS custom property on the ``
+instead of default `ch`, define a `--textfield-input-width` CSS custom property on the ``
element:
```html
@@ -110,7 +110,7 @@ element:
class="TextField__input"
name="sizeEm"
placeholder="Placeholder"
- style="--width: 4em;"
+ style="--textfield-input-width: 4em;"
/>
```
diff --git a/packages/web/src/scss/components/TextField/_TextField.scss b/packages/web/src/scss/components/TextField/_TextField.scss
index f598c6cf11..af116117d4 100644
--- a/packages/web/src/scss/components/TextField/_TextField.scss
+++ b/packages/web/src/scss/components/TextField/_TextField.scss
@@ -1,3 +1,20 @@
+// 1. We pass the input arrows width if the input type is number.
+// 2. If the input has a size attribute, we calculate the width based on the input width, padding, border width and arrows width.
+// 3. We generate the input width based on the number of characters. This is useful if you want to set the width based on the number of characters.
+// 4. We position the password toggle button next to the input and use pseudo-element for border styling.
+// We do this because password managers place their icon buttons inside the password input.
+// 5. Because the button is next to the input, we need to use pseudo element to style the border around both of them.
+// 6. We need to set the same background color for the button when the input is hovered, active or focused.
+// 7. We toggle icons of the password toggle and we disable the no-descending-specificity to make the code more readable.
+// 8. To make the 5. work, we need to style the input and the button when the input is focused.
+// 9. The password toggle button should have its own focus-visible style.
+// 10. Disabled input and its optional password toggle should have the same disabled style.
+// The code is more comprehensible when styling elements in this order:
+// 1. input (with a pseudo-element),
+// 2. password toggle button,
+// 3. validation text.
+// Also, high-specificity selectors are necessary to achieve the desired result.
+
@use '../../settings/cursors';
@use '../../theme/form-fields' as form-fields-theme;
@use '../../tools/form-fields' as form-fields-tools;
@@ -35,20 +52,23 @@ $_field-name: 'TextField';
appearance: none;
}
+ // 1.
&[type='number'] {
- --arrows-width: #{theme.$input-number-arrows-width};
+ --textfield-arrows-width: #{theme.$input-number-arrows-width};
}
+ // 2.
&[size] {
width: calc(
- var(--width) + 2 * #{form-fields-theme.$box-field-input-padding-x} + 2 * #{form-fields-theme.$box-field-input-border-width} +
- var(--arrows-width, 0px)
+ var(--textfield-input-width) + 2 * #{form-fields-theme.$box-field-input-padding-x} + 2 * #{form-fields-theme.$box-field-input-border-width} +
+ var(--textfield-arrows-width, 0px)
);
}
+ // 3.
@each $size in theme.$input-size-characters {
&[size='#{$size}'] {
- --width: #{$size}ch;
+ --textfield-input-width: #{$size}ch;
}
}
}
@@ -70,8 +90,7 @@ $_field-name: 'TextField';
border-bottom-right-radius: 0;
}
-// We position the button next to the input and use pseudo-element for border styling.
-// We do this because password managers place their icon buttons inside the password input.
+// 4.
.TextField__passwordToggle__button {
@include reset.button();
@@ -81,19 +100,30 @@ $_field-name: 'TextField';
padding: theme.$input-password-toggle-padding;
color: form-fields-theme.$box-field-input-color-default;
border-radius: 0 form-fields-theme.$box-field-input-border-radius form-fields-theme.$box-field-input-border-radius 0;
- background-color: form-fields-theme.$box-field-input-background;
+ background-color: form-fields-theme.$input-background-color;
+ // 5.
&::before {
content: '';
position: absolute;
inset: 0;
border: form-fields-theme.$box-field-input-border-width form-fields-theme.$box-field-input-border-style
- form-fields-theme.$box-field-input-border-color-default;
+ form-fields-theme.$input-border-color;
border-radius: form-fields-theme.$box-field-input-border-radius;
pointer-events: none;
}
}
+// 6.
+.TextField__input:hover + .TextField__passwordToggle__button {
+ background-color: form-fields-theme.$input-background-color-hover;
+}
+
+.TextField__input:active + .TextField__passwordToggle__button,
+.TextField__input:focus-within + .TextField__passwordToggle__button {
+ background-color: form-fields-theme.$input-background-color-active;
+}
+
.TextField__passwordToggle__icon {
pointer-events: none;
}
@@ -103,13 +133,13 @@ $_field-name: 'TextField';
display: block;
}
-// stylelint-disable-next-line no-descending-specificity -- In this case ↕️, the code is more readable this way.
+// stylelint-disable-next-line no-descending-specificity -- 7.
.TextField__passwordToggle__icon--shown,
.TextField__passwordToggle__button[aria-pressed='true'] > .TextField__passwordToggle__icon--hidden {
display: none;
}
-// stylelint-disable selector-max-specificity, selector-max-class, selector-max-compound-selectors -- We need high-specificity selectors here.
+// stylelint-disable selector-max-specificity, selector-max-class, selector-max-compound-selectors -- 8.
.TextField > .TextField__input:focus-visible,
.TextField > .TextField__passwordToggle > .TextField__input:focus-visible ~ .TextField__passwordToggle__button::before {
@include form-fields-tools.box-field-focus-visible();
@@ -121,6 +151,7 @@ $_field-name: 'TextField';
outline: 0;
}
+// 9.
.TextField__passwordToggle__button:focus-visible::after {
content: '';
position: absolute;
@@ -153,36 +184,31 @@ $_field-name: 'TextField';
@include form-fields-tools.box-field-disabled-input();
}
-// stylelint-disable no-descending-specificity, selector-max-specificity, selector-max-class, selector-max-compound-selectors
-// -- The code is more comprehensible when styling elements in this order:
-// 1. input (with a pseudo-element),
-// 2. password toggle button,
-// 3. validation text.
-// Also, high-specificity selectors are necessary to achieve the desired result.
+// stylelint-disable no-descending-specificity, selector-max-specificity, selector-max-class, selector-max-compound-selectors -- 10.
.TextField > .TextField__input:disabled,
:is(.TextField--disabled, .TextField.is-disabled) > .TextField__input,
.TextField .TextField__passwordToggle .TextField__input:disabled ~ .TextField__passwordToggle__button::before,
:is(.TextField--disabled, .TextField.is-disabled)
.TextField__passwordToggle
.TextField__passwordToggle__button::before {
- border-color: theme.$input-border-color-disabled;
+ border-color: form-fields-theme.$input-border-color-disabled;
}
.TextField .TextField__input:disabled ~ .TextField__passwordToggle__button,
:is(.TextField--disabled, .TextField.is-disabled) .TextField__passwordToggle__button {
@include form-fields-tools.input-disabled();
- background-color: form-fields-theme.$box-field-input-background-disabled;
+ background-color: form-fields-theme.$input-background-color-disabled;
pointer-events: none;
cursor: cursors.$disabled;
}
// stylelint-enable
+:is(.TextField--disabled, .TextField.is-disabled) > :is(.TextField__helperText) {
+ @include form-fields-tools.helper-text-disabled();
+}
+
:is(.TextField--disabled, .TextField.is-disabled)
> :is(.TextField__validationText, [data-spirit-element='validation_text']) {
@include form-fields-tools.validation-text-disabled();
}
-
-:is(.TextField--disabled, .TextField.is-disabled) > :is(.TextField__helperText) {
- @include form-fields-tools.helper-text-disabled();
-}
diff --git a/packages/web/src/scss/components/TextField/_theme.scss b/packages/web/src/scss/components/TextField/_theme.scss
index cf733cfc77..1686280c7e 100644
--- a/packages/web/src/scss/components/TextField/_theme.scss
+++ b/packages/web/src/scss/components/TextField/_theme.scss
@@ -1,7 +1,7 @@
-@use '@tokens' as tokens;
+@use '@global' as global-tokens;
+@use '../../settings/globals';
-$input-border-color-disabled: tokens.$border-primary-disabled;
$input-number-arrows-width: 1.25rem;
-$input-password-toggle-padding: tokens.$space-400;
-$input-password-toggle-icon-size: tokens.$space-700;
+$input-password-toggle-padding: global-tokens.$space-500;
+$input-password-toggle-icon-size: global-tokens.$space-900;
$input-size-characters: (2, 3, 4);
diff --git a/packages/web/src/scss/components/index.scss b/packages/web/src/scss/components/index.scss
index 4dcbe6460b..91c3233cfb 100644
--- a/packages/web/src/scss/components/index.scss
+++ b/packages/web/src/scss/components/index.scss
@@ -32,7 +32,8 @@
@forward 'Tag';
// @forward 'TextArea';
-// @forward 'TextField';
+@forward 'TextField';
+
// @forward 'Toast';
// @forward 'Tooltip';
@forward 'UNSTABLE_ActionLayout';
diff --git a/packages/web/src/scss/theme/_form-fields.scss b/packages/web/src/scss/theme/_form-fields.scss
index 28913a2a15..61033fd87f 100644
--- a/packages/web/src/scss/theme/_form-fields.scss
+++ b/packages/web/src/scss/theme/_form-fields.scss
@@ -39,24 +39,20 @@ $inline-field-input-border-width: global-tokens.$border-width-200;
$inline-field-input-background-color-checked: var(--#{globals.$prefix}color-selected-default);
// Box field form components – TextField, TextArea, etc.
-// Commented out to allow nice diff
-// $box-field-input-color-default: tokens.$text-primary-default;
-// $box-field-input-border-width: tokens.$border-width-100;
-// $box-field-input-border-style: tokens.$border-style-100;
-// $box-field-input-border-color-default: tokens.$border-primary-default;
-// $box-field-input-border-color-disabled: tokens.$border-primary-disabled;
-// $box-field-input-border-color-focus: tokens.$focus-default;
-//
-// $box-field-input-border-radius: tokens.$radius-100;
-// $box-field-input-background: tokens.$background-basic;
-// $box-field-input-background-disabled: tokens.$background-cover;
-// $box-field-input-placeholder-color-default: tokens.$text-secondary-default;
-// $box-field-input-placeholder-color-disabled: tokens.$text-secondary-disabled;
-// $box-field-input-padding-x: tokens.$space-400;
-// $box-field-input-padding-y: calc(#{tokens.$space-400} - #{tokens.$border-width-100});
-// $box-field-input-width: 18rem;
-// $box-field-label-typography: tokens.$body-small-text-regular;
-// $box-field-label-margin-bottom: tokens.$space-300;
+$box-field-input-color-default: var(--#{globals.$prefix}color-form-field-content);
+$box-field-input-border-width: global-tokens.$border-width-100;
+$box-field-input-border-style: solid;
+$box-field-input-border-color-focus: var(--#{globals.$prefix}color-border-focus);
+$box-field-input-focus-shadow: global-tokens.$focus;
+
+$box-field-input-border-radius: global-tokens.$radius-300;
+$box-field-input-placeholder-color-default: var(--#{globals.$prefix}color-form-field-placeholder);
+$box-field-input-placeholder-color-disabled: var(--#{globals.$prefix}color-disabled-content);
+$box-field-input-padding-x: global-tokens.$space-600;
+$box-field-input-padding-y: calc(#{global-tokens.$space-500} - #{global-tokens.$border-width-100});
+$box-field-input-width: 18rem;
+$box-field-label-typography: global-tokens.$body-small-semibold;
+$box-field-label-margin-bottom: global-tokens.$space-400;
$validation-states: (
success: (
diff --git a/packages/web/src/scss/tools/_form-fields.scss b/packages/web/src/scss/tools/_form-fields.scss
index 472d88ff14..ecaa4684b0 100644
--- a/packages/web/src/scss/tools/_form-fields.scss
+++ b/packages/web/src/scss/tools/_form-fields.scss
@@ -101,14 +101,25 @@
padding: form-fields-theme.$box-field-input-padding-y form-fields-theme.$box-field-input-padding-x;
color: form-fields-theme.$box-field-input-color-default;
border: form-fields-theme.$box-field-input-border-width form-fields-theme.$box-field-input-border-style
- form-fields-theme.$box-field-input-border-color-default;
+ form-fields-theme.$input-border-color;
border-radius: form-fields-theme.$box-field-input-border-radius;
- background: form-fields-theme.$box-field-input-background;
+ background: form-fields-theme.$input-background-color;
&::placeholder {
color: form-fields-theme.$box-field-input-placeholder-color-default;
opacity: 1;
}
+
+ @media (hover: hover) {
+ &:hover {
+ background-color: form-fields-theme.$input-background-color-hover;
+ }
+ }
+
+ &:active,
+ &:focus-within {
+ background-color: form-fields-theme.$input-background-color-active;
+ }
}
@mixin input-field-validation-states($field-name, $shadow-x: null, $shadow-y: null) {
@@ -183,8 +194,8 @@
@mixin box-field-disabled-input() {
@include input-disabled();
- border-color: form-fields-theme.$box-field-input-border-color-disabled;
- background-color: form-fields-theme.$box-field-input-background-disabled;
+ border-color: form-fields-theme.$input-border-color-disabled;
+ background-color: form-fields-theme.$input-background-color-disabled;
&::placeholder {
color: form-fields-theme.$box-field-input-placeholder-color-disabled;
diff --git a/tests/e2e/demo-components-compare.spec.ts b/tests/e2e/demo-components-compare.spec.ts
index 1a4ad3410a..cdc4c1faa7 100644
--- a/tests/e2e/demo-components-compare.spec.ts
+++ b/tests/e2e/demo-components-compare.spec.ts
@@ -22,7 +22,6 @@ const IGNORED_TESTS: string[] = [
'Stack',
'Text',
'TextArea',
- 'TextField',
'Toast',
'Tooltip',
'UNSTABLE_Slider',
diff --git a/tests/e2e/demo-components-compare.spec.ts-snapshots/textfield-chromium-linux.png b/tests/e2e/demo-components-compare.spec.ts-snapshots/textfield-chromium-linux.png
index 582822c32a..0941b6f3c3 100644
Binary files a/tests/e2e/demo-components-compare.spec.ts-snapshots/textfield-chromium-linux.png and b/tests/e2e/demo-components-compare.spec.ts-snapshots/textfield-chromium-linux.png differ