I have an input with a label that always goes up when the user selects the input to type something.
The only problem is that the label always goes down when the input is taken out of focus.
const MyApp = () => {
return(
<div>
<label className="relative flex min-h-10">
<input
className={
"flex flex-1 p-2 bg-white disabled:bg-[#9b9b9b] text-black transition-[outline-color] peer py-3 outline outline-1 outline-gray-400 rounded-md peer placeholder:text-transparent focus-within:outline-2 focus-within:outline-black focus-within:placeholder:text-gray-400 aria-[invalid=true]:outline-2 aria-[invalid=true]:outline-rose-600 disabled:opacity-50 disabled:cursor-not-allowed focus:text-[#205FF3] focus:outline-[#205FF3]"}
/>
<span
className={
`bottom-[22px] disabled:bg-transparent peer-focus:bottom-[45px]
flex absolute h-full items-center rounded-md py-0 px-1 mx-2 cursor-text transition-all text-gray-400 outline-gray-400
peer-focus:h-1 peer-focus:text-sm peer-focus:rounded-none peer-focus:text-[#205FF3] peer-focus:outline-black
peer-[:not(:placeholder-shown)]:h-1 peer-[:not(:placeholder-shown)]:text-sm peer-[:not(:placeholder-shown)]:rounded-none
peer-aria-[invalid=true]:text-roseoutline-[#CB5353] aria-[invalid=true]:peer-focus:text-[#CB5353] aria-[invalid=true]:text-[#CB5353]
`}
>
Username
</span>
</label>
</div>
);
}
How can I solve this problem?
You would need to look at applying the same styles that apply from peer-focus:
variant classes. There are several ways you could approach this.
You could consider adding the required
attribute to the <input>
. This will mean that when the <input>
has a value, the :valid
CSS pseudo-class will match it, allowing you to use peer-valid:
:
const MyApp = () => {
return(
<div>
<label className="relative flex min-h-10">
<input
className={
"flex flex-1 p-2 bg-white disabled:bg-[#9b9b9b] text-black transition-[outline-color] peer py-3 outline outline-1 outline-gray-400 rounded-md peer placeholder:text-transparent focus-within:outline-2 focus-within:outline-black focus-within:placeholder:text-gray-400 aria-[invalid=true]:outline-2 aria-[invalid=true]:outline-rose-600 disabled:opacity-50 disabled:cursor-not-allowed focus:text-[#205FF3] focus:outline-[#205FF3]"}
required
/>
<span
className={
`bottom-[22px] disabled:bg-transparent peer-focus:bottom-[45px]
flex absolute h-full items-center rounded-md py-0 px-1 mx-2 cursor-text transition-all text-gray-400 outline-gray-400
peer-focus:h-1 peer-focus:text-sm peer-focus:rounded-none peer-focus:text-[#205FF3] peer-focus:outline-black
peer-[:not(:placeholder-shown)]:h-1 peer-[:not(:placeholder-shown)]:text-sm peer-[:not(:placeholder-shown)]:rounded-none
peer-aria-[invalid=true]:text-roseoutline-[#CB5353] aria-[invalid=true]:peer-focus:text-[#CB5353] aria-[invalid=true]:text-[#CB5353]
peer-valid:h-1 peer-valid:text-sm peer-valid:rounded-none peer-valid:text-[#205FF3] peer-valid:outline-black peer-valid:bottom-[45px]
`}
>
Username
</span>
</label>
</div>
);
}
ReactDOM.createRoot(document.getElementById('app')).render(<MyApp/>);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
<div id="app"></div>
You could also consider adding a placeholder
attribute to the <input>
this will mean that when the <input>
has a value, the :placeholder-shown
CSS pseudo-class will not match it, allowing you to use peer-[:not(:placeholder-shown)]:
:
const MyApp = () => {
return(
<div>
<label className="relative flex min-h-10">
<input
className={
"flex flex-1 p-2 bg-white disabled:bg-[#9b9b9b] text-black transition-[outline-color] peer py-3 outline outline-1 outline-gray-400 rounded-md peer placeholder:text-transparent focus-within:outline-2 focus-within:outline-black focus-within:placeholder:text-gray-400 aria-[invalid=true]:outline-2 aria-[invalid=true]:outline-rose-600 disabled:opacity-50 disabled:cursor-not-allowed focus:text-[#205FF3] focus:outline-[#205FF3]"}
placeholder="foo"
/>
<span
className={
`bottom-[22px] disabled:bg-transparent peer-focus:bottom-[45px]
flex absolute h-1 items-center rounded-none py-0 px-1 mx-2 cursor-text transition-all text-gray-400 outline-gray-400
peer-focus:h-1 peer-focus:text-sm peer-focus:rounded-none peer-focus:text-[#205FF3] peer-focus:outline-black
peer-aria-[invalid=true]:text-roseoutline-[#CB5353] aria-[invalid=true]:peer-focus:text-[#CB5353] aria-[invalid=true]:text-[#CB5353] text-sm
peer-[:not(:placeholder-shown)]:bottom-[45px] peer-[:not(:placeholder-shown)]:text-[#205FF3] peer-[:not(:placeholder-shown)]:outline-black
`}
>
Username
</span>
</label>
</div>
);
}
ReactDOM.createRoot(document.getElementById('app')).render(<MyApp/>);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
<div id="app"></div>
You could also consider tracking if the <input>
has a value with a React state, add applying classes conditionally:
const { useState } = React;
const MyApp = () => {
const [hasValue, setHasValue] = useState();
return(
<div>
<label className="relative flex min-h-10">
<input
className={
"flex flex-1 p-2 bg-white disabled:bg-[#9b9b9b] text-black transition-[outline-color] peer py-3 outline outline-1 outline-gray-400 rounded-md peer placeholder:text-transparent focus-within:outline-2 focus-within:outline-black focus-within:placeholder:text-gray-400 aria-[invalid=true]:outline-2 aria-[invalid=true]:outline-rose-600 disabled:opacity-50 disabled:cursor-not-allowed focus:text-[#205FF3] focus:outline-[#205FF3]"}
onChange={({ target }) => setHasValue(target.value !== '')}
/>
<span
className={
`disabled:bg-transparent peer-focus:bottom-[45px] text-sm rounded-none
flex h-1 absolute items-center py-0 px-1 mx-2 cursor-text transition-all
peer-focus:h-1 peer-focus:text-sm peer-focus:rounded-none peer-focus:text-[#205FF3] peer-focus:outline-black
peer-[:not(:placeholder-shown)]:h-1 peer-[:not(:placeholder-shown)]:text-sm peer-[:not(:placeholder-shown)]:rounded-none
peer-aria-[invalid=true]:text-roseoutline-[#CB5353] aria-[invalid=true]:peer-focus:text-[#CB5353] aria-[invalid=true]:text-[#CB5353]
${hasValue ? 'bottom-[45px] text-[#205FF3] outline-black' : 'bottom-[22px] text-gray-400 outline-gray-400'}
`}
>
Username
</span>
</label>
</div>
);
}
ReactDOM.createRoot(document.getElementById('app')).render(<MyApp/>);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.tailwindcss.com/3.4.1"></script>
<div id="app"></div>