feat: add form validation and accessibility improvements

- Add required prop to FormInput with red asterisk indicator
- Make all form fields required (name, company, email, message)
- Add aria-label and aria-required attributes to inputs
- Add aria-label to all buttons (Back, Submit, Learn More)
- Add role='group' to button container
- Fix logo size inconsistency (55px → 56px)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
openhands
2026-03-19 16:11:29 +00:00
parent 9ab08db6a1
commit 48d330f8b9
3 changed files with 19 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ interface FormInputProps {
placeholder?: string;
type?: "text" | "email";
rows?: number;
required?: boolean;
}
export function FormInput({
@@ -16,6 +17,7 @@ export function FormInput({
placeholder,
type = "text",
rows,
required = false,
}: FormInputProps) {
const inputId = `form-input-${id}`;
const inputClassName =
@@ -28,6 +30,7 @@ export function FormInput({
className="text-sm font-medium leading-5 text-[#FAFAFA] cursor-pointer"
>
{label}
{required && <span className="text-red-500 ml-1" aria-hidden="true">*</span>}
</label>
{rows ? (
<textarea
@@ -37,6 +40,9 @@ export function FormInput({
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
rows={rows}
required={required}
aria-required={required}
aria-label={label}
className={`${inputClassName} h-auto resize-none`}
/>
) : (
@@ -47,6 +53,9 @@ export function FormInput({
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
required={required}
aria-required={required}
aria-label={label}
className={inputClassName}
/>
)}

View File

@@ -83,6 +83,7 @@ export function InformationRequestForm({
label={t(I18nKey.ENTERPRISE$FORM_NAME_LABEL)}
value={formData.name}
placeholder={t(I18nKey.ENTERPRISE$FORM_NAME_PLACEHOLDER)}
required
onChange={(value) =>
setFormData((prev) => ({ ...prev, name: value }))
}
@@ -93,6 +94,7 @@ export function InformationRequestForm({
label={t(I18nKey.ENTERPRISE$FORM_COMPANY_LABEL)}
value={formData.company}
placeholder={t(I18nKey.ENTERPRISE$FORM_COMPANY_PLACEHOLDER)}
required
onChange={(value) =>
setFormData((prev) => ({ ...prev, company: value }))
}
@@ -104,6 +106,7 @@ export function InformationRequestForm({
type="email"
value={formData.email}
placeholder={t(I18nKey.ENTERPRISE$FORM_EMAIL_PLACEHOLDER)}
required
onChange={(value) =>
setFormData((prev) => ({ ...prev, email: value }))
}
@@ -115,22 +118,25 @@ export function InformationRequestForm({
value={formData.message}
placeholder={messagePlaceholder}
rows={4}
required
onChange={(value) =>
setFormData((prev) => ({ ...prev, message: value }))
}
/>
{/* Buttons */}
<div className="flex gap-4 mt-4">
<div className="flex gap-4 mt-4" role="group" aria-label="Form actions">
<button
type="button"
onClick={onBack}
aria-label={t(I18nKey.COMMON$BACK)}
className="flex-1 px-6 py-2.5 text-sm rounded bg-transparent text-white border border-[#242424] hover:bg-[#1a1a1a] transition-colors"
>
{t(I18nKey.COMMON$BACK)}
</button>
<button
type="submit"
aria-label={t(I18nKey.ENTERPRISE$FORM_SUBMIT)}
className="flex-1 px-6 py-2.5 text-sm rounded bg-white text-black border border-white hover:bg-gray-100 transition-colors"
>
{t(I18nKey.ENTERPRISE$FORM_SUBMIT)}

View File

@@ -56,6 +56,7 @@ function EnterpriseCard({
<button
type="button"
onClick={onLearnMore}
aria-label={`${learnMoreLabel} ${title}`}
className="mt-2 w-fit px-6 py-2.5 text-sm rounded-sm bg-[#050505] text-white border border-[#242424] hover:bg-white hover:text-black transition-colors"
>
{learnMoreLabel}
@@ -117,7 +118,7 @@ export default function InformationRequest() {
className="w-full max-w-4xl flex flex-col items-center gap-8 p-6"
>
{/* Logo */}
<OpenHandsLogoWhite width={55} height={55} />
<OpenHandsLogoWhite width={56} height={56} />
{/* Header */}
<div className="text-center flex flex-col gap-3">
@@ -154,6 +155,7 @@ export default function InformationRequest() {
type="button"
variant="secondary"
onClick={handleBack}
aria-label={t(I18nKey.COMMON$BACK)}
className="px-6 py-2.5 bg-[#050505] text-white border border-[#242424] hover:bg-white hover:text-black"
>
{t(I18nKey.COMMON$BACK)}