Why is :read-only CSS pseudo-class being applied on this checkbox?

All we need is an easy explanation of the problem, so here it is.

I have the following SCSS code:

input[type="checkbox"] {
    ...
    &:read-only, &[readonly] {
        cursor: default;
        filter: grayscale(1);
    }
}

That is being applied to
<input type="checkbox" id="checkbox" onChange={this.rememberMe} />

Following MDN: :read-only documentation:

it will select any element that cannot be edited by the user.

Why is it being applied on my input that is editable?

The problem is applicable both to Firefox and Chrome.

Why is :read-only CSS pseudo-class being applied on this checkbox?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

  • Because <input type="checkbox" /> and <input type="radio" /> (and most other elements) are inherently read-only.
  • Unlike an <input type="text" /> or <input type="date" />, when you interact with (i.e. toggle) a checkbox or radio button you are not changing its value, you are changing its checked state.
  • Yes, I agree it’s counter-intuitive.

Consequently…

  1. You should not apply the <input readonly attribute to a radio or checkbox for any purpose.
    • Because it won’t do anything useful.
  2. You should not define a CSS selector that uses the :read-only pseudo-class to select <input> elements that have an explicit HTML <input readonly attribute set.

From the current WHATWG HTML specification (emphasis mine, especially the last point):

https://html.spec.whatwg.org/multipage/semantics-other.html#selector-read-only

The :read-write pseudo-class must match any element falling into one of the following categories, which for the purposes of Selectors are thus considered user-alterable: [SELECTORS]

  • <input> elements to which the <input readonly attribute applies, and that are mutable (i.e. that do not have the <input readonly attribute specified and that are not <input disabled).
  • <textarea> elements that do not have a <textarea readonly attribute, and that are not <textarea disabled.
  • elements that are editing hosts or editable and are neither <input> elements nor <textarea> elements.
    • [i.e. contenteditable]
  • The :read-only pseudo-class must match all other HTML elements.

Now, if you want a "read-only checkbox/radio" then you don’t have too many good options, unfortunately; instead you have a mix of terrible options and barely-adequate ones…:


If you want to use the :read-only pseudo-class on all input elements except radio and checkboxes then you need to think carefully (and test it too, using variations on document.querySeletorAll("input:read-only") in your browser’s console!)

I recommend that you do not apply any styles using selectors for input elements without also explicitly specifying the [type=""] attribute selector – this is because styles with a selector like "input" (without any attribute-selectors) will be applied to future HTML input elements that we don’t know about yet and could be introduced at any point in the near-future, and maybe next week Google Chrome adds a new <input type="human-dna-sample" /> or Microsoft adds <input type="clippy" /> to a particularly retro edition of their Edge browser – so you definitely don’t want a :read-only style applied to those elements until you at least know how it will look and work – and so the browser will use its default/native styling which won’t violate your users/visitor’s expectations if they happen to come across it on your website at some point.

…so it means you need to write out rules for every known <input type="..."> as repetitive input[type=""] style rules, and now you might wonder if there were any pseudo-classes for input elements based on their default native appearance because a lot of them sure do look share similar, if not identical, native appearance and visual-semantics (and shadow DOM structure, if applicable) – for example in desktop Chrome the input types text, password, email, search, url, tel and more are all clearly built around the same native textbox widget, so there surely must be a pseudo-class for different input "kinds", right? Something like input:textbox-kind for text, password, etc and input:checkbox-kind for checkbox and radio – unfortunately such a thing doesn’t exist and if introduced tomorrow the W3C’s CSS committee probably wouldn’t approve it for a few more years at least – so until then we need to explicitly enumerate every input[type=""] that we know about so that we can accurately anticipate how browsers will render them with our type=""-specific style rules instead of throwing everything as input {} and seeing what sticks.

…fortunately the list isn’t too long, so I just wrote the rules out just now:

Feel free to copy + paste this; it’s hardly even copyrightable. And I want to see how far this spreads across the Internet in my lifetime.

At the bottom is a CSS selector that will select only <input elements that are from the future by using an exhaustive set of :not([type="..."]) selectors, as well as not matching input elements with an empty type="" attribute or missing one entirely.

/* Textbox-kind: */ 
input[type="text"]:read-only,
input[type="password"]:read-only,
input[type="search"]:read-only,
input[type="tel"]:read-only,
input[type="url"]:read-only,
input[type="email"]:read-only,
input[type="number"]:read-only {
    background-color: #ccc;
    cursor: 'not-allowed';
}

/* Date/time pickers: */ 
input[type="date"]:read-only,
input[type="datetime-local"]:read-only,
input[type="time"]:read-only,
input[type="week"]:read-only,
input[type="month"]:read-only {
    background-color: #ccc;
    cursor: 'not-allowed';
}

/* Button-kind (these are all practically obsolete now btw, as the <button> element is far, far, far superior in every way) */
input[type="button"]:disabled,
input[type="reset"]:disabled,
input[type="submit"]:disabled,
input[type="image"]:disabled {
    background-color: #ccc;
    border: 1px outset #666;
    cursor: 'not-allowed';
    color: #666;
    text-shadow: 0 1px rgba(255,255,255,0.2);
}

/* Checkbox-kind (Don't use `:read-only` with these): */
input[type="checkbox"]:disabled,
input[type="radio"]:disabled {
    /* I'm not setting any properties here because it's impossible to effectively style these elements without resorting to image-replacements using the `:checked` state in selectors for their parent or adjacent `<label>` or ::before/::after` of other proximate elements. */
}

/* Weird-stuff-kind: */ 
input[type="color"]:read-only,
input[type="file"]:read-only,
input[type="hidden"]:read-only,
input[type="range"]:read-only {
    /* Again, due to differences in how different browsers and platforms display (and consequently style) these inputs I don't think it's worth doing anything. */
}

/* If you **really** want to select _future_ <input> elements in-advance... do this: */

input[type]:not([type="text"]):not([type="password"]):not([type="search"]):not([type="tel"]):not([type="url"]):not([type="email"]):not([type="number"]):not([type="date"]):not([type="datetime-local"]):not([type="time"]):not([type="week"]):not([type="month"]):not([type="button"]):not([type="reset"]):not([type="submit"]):not([type="image"]):not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="file"]):not([type="hidden"]):not([type="range"]) {

}

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply