diff --git a/src/app/shared/ui/form-field/form-field.component.html b/src/app/shared/ui/form-field/form-field.component.html
new file mode 100644
index 0000000..08ef6c5
--- /dev/null
+++ b/src/app/shared/ui/form-field/form-field.component.html
@@ -0,0 +1,23 @@
+
diff --git a/src/app/shared/ui/form-field/form-field.component.scss b/src/app/shared/ui/form-field/form-field.component.scss
new file mode 100644
index 0000000..b04efe7
--- /dev/null
+++ b/src/app/shared/ui/form-field/form-field.component.scss
@@ -0,0 +1,32 @@
+.app-form-field {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-xs, 0.25rem);
+}
+
+.app-form-field__label {
+ font-size: 0.875rem;
+ font-weight: 600;
+ color: var(--text-primary, #1f322d);
+}
+
+.app-form-field__required {
+ color: var(--error-color, #c0392b);
+ margin-left: 0.125rem;
+}
+
+.app-form-field__control {
+ display: block;
+}
+
+.app-form-field__hint {
+ margin: 0;
+ font-size: 0.8125rem;
+ color: var(--text-secondary, #6b7280);
+}
+
+.app-form-field__error {
+ margin: 0;
+ font-size: 0.8125rem;
+ color: var(--error-color, #c0392b);
+}
diff --git a/src/app/shared/ui/form-field/form-field.component.ts b/src/app/shared/ui/form-field/form-field.component.ts
new file mode 100644
index 0000000..6f9dd21
--- /dev/null
+++ b/src/app/shared/ui/form-field/form-field.component.ts
@@ -0,0 +1,21 @@
+import { ChangeDetectionStrategy, Component, input } from '@angular/core';
+
+let nextFormFieldId = 0;
+
+@Component({
+ selector: 'app-form-field',
+ standalone: true,
+ templateUrl: './form-field.component.html',
+ styleUrl: './form-field.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FormFieldComponent {
+ readonly label = input(null);
+ readonly hint = input(null);
+ readonly error = input(null);
+ readonly required = input(false);
+
+ protected readonly fieldId = `app-form-field-${++nextFormFieldId}`;
+ protected readonly hintId = `${this.fieldId}-hint`;
+ protected readonly errorId = `${this.fieldId}-error`;
+}