Once again about the visualization of input types checkbox and radio. For those who have forgotten how

The topic is old and already, as it turned out, forgotten.



Recently I had a short job on the development of technical specifications for the modernization of a long-existing project. And, in particular, the case concerned the styling of the notorious <input type = "checkbox">. It turned out that the performer, a programmer “of all trades” did not even understand that I was explaining to him in words how to do it. I had to make examples and, as a result, this text appeared.



Let me remind you that now checkbox and radiobox are depicted differently by different sites. It happens that an unmarked input cannot be immediately discerned - it is so "designerly beautiful", and in the latest versions of Chrome, the selected checkboxes have become a vile cyan color.



So the situation



There are three companies that use a certain software product related to orders, accounting, warehouse, etc. Working with customers, partners, executors, etc.



Marketers and advertisers also use it. What this system does - no matter what it's written on - doesn't matter.



And it is important that the site of this product has many pages with forms on which there are many input checkboxes and radio.



Employee complaints



Director : On the big screen, the "kryzhiki" are hard to see and are invisible.

Chief accountant : On my computer, the “kryzhiki” look like this, on the employees it looks different, at home it is also different, but on the tablet it is completely different.

Marketer : Is it possible, so that some unselected positions would be red, and others selected were green?

Etc.



So the task



  1. Fix the appearance of the checkbox and radiobox with minimal costs and minimal changes.
  2. Make checkbox and radiobox styling for different users. Important: this is a closed site, there are “all our own”, “beauty” is not needed, but the effectiveness of perception is needed.


What is not allowed



1. The server side must not be touched.

2. You cannot touch javascript files, you cannot insert your own javascript.

3. The css files cannot be touched.



What is allowed



1. Edit html templates.

2. Create a style file for all users.

4. Create a style file for a specific user or group of users.

And what you did you can immediately look at codepen.io , but it's better to read further.



Preliminary study showed



1. Almost all <input type = "checkbox"> have a name field, and those that do not have an id.

2. All <input type = "radio"> have a name field, some have an id.

3. Accordingly, in css the checkbox can be accessed by both id and name. To radio - either by id, or by the number of the parent's child.



Source code snippets:



/*  1 */
<tag><input type="checkbox">  </tag>

/*  2 */
<tag><input type="checkbox">  <br>
<input type="checkbox">  </tag>

/*  3 */
...<label><input type="checkbox">  </label>...

/*  4 */
<td><input id="idxxx" type="checkbox"></td>
<td><label for="idxxx"> </label></td>


Let's fix the code:



/*  1 */
<tag><label class="new-input"><input type="checkbox"><s></s><span> </span></label></tag>

/*  2 */
<tag><label class="new-input"><input type="checkbox"><s></s><span> </span></label><br>...</tag>

/*  3 */
...<label class="new-input"><input type="checkbox"><s></s><span> </span></label>...

/*  4 */
<td><label class="new-input new-input-one"><input id="idxxx" type="checkbox"><s></s></label></td>
<td><label for="idxxx"> </label></td>


Everything is the same for <input type = "radio">, the LABEL class is the same.



What exactly did you do?



  1. Each input (stern of option 3) has been wrapped with a LABEL tag with our class. Option 3 just added a class.
  2. Immediately after the input, we inserted an empty S tag. Since the input itself will not be visible, this tag will render this input.
  3. The accompanying text was wrapped with the SPAN tag (except for option 4). This tag will be needed when we decide the question of aligning the visual input relative to this text.
  4. Option 4 was added another class to avoid this alignment, since the accompanying text is in another cell of the table. Strictly speaking, it would be necessary to do the opposite - add a class responsible for alignment to options 1-3. But, there are much more options 1-3 than the 4th one, and in order not to inflate the html it is done like this.


Rhetorical questions and rhetorical answers
1. S? , S – . , input.



2. S SPAN ? , html? , , «» .



  .new-input > S { }
  .new-input > .new-input-S {}


3. , , «» html . – mni mnio. :-))



Some preliminary considerations and css tweaks regarding box-sizing: border-box, LABEL normalization, A + B, A ~ B and [attr] selectors,: checked,: disabled and :: before pseudo-classes. Whoever is not sure what he knows or wants to refresh his knowledge looks under the cut.



