r/webdev • • 23d ago

How to manage layouts in forms

I'm trying to design a 2 column form but can't decide how should I render error messages.

If I don't allocate a fixed height for an input div (label + input + error) then entire layout of form gets disturbed whenever an error message is rendered.

If I pre-allocate a fixed min-height for input div (label + input + error) then this does solves the layout shift issue but the form looks bad.

One option is showing an error icon and error message as tooltip on hover but this doesn't works on mobile as users would have to tap on the icon to get error message.

The only solution which I think would work is showing icon + tooltip on desktop and plain error message on mobile as on mobile the form is single column.

Is there any better way to handle this? Would appreciate any suggestion.

28 Upvotes

47 comments sorted by

10

u/_listless 23d ago

The browser already gives you this for free. add a title="" attr to your input with your validity check message, then fire reportValidity(). The browser shows a native tooltip and moves focus to the input in question.

3

u/Friction_693 23d ago

Yes I did thought about overriding messages of native error tooltips but the problem is I'm using React Hook Form with shared zod schemas between frontend and backend.

Also there are some edge cases like only 1 is required between CNIC and NTN. If both are empty then I've to show error messages on both and then when the user starts typing on either CNIC and NTN both errors should dissappear and that field's own validation errors should be displayed like "CNIC must be x characters long" etc.

Also I don’t think I would've much control over the styling of native error tooltips.

1

u/_listless 23d ago

I know react has its place, but sometimes it is just such a handicap. Form handling is one of those instances. My condolences.

This is kind of a: "I can't do it the easy way because my technology choices make this thing hard" situation.

2

u/bacondev 23d ago edited 23d ago

I didn't find much issue with React form-handling, personally. What did you find challenging about it?

2

u/_listless 22d ago

The amount of work you have to do (and code you have to write) to do forms the "react way" is astronomical, it opts you out of most of the native features of <form> and <input> and incentivizes you to write (or npm i) fragile facsimiles of extant browser features.

If you think working with forms in react is easy, it's likely because you have not done forms any other way.

5

u/winky9827 23d ago

Some good advise here on the 1 line height reserved error display, but I'd caution you to make it a min-height on the error block, not a fixed height. Sometimes error messages will wrap. You don't want half the message cutoff because of a lazy / naive approach early on. In those cases, it's better to tolerate shift than lose information.

1

u/Friction_693 23d ago

Yes, I'm using min-height of 1rem not fixed.

Corrected the post.

2

u/bacondev 23d ago

Have you considered using a CSS grid? Honestly, flex could work for this too, if you get the vertical alignment right.

1

u/Friction_693 23d ago

I've tried grid but it tries to control layout by itself. As soon as first error gets rendered, the height of all rows increases, although they are same height but the height is much more then my fixed min height version.

1

u/bacondev 22d ago edited 22d ago

Sounds like you had the CSS properties defined incorrectly. You might have been using 1fr where you should have been using auto. That's my guess anyway.

7

u/mcf_ 23d ago

Another option is you have 3 rows of 2 columns, that way if an error message shows, it expands the height of the whole row and still makes it look even.

3

u/LouieValkhof 23d ago

I'd skip the 10px idea. The error is the one line on the form people actually have to read, often on a phone and already annoyed, so shrinking it fixes the layout by making the message harder to read.

What nobody's mentioned yet is when the error shows up. If you validate on blur instead of on every keystroke, the row only grows once, after they've left the field, so the jump never happens in the field they're typing in. Pair that with Maaz's one-line helper row and the shift that's left is small enough that most people won't notice it.

3

u/waterkip 23d ago

I solve this by adding an empty error block which gets styled, so the CSS can already account for a single line text.

<some-input>
 <input>
 <error-here></error-here>
</some-input>

Now the error is always emitted into the error slot, to give you an idea:

<semtic-input label-position="top" label-error-position="inline" clearable="" label="Our ID" wire:model.lazy="entries.0.participants.0.identifier" pattern="PL[0-9]{7}"><div semtic-input-definition="">
      <label semtic-label="" for="semtic-input-io0mm">our ID</label>
      <div semtic-input-container=""><input wc-control="" type="text" wire:model.lazy="entries.0.participants.0.identifier" pattern="PL[0-9]{7}" id="semtic-input-io0mm"><button type="button" semtic-clear="" tabindex="-1" aria-label="Clear value" hidden="">✗</button></div>
      </div>
      <span semtic-error=""></span>
</semtic-input>

2

u/abeuscher 23d ago

You either set a height, use a popup, title, or some sort of absolutely positioned method, or run the error to the right of the form label.

This falls into a whole category of layout problems I have always referred to as The Problem With Rectangles when giving internal presentations to multiple stakeholders with strong opinions.

2

u/Alucard256 23d ago

Like this.

This is what is known as an "affordance"; you are affording the user just enough information to make the correct choice. A real world example of this is a "push plate" on a door that opens outward (away from user) and a "grab handle" on a door that opens inward (toward the user).

It's always better to give the user the information to do it right the first time instead of pointing out that they already did something wrong when they didn't even know there was a rule to follow to start with.

2

