Style PasswordField like TextField

Signed-off-by: James Brooks <jamesbrooks@spotify.com>
This commit is contained in:
James Brooks
2026-05-07 08:08:38 +01:00
parent e68cb8ac0f
commit 11699ac31f
4 changed files with 71 additions and 48 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/ui': patch
---
Updated `PasswordField` to visually match `TextField`, including consistent focus rings, error states, disabled appearance, and background colour behaviour.
**Affected components:** PasswordField
+1
View File
@@ -2361,6 +2361,7 @@ export const PasswordFieldDefinition: {
readonly inputIcon: 'bui-PasswordFieldIcon';
readonly inputVisibility: 'bui-PasswordFieldVisibility';
};
readonly bg: 'consumer';
readonly propDefs: {
readonly size: {
readonly dataAttribute: true;
@@ -24,33 +24,38 @@
width: 100%;
flex-shrink: 0;
&[data-size='small'] {
--password-field-item-height: 2rem;
&[data-on-bg='neutral-1'] .bui-PasswordFieldInput {
background-color: var(--bui-bg-neutral-2);
}
&[data-size='medium'] {
--password-field-item-height: 2.5rem;
&[data-on-bg='neutral-2'] .bui-PasswordFieldInput {
background-color: var(--bui-bg-neutral-3);
}
&[data-on-bg='neutral-3'] .bui-PasswordFieldInput {
background-color: var(--bui-bg-neutral-4);
}
}
.bui-PasswordFieldInputWrapper {
display: flex;
align-items: center;
border-radius: var(--bui-radius-2);
border: 1px solid var(--bui-border-2);
background-color: var(--bui-bg-neutral-1);
transition: border-color 0.2s ease-in-out, outline-color 0.2s ease-in-out;
position: relative;
&[data-size='small'] {
&[data-size='small'] .bui-PasswordFieldInput {
height: 2rem;
padding-right: 2rem;
}
&[data-size='medium'] {
&[data-size='medium'] .bui-PasswordFieldInput {
height: 2.5rem;
padding-right: 2.5rem;
}
&:has([data-invalid]) {
border-color: var(--bui-fg-danger);
&[data-size='small'] .bui-PasswordFieldInput[data-icon] {
padding-left: var(--bui-space-8);
}
&[data-size='medium'] .bui-PasswordFieldInput[data-icon] {
padding-left: var(--bui-space-9);
}
&:has([data-disabled]) {
@@ -60,41 +65,47 @@
}
.bui-PasswordFieldIcon {
flex: 0 0 auto;
display: grid;
place-content: center;
position: absolute;
left: var(--bui-space-3);
top: 50%;
transform: translateY(-50%);
color: var(--bui-fg-primary);
flex-shrink: 0;
pointer-events: none;
width: var(--password-field-item-height);
height: var(--password-field-item-height);
& svg {
.bui-PasswordField[data-size='small'] & {
width: 1rem;
height: 1rem;
}
&[data-size='small'],
&[data-size='small'] svg {
width: 1rem;
height: 1rem;
}
.bui-PasswordField[data-size='medium'] & {
width: 1.25rem;
height: 1.25rem;
}
&[data-size='medium'],
&[data-size='medium'] svg {
width: 1.25rem;
height: 1.25rem;
}
}
.bui-PasswordFieldInput {
flex: 1;
display: flex;
align-items: center;
padding: 0;
padding: 0 var(--bui-space-3);
border-radius: var(--bui-radius-2);
border: none;
background-color: transparent;
background-color: var(--bui-bg-neutral-1);
font-size: var(--bui-font-size-3);
font-family: var(--bui-font-regular);
font-weight: var(--bui-font-weight-regular);
color: var(--bui-fg-primary);
transition: box-shadow 0.2s ease-in-out;
width: 100%;
height: 100%;
outline: none;
cursor: inherit;
&[data-focused] {
outline: none;
box-shadow: inset 0 0 0 1px var(--bui-ring);
}
&::-webkit-search-cancel-button,
&::-webkit-search-decoration {
@@ -105,23 +116,20 @@
color: var(--bui-fg-secondary);
}
&[data-disabled] {
cursor: not-allowed;
&[data-invalid] {
box-shadow: inset 0 0 0 1px var(--bui-border-danger);
}
&:first-child {
.bui-PasswordField[data-size='small'] & {
padding-inline: var(--bui-space-3) 0;
}
.bui-PasswordField[data-size='medium'] & {
padding-inline: var(--bui-space-4) 0;
}
&[data-disabled] {
cursor: not-allowed;
}
}
.bui-PasswordFieldVisibility {
flex: 0 0 auto;
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
display: grid;
place-content: center;
background-color: transparent;
@@ -130,16 +138,22 @@
padding: 0;
margin: 0;
color: var(--bui-fg-primary);
width: var(--password-field-item-height);
height: var(--password-field-item-height);
& svg {
.bui-PasswordField[data-size='small'] & {
&[data-size='small'] {
width: 2rem;
height: 2rem;
& svg {
width: 1rem;
height: 1rem;
}
}
.bui-PasswordField[data-size='medium'] & {
&[data-size='medium'] {
width: 2.5rem;
height: 2.5rem;
& svg {
width: 1.25rem;
height: 1.25rem;
}
@@ -32,6 +32,7 @@ export const PasswordFieldDefinition = defineComponent<PasswordFieldOwnProps>()(
inputIcon: 'bui-PasswordFieldIcon',
inputVisibility: 'bui-PasswordFieldVisibility',
},
bg: 'consumer',
propDefs: {
size: { dataAttribute: true, default: 'small' },
className: {},