diff --git a/.changeset/gentle-coats-drum.md b/.changeset/gentle-coats-drum.md new file mode 100644 index 0000000000..a0d6025f54 --- /dev/null +++ b/.changeset/gentle-coats-drum.md @@ -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 diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index ac6c9f13a2..1bd3022cd4 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -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; diff --git a/packages/ui/src/components/PasswordField/PasswordField.module.css b/packages/ui/src/components/PasswordField/PasswordField.module.css index 8ede1212a0..9fdbcd8ef1 100644 --- a/packages/ui/src/components/PasswordField/PasswordField.module.css +++ b/packages/ui/src/components/PasswordField/PasswordField.module.css @@ -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; } diff --git a/packages/ui/src/components/PasswordField/definition.ts b/packages/ui/src/components/PasswordField/definition.ts index 7133969eff..1a3be04c76 100644 --- a/packages/ui/src/components/PasswordField/definition.ts +++ b/packages/ui/src/components/PasswordField/definition.ts @@ -32,6 +32,7 @@ export const PasswordFieldDefinition = defineComponent()( inputIcon: 'bui-PasswordFieldIcon', inputVisibility: 'bui-PasswordFieldVisibility', }, + bg: 'consumer', propDefs: { size: { dataAttribute: true, default: 'small' }, className: {},