tailwind1-12 Exercise 3: A Form with Validation States ======================================================== page.html (fragment):
Check in the console at each moment: el.matches(':invalid'), el.matches(':user-invalid'), the input's border colour, and getComputedStyle(emailErr).display. 1. Page loaded, field empty: :invalid true :user-invalid false border yellow-500 (oklch(0.795 0.184 86.047)) message: none 2. Typed "abc" and clicked elsewhere (real input events): :user-invalid true border red-500 background red-50 message: block 3. Replaced with "me@example.com" and clicked elsewhere: :user-invalid false border gray-300 (oklch(0.872 0.01 258.338)) message: none WHY THIS WORKS AS AN ANSWER --------------------------- :invalid matches as soon as the field breaks its rules, and a required empty field breaks them before anybody has touched it, so it is true at step 1. If the red colour and message were tied to invalid:, a visitor would see an error the moment the page loaded. :user-invalid only starts to match after the user has interacted with the field and left it, which is what step 2 shows, and it stops matching again when the value is fixed (step 3). The yellow border on invalid: is a mild "required" hint that is true from the start. peer on the input lets the following paragraph use peer-user-invalid:block, so the message appears only with the red state; aria-describedby links it to the field for assistive technology, and the text message means the error is not conveyed by colour alone. novalidate stops the browser's own pop-up bubble from competing with the page's message.