Skip to content

Commit

Permalink
BREAKING CHANGE(web): Switch TextField component to v3 design tokens …
Browse files Browse the repository at this point in the history
…#DS-1447

Add component prefix to component CSS variables.
  • Loading branch information
crishpeen committed Sep 17, 2024
1 parent 22475dc commit e6958c2
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 52 deletions.
4 changes: 2 additions & 2 deletions packages/web/src/scss/components/TextField/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<input>`
instead of default `ch`, define a `--textfield-input-width` CSS custom property on the `<input>`
element:

```html
Expand All @@ -110,7 +110,7 @@ element:
class="TextField__input"
name="sizeEm"
placeholder="Placeholder"
style="--width: 4em;"
style="--textfield-input-width: 4em;"
/>
</div>
```
Expand Down
70 changes: 48 additions & 22 deletions packages/web/src/scss/components/TextField/_TextField.scss
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
}
}
}
Expand All @@ -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();

Expand All @@ -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;
}
Expand All @@ -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();
Expand All @@ -121,6 +151,7 @@ $_field-name: 'TextField';
outline: 0;
}

// 9.
.TextField__passwordToggle__button:focus-visible::after {
content: '';
position: absolute;
Expand Down Expand Up @@ -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();
}
8 changes: 4 additions & 4 deletions packages/web/src/scss/components/TextField/_theme.scss
Original file line number Diff line number Diff line change
@@ -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);
3 changes: 2 additions & 1 deletion packages/web/src/scss/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
@forward 'Tag';

// @forward 'TextArea';
// @forward 'TextField';
@forward 'TextField';

// @forward 'Toast';
// @forward 'Tooltip';
@forward 'UNSTABLE_ActionLayout';
Expand Down
32 changes: 14 additions & 18 deletions packages/web/src/scss/theme/_form-fields.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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: (
Expand Down
19 changes: 15 additions & 4 deletions packages/web/src/scss/tools/_form-fields.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion tests/e2e/demo-components-compare.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ const IGNORED_TESTS: string[] = [
'Stack',
'Text',
'TextArea',
'TextField',
'Toast',
'Tooltip',
'UNSTABLE_Slider',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e6958c2

Please sign in to comment.