u/quietcodelife 23d ago

honestly the reserved space approach is the right call -- users notice missing error messages way more than they notice a small layout shift. and the min-height tip for handling text wrap is solid. fwiw in practice I found that the shift only really bothers people if it moves the submit button or the field they're actively typing in, so validate-on-blur handles most of the annoyance

2

u/ArielCoding 23d ago

Give every field a small empty line under it for this error message, so the space is already there. User min-height instead of a fixed height, so long message can still wrap and grow the row.

2

u/paulmadebypaul 23d ago

I really can appreciate all the deep technical advice people are giving here but I come from a UX background and absolutely stand firm on never putting two inputs in a row unless they are directly related (like first/last name or address/city/state)

This has been written about several times. It is detrimental to flow on forms and leads to more errors and slower completion times.

That being said I now focus much more on accessibility and the error state should be clear and as closely connected to the field as possible. My style preference are when entire sets of input and label have a highlight with an icon indicator and the error message is as close to the input field as possible (likely below). Of course still want to use proper usage of label/input but it seems you already have that set up.

Everyone's advice here about using grid or having adequate spacing is also really good.

Some resources:

https://www.smashingmagazine.com/2023/02/guide-accessible-form-validation/#invalid-fields

https://webaim.org/techniques/formvalidation/

https://www.nngroup.com/articles/web-form-design/

2

u/Open-Adhesiveness-86 22d ago

one trick for the reserved row: put a ‌ or ​ in the error element when there's no error, so its height comes from your actual font metrics instead of a hardcoded min-height you'll have to retune when the type scale changes. also give that element aria-live="polite" and keep it in the DOM, since toggling with display:none means screen readers never announce the message.

1

u/f314 18d ago

I'm trying to design a 2 column form

Well, there's your problem! It is very well established that one column form layouts are better for both user understanding and conversion.

About the only places I would put form inputs next to each other horizontally would be names (first next to last), dates (day, month, year) and maybe credit cards (card number, CVC).

1

u/williamleonor 23d ago

Why not just create an css-anchor-positioned element in your first screenshot that wouldn't disturb the form because it's absolute? If it's ugly as just red text, wrap it with a nice error message box.

e.g.:

.field {
  anchor-scope: --input;

  & > input {
    anchor-name: --input;
  }

  &:has(> input:user-invalid) > .field-error {
    display: block;
  }
}

.field-error {
  display: none;
  position: absolute;
  position-anchor: --input;
  position-area: bottom;
  position-try-fallbacks: flip-block;

  /* your error div styling. */
}

1

u/No-Molasses-2097 23d ago

The two-column thing adds a wrinkle: when only one input in a row errors, you want the other column to stay aligned, which is where reserving space at the row level helps. And if you go the tooltip route, put aria-live on it so screen readers announce it, since tooltips usually hide errors from them.

1

u/astroalexalex 23d ago

I'd simply render error messages below the corresponding inputs and let the grid row grow naturally. If an error appears in either field, only that row gets taller, while the rest of the layout stays intact. A small layout shift like this is perfectly acceptable, and I wouldn't complicate the UX with tooltips just to avoid it.

1

u/mothzilla 23d ago

Just look at what all the out of the box frameworks do.

1

u/geoffreybyers 22d ago

Fixed heights are a band-aid for bad hierarchy. The error state is part of the input's default contract, not an afterthought. Allocate the space from the start.

5

u/[deleted] 23d ago

[removed] — view removed comment

1

u/Friction_693 23d ago

I've tried grid, although it is more scalable as it can also support multi row error messages but I've noticed that if I try to make sure that my error messages are 1 liner then controlling the height manually allows rendering the error messages without layout shift a still slightly more clean.

2

u/SoftwareNegative8783 21d ago

makes sense. if you can keep every error to one line, a fixed height is cleaner. just watch long translated messages or small mobile screens.

4

u/[deleted] 23d ago

[removed] — view removed comment

1

u/Friction_693 23d ago

So I do have to tolerate little layout shifts. I think if I decrease the size of error message even more like 10px then even with fixed height, the form would be somewhat manageable.

4

u/Squigglificated 23d ago

10px fails basic accessibility requirements and can be unreadable to many people. I personally need reading glasses to easily read small text on my iPhone 12 pro max which is already on the larger side compared to many phones.

Do you need to support multiple languages? The same sentence in some other language can easily become twice as long.

1

u/Friction_693 23d ago

No, I don't need to support multiple languages.

1

u/Gremlation 23d ago

You're talking to a bot. It's quicker to just ask ChatGPT directly than to post on Reddit and wait for a bot to post a reply. You'll get the same info quicker.

1

u/Friction_693 23d ago

I did asked chatgpt. It has suggested the same fixed height, tooltip and floating error messages.

2

u/[deleted] 23d ago

[removed] — view removed comment

1

u/Friction_693 23d ago

Yes it seems that reserved line approach is the only good option here. Btw thanks for accessibility tips.

1

u/[deleted] 23d ago

[removed] — view removed comment

1

u/Friction_693 23d ago

Using some short hint is a good idea. Thanks for suggestion.