+
+
+ );
+ },
+);
+
+PasswordField.displayName = 'PasswordField';
diff --git a/packages/ui/src/components/PasswordField/index.ts b/packages/ui/src/components/PasswordField/index.ts
new file mode 100644
index 0000000000..df7874e1c2
--- /dev/null
+++ b/packages/ui/src/components/PasswordField/index.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export * from './PasswordField';
+export * from './types';
diff --git a/packages/ui/src/components/PasswordField/types.ts b/packages/ui/src/components/PasswordField/types.ts
new file mode 100644
index 0000000000..8af9a6134c
--- /dev/null
+++ b/packages/ui/src/components/PasswordField/types.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2025 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { TextFieldProps as AriaTextFieldProps } from 'react-aria-components';
+import { ReactNode } from 'react';
+import type { Breakpoint } from '../../types';
+import type { FieldLabelProps } from '../FieldLabel/types';
+
+/** @public */
+export interface PasswordFieldProps
+ extends AriaTextFieldProps,
+ Omit {
+ /**
+ * An icon to render before the input
+ */
+ icon?: ReactNode;
+
+ /**
+ * The size of the password field
+ * @defaultValue 'medium'
+ */
+ size?: 'small' | 'medium' | Partial>;
+
+ /**
+ * Text to display in the input when it has no value
+ */
+ placeholder?: string;
+}
diff --git a/packages/ui/src/components/TextField/types.ts b/packages/ui/src/components/TextField/types.ts
index 75a5c68aaa..b971ef8d26 100644
--- a/packages/ui/src/components/TextField/types.ts
+++ b/packages/ui/src/components/TextField/types.ts
@@ -23,6 +23,14 @@ import type { FieldLabelProps } from '../FieldLabel/types';
export interface TextFieldProps
extends AriaTextFieldProps,
Omit {
+ /**
+ * The HTML input type for the text field
+ *
+ * @remarks
+ * Use `SearchField` for
+ * search inputs and `PasswordField` for password inputs.
+ */
+ type?: 'text' | 'email' | 'tel' | 'url';
/**
* An icon to render before the input
*/
diff --git a/packages/ui/src/css/components.css b/packages/ui/src/css/components.css
index 8aa81a8217..9696425431 100644
--- a/packages/ui/src/css/components.css
+++ b/packages/ui/src/css/components.css
@@ -39,6 +39,7 @@
@import '../components/TagGroup/TagGroup.styles.css';
@import '../components/Text/styles.css';
@import '../components/TextField/TextField.styles.css';
+@import '../components/PasswordField/PasswordField.styles.css';
@import '../components/SearchField/SearchField.styles.css';
@import '../components/Skeleton/Skeleton.styles.css';
@import '../components/Tooltip/Tooltip.styles.css';
diff --git a/packages/ui/src/utils/componentDefinitions.ts b/packages/ui/src/utils/componentDefinitions.ts
index 5a0e8bdc1a..931bfacca7 100644
--- a/packages/ui/src/utils/componentDefinitions.ts
+++ b/packages/ui/src/utils/componentDefinitions.ts
@@ -174,6 +174,12 @@ export const componentDefinitions = {
emptyState: 'bui-MenuEmptyState',
},
},
+ PasswordField: {
+ classNames: {
+ root: 'bui-PasswordField',
+ inputVisibility: 'bui-InputVisibility',
+ },
+ },
Popover: {
classNames: {
root: 'bui-Popover',
@@ -286,6 +292,7 @@ export const componentDefinitions = {
inputWrapper: 'bui-InputWrapper',
input: 'bui-Input',
inputIcon: 'bui-InputIcon',
+ inputAction: 'bui-InputAction',
},
dataAttributes: {
invalid: [true, false] as const,