Preliminary considerations
1. , «» css (box-sizing:content-box) width height , padding border . box-sizing:border-box , padding border width height.



2. , , «» . «» LABEL , . LABEL.



LABEL {
    box-sizing:border-box; cursor:pointer; user-select:none;
}
LABEL *,
LABEL *::before,
LABEL *::after {
    box-sizing:inherit;
}


.., box-sizing:border-box LABEL, . ( ).



3. «A + B» , B, A, .. B. , «A ~ B» , B A, .. .

, «».



?



<label class="new-input"><input type="checkbox"><s></s><span> </span></label>
<label class="new-input"><input type="radio"><s></s><span> </span></label>


/* 1 */
.new-input > INPUT + S {}
.new-input > INPUT ~ SPAN {}

/* 2 */
.new-input > INPUT:not(:checked) + S {}
.new-input > INPUT:not(:checked) ~ SPAN {}

/* 3 */
.new-input > INPUT:checked + S {}
.new-input > INPUT:checked ~ SPAN {}

/* 4 */
.new-input > INPUT:disabled + S {}
.new-input > INPUT:disabled ~ SPAN {}

/* 5 */
.new-input > INPUT[type="radio"] + S {}


– S SPAN.

– INPUT .

– INPUT .

– INPUT .



, , – S , input radio.

, S SPAN input.



4. S input, input display:none, , LABEL , S . html hidden input? , hidden input «» , html .



So, let's start rendering input



Example N 1. The simplest one - we use alphabetic characters. The

html code is the same, and the css will be like this:



/* s1 */
.new-input > INPUT + S::before {
  content: "c";
}
/* s2 */
.new-input > INPUT:checked + S::before {
  content: "V";
}
/* s3 */
.new-input > INPUT[type="radio"] + S::before {
  content: "r";
}
/* s4 */
.new-input > INPUT[type="radio"]:checked + S::before {
  content: "X";
}
/* s5 */
.new-input > INPUT:disabled + S::before {
  opacity: 0.5;
}
/* s6 */
.new-input > S {
  text-decoration: none;
  margin-left: 3px;
  margin-right: 6px;
}
/* s7 */
.new-input > S::before {
  display: inline-block;
  width: 1.25em;
  text-align: center;
  color: #fafafa;
  background-color: #37474f;
}
/* s8 */
.new-input > INPUT[type="radio"] + S::before {
  border-radius: 50%;
}


The S tag will render input. But we will "divide" it by functionality: the S tag itself will be responsible for placement in LABEL and alignment relative to the next SPAN.



And the pseudo-element S :: before will be placed inside the S tag and will act as input.



The s1 line defines which character will be placed in S :: before when input is not selected. In principle, it would be necessary to write ".new-input> INPUT: not (: checked) + S :: before", but some browsers (for example, IE) may not execute such a construction.

String s2 specifies the character when input is selected.

The s3 and s4 lines do the same for the input radio.

String s5describes what happens if input is blocked - in this case, the S tag will be half transparent.

Line s6 defines the alignment, in this case it gives padding to the left and right (only in this example). Plus, it removes the regular strikethrough.

Line s7 makes a square, s8 turns it into a circle for input radio.



Example # 1 can be viewed at codepen.io . There are native inputs and new ones. The former can be removed.



More details about display: inline-block, font-size, line-height
font-size, line-height. line-height – font-size, line-height – , line-height – . line-height:1.25, S::before width:1.25em.



S::before display: inline-block – S::before «» ( , , .), «» . .





Question:



Can you use special characters? Like these:

□ ■ ▢ ▣ ○ ● ◎ ◉

Give them the right size and that's it. No?



Answer:



You can. But you don't need to. For there will be large hemorrhoids and dances with tambourines on the assignment of the desired size, vertical alignment, trimming and so on. Plus, different browsers work differently with these symbols.

We went the other way. Although the final example contains an implementation of this idea.



Example N 2. "Draw" input elements using css



html code is the same, but css will be like this:



