Creating a custom input cursor/caret
I’m travelling down a well worn path. I want to create a user input field with a different coloured caret. In case you don’t know what I mean by ‘caret’, it’s essentially the cursor in an input field or other text entry area.
I don’t want to get into why I’m doing this, or the merits (or lack thereof) of creating a custom caret. I just want one, OK?
Right, now by default, a custom caret isn’t possible. Common sense seems to suggest that you should be able to use the ‘right’ element (an input
for example) and use something like :caret-color: red;
along with :caret-style: block;
in the CSS.
However, I just pulled those pseudo elements straight out of my hat (yes, I typed something different than ‘hat’ at first). Turns out there is no way of doing that currently with W3C HTML/CSS; there is no pseudo element for the caret-style and caret-colour. I know, where’s that ‘Boo Hoo’ emoji? That said, Šime pointed out in the comments that there is a draft proposal for caret-color so, maybe one day!
Half solutions
Plan B; make a span and use contenteditable="true"
and use the right border to create a faux caret.
My thought was I could perhaps use a simple animation when the span has focus to make the border blink so it appears like a cursor. For example:
<span class="my-Input" contenteditable="true"></span>
.my-Input {
border-right: 1px solid transparent;
}
.my-Input:focus {
animation: PulseAttention 1.5s cubic-bezier(.215, .61, .355, 1) forwards infinite;
}
@keyframes PulseAttention {
50% {
border-color: red;
}
}
Now, the astute will already be thinking ‘what about the default cursor’. Well I came across a nice little CSS hack on Stack Overflow (sorry can’t remember the link) that worked by setting the font-color to the same value as the background-color and then make the text appear by giving it a text-shadow. For example:
.my-Input {
color: white; /*Means both the text and caret will be white*/
background-color: white;
text-shadow: 0 0 2px #888;
}
Nice trick, right? Yes, except that iOS has other ideas; it still renders the standard blue caret. Android may do the same but the fact iOS failed the initial test was enough to make that option untenable.
I’d reached the point I hate. It’s the point I usually start writing a blog post in the hope that someone smarter has a better option. Feel free to point out my idiocy in the comments below.
This is the point when you know in your bones you are about to do something hacky. To circumvent the way things were intended.
Going to web-developer hell
Next idea: keep the input
and place it out of the viewport. When a user clicks the label
(which is in the viewport) the associated input gets focus and the user can type into it. I can then have another faceless element (“a span has no name” #GOT) and make the text content of this new element be equal to the value of the input the user is typing into (JavaScript required here sadly).
The new span is my visible ‘faux’ input box that I can style how I want, complete with custom caret. The label can be ever present, sitting on top of the faux input. We can use the following selector to move the text of the label out of the way as soon as the input contains any value:
.fb-Search_Input:not([value=""]) ~ .fb-Search_Label {
justify-content: flex-end;
}
In case that looks a bit bonkers, I’m saying, if the label has a prior sibling with the correct class that doesn’t have an empty value attribute, move the text over to the other side.
This whole thing is starting to smell a bit but you know, I do want that custom caret 😉
Making a span flow like an input
The first fly in the ointment of this approach was that when a user types in a ‘proper’ input box, and the text is longer than the available space, the caret stays at the right-most edge of the input and the overrunning text starts to flow to the left out of the input at the opposite side.
However, with this faux input that isn’t the case. The text just overflows the box (because you know, it’s just a normal element). We can’t just add overflow: hidden
as the hidden part will be the characters the user is currently inputting. Thankfully, the solution is simple. We can add the dir="rtl"
attribute to the faux input element. This makes the text flow in the opposite direction, just like a normal input would.
Just in case
For a belt and braces approach and to minimize the flack from your peers, we can wrap the various CSS selectors in overrides that makes sure that in the eventuality that JS isn’t present, the real input performs as expected and the faux input is hidden.
This is what the result looks and behaves like:
See the Pen Custom caret for input by Ben Frain (@benfrain) on CodePen.
Summary
You probably don’t want to go messing with inputs. The default cursors/carets are there for good reason; it makes it quite difficult for chimps like me to make the native input element inaccessible.
However, if you are determined to hack things to get the visuals you want, getting a custom cursor can be created, albeit in a hacky manner and requiring JS to work.
There’s a proposed caret-color property https://drafts.csswg.org/css-ui/#insertion-caret, btw .