/* s1 */
.new-input > S::before {
  content: "";
  display: inline-block;
  width: 0.75em;
  height: 0.75em;
  border: 1px solid currentColor;
  padding: 2px;
  background-clip: content-box;
  border-radius: 20%;
}
/* s2 */
.new-input > INPUT[type="radio"] + S::before {
  border-radius: 50%;
}
/* s3 */
.new-input > INPUT:checked + S::before {
  background-color: currentColor;
}
/* s4 */
.new-input > INPUT:disabled + S::before {
  opacity: 0.5;
}
/* s5 */
.new-input > S {
  text-decoration: none;
  margin-left: 3px;
  margin-right: 6px;
}


The s1 line defines S :: before to render input. This will be an inline-block whose width and height are set to 0.75em, which is roughly the same height as the uppercase letter and depends on the parent's font-size. A thin border is set with the current color, inner padding, small rounding of corners. And the most important! - property background-clip: content-box is set. This is a very interesting property - if the background-color is set, it will paint only the content part and will not affect the padding. What we need.



The s2 line for a radio input makes S :: before round.

The s3 line for the marked input sets S :: before background-color to the current color. That is, it "draws" a square or circle inside.

Lines4 unlocks input, line s5 gives padding to the left and right.



The advantages of this method



  1. Everything is very simple. Works on all browsers. Even IE10 (in emulation of the 11th).
  2. You can color as you like.
  3. Since S :: before is inline-block, then it sits on the bottom of the baseline exactly and does not get off it anywhere. If it is larger in height than the text, it will simply increase the line height and stay on the baseline.
  4. Since the input visualization is inside an S tag, it can be positioned and aligned easily.
  5. S :: before's dimensions in em make it possible to set its size relative to the size of the caption text. You can, for example, set limits for the height and width.


Disadvantages of this method



Mainly in em sizes. The fact is that a situation may arise when the width and height when calculating (from em to px) will have a fractional value. Rounding may not work correctly on ordinary computers with a normal screen. For example, dimensions 12.8px by 12.8px in the same Mozila can become 13px by 12px. Then you need to set fixed sizes. Although on modern monitors and video cards, laptops, tablets and smartphones, this does not happen due to the fact that the point (pixel) of the browser consists of several screen pixels.



Example # 2 can be viewed at codepen.io . There are native inputs and new ones. The former can be removed.

So, the first task - rendering input - is done. We pass to the selected "coloring".



Coloring input



html for example:



<label class="new-input"><input name="chb1" type="checkbox" ...><s></s><span> </span></label>
<label class="new-input"><input id="rb1" type="radio" ...><s></s><span> </span></label>


We will refer to an input of the checkbox type by name, to radio by id.



We paint everything blue



/*  input */
.new-input > INPUT[name="chb1"] + S,
.new-input > INPUT#rb1 + S {
  color: #0091ea;
}
/*  text */
.new-input > INPUT[name="chb1"] ~ SPAN,
.new-input > INPUT#rb1 ~ SPAN {
  color: #0091ea;
}
/*   */
.new-input > INPUT[name="chb1"] ~ *,
.new-input > INPUT#rb1 ~ * {
  color: #0091ea;
}


Remember the specificity in css, these styles will be more specific than the basic ones and will definitely work. How do they differ from those described above? Those that only apply to select inputs - those that have the specified name and id value.



Everything is good here, except that the unselected inputs will not look very good - the thin blue border is hardly noticeable.



Color it green when input is selected



/*  input */
.new-input > INPUT[name="chb1"]:checked + S,
.new-input > INPUT#rb1:checked + S {
  color: #00c853;
}
/*  text */
.new-input > INPUT[name="chb1"]:checked ~ SPAN,
.new-input > INPUT#rb1:checked ~ SPAN {
  color: #00c853;
}
/*   */
.new-input > INPUT[name="chb1"]:checked ~ *,
.new-input > INPUT#rb1:checked ~ * {
  color: #00c853;
}


The first option, in my opinion, is not very good - both the frame and the inner square / circle will be green. You can only color it.



/*  input    */
.new-input > INPUT[name="chb1"]:checked + S::before,
.new-input > INPUT#rb1:checked + S::before {
  background-color: #00c853;
}


Color in red when input is NOT selected



/*  input */
.new-input > INPUT[name="chb1"]:not(:checked) + S,
.new-input > INPUT#rb1:not(:checked) + S {
  color: #d50000;
}
/*  text */
.new-input > INPUT[name="chb1"]:not(:checked) ~ SPAN,
.new-input > INPUT#rb1:not(:checked) ~ SPAN {
  color: #d50000;
}
/*   */
.new-input > INPUT[name="chb1"]:not(:checked) ~ *,
.new-input > INPUT#rb1:not(:checked) ~ * {
  color: #d50000;
}


Is the logic clear? You can continue to make more complex designs.



For example, if input is not selected, the text should be red and bold, and if the inner input element and text is selected, it should be green. Elementary!



/* ,    */
.new-input > INPUT[name="chb1"]:not(:checked) ~ SPAN,
.new-input > INPUT#rb1:not(:checked) ~ SPAN {
  color: #d50000;
  font-weight: bold;
}
/*   input,   */ 
.new-input > INPUT[name="chb1"]:checked + S::before,
.new-input > INPUT#rb1:checked + S::before {
  background-color: #00c853;
}
/* ,   */ 
.new-input > INPUT[name="chb1"]:checked ~ SPAN,
.new-input > INPUT#rb1:checked ~ SPAN {
  color: #00c853;
}


And, for example, you need to process a whole group of input (10-15 pieces). In order not to write a bunch of lines, you can find their common parent (.parent_element) and shorten the condition.



.parent_element > .new-input > INPUT:not(:checked) ~ SPAN {
  color: #d50000;
  font-weight: bold;
}
.parent_element > .new-input > INPUT:checked + S::before {
  background-color: #00c853;
}
.parent_element > .new-input > INPUT:checked ~ SPAN {
  color: #00c853;
}


You can see everything in the final example on codepen.io



That's, it seems, and that's it. It remains only to "scratch the birthmarks" of the perfectionist - the problems of alignment.



Align visual input and accompanying text



To begin with, let me remind you of well-known things about text placement, formatting, and more. Everything is under the cut.



Commonly known things
, , css . .



1. font-size , . (baseline), «» . «» – g – «» . (cap height) – «» () . «» – – «» . , , . «» 75% . , font-size:16px, Arial 12px. , «» .



2. line-height . , font-size, , . , .



3. , S SPAN font-size line-height - . font-size:16px line-height:1.25. N1 S::before 1.25em, . N2 ( ) – S::before 0.75em, . font-size . , .



4. - «», , . . , , 50%. , , 150%. -, / – -! , -.



? , , input — , , , . !



What happens if the accompanying text in SPAN is displayed in two or three lines? Obviously, it will "fit" under input. It's not pretty, it needs to be fixed.



One ancient method is: the S tag float: left, and the SPAN tag display: block and overflow: hidden.



This will create a column of text. It is assumed that some of them will have an appropriate margin, which will give a skip between them. Well, there is also added hemorrhoids with the termination of float after SPAN. We're going to take the modern route - use flexbox. He's completely out of place here.



.new-input {
  display: flex;
  flex-direction: row;
  align-items: start;
}
.new-input > S {
  margin-right: 4px;
  flex: 0 0 auto;
}
.new-input > SPAN {
  flex: 0 1 auto;
}


In this case the LABEL tag (which is .new-input) will be flex, S and SPAN will be blocks, placed at the top of the LABEL. The text in SPAN, if necessary, will be in several lines. Because of this, the visual input was described in S :: before. Regardless of the height, SPAN S :: before will be aligned with the first SPAN line. Alternatively, you could specify align-items: center - then with a single-line SPAN, the visual input would be at the top, and with two lines - in the middle, and with three lines - at the second line. In the final example, you can switch the location of input.



That's all



I hope it was interesting and useful to someone. Please do not scold me much - this is my first experience on Habr.



Example # 1 is just a demonstration of the interaction between changing input and a neighboring element.



Example N 2 - visualization of input using css, as the basis of the solution.



The final example is everything described together.



About a specific implementation



There were extensive forms, where the field blocks that could be edited by specific users were highlighted with a faint background, and the rest of the input had the disabled property and served only for information. Therefore, the style ".new-input> INPUT: disabled + S :: before" was not applied.



UPD



In response to comments Example N 3 .

There is key and focus support for hidden input at work.



All Articles