]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Form check markup v2 (#25050)
authorMark Otto <markd.otto@gmail.com>
Sat, 23 Dec 2017 00:13:01 +0000 (16:13 -0800)
committerGitHub <noreply@github.com>
Sat, 23 Dec 2017 00:13:01 +0000 (16:13 -0800)
* match layout behaviors

* ditch the indicator as separate element for psuedo-elements on the label

* move disabled to attribute only on input

* redo default inline check to support new markup

* redo inline forms

* clean up vars

* update validation mixin to new structure

* update checks in docs

* linting for for/id attributes

docs/4.0/components/dropdowns.md
docs/4.0/components/forms.md
docs/4.0/migration.md
scss/_custom-forms.scss
scss/_forms.scss
scss/_variables.scss
scss/mixins/_forms.scss

index 926729a3d9abae94d895efc28ee5d122ba4229c3..b49e84f920eba8142a9d11fe646f84bb896a7168 100644 (file)
@@ -615,8 +615,8 @@ Put a form within a dropdown menu, or make it into a dropdown menu, and use [mar
       <input type="password" class="form-control" id="exampleDropdownFormPassword1" placeholder="Password">
     </div>
     <div class="form-check">
-      <label class="form-check-label">
-        <input type="checkbox" class="form-check-input">
+      <input type="checkbox" class="form-check-input" id="dropdownCheck">
+      <label class="form-check-label" for="dropdownCheck">
         Remember me
       </label>
     </div>
@@ -639,8 +639,8 @@ Put a form within a dropdown menu, or make it into a dropdown menu, and use [mar
     <input type="password" class="form-control" id="exampleDropdownFormPassword2" placeholder="Password">
   </div>
   <div class="form-check">
-    <label class="form-check-label">
-      <input type="checkbox" class="form-check-input">
+    <input type="checkbox" class="form-check-input" id="dropdownCheck2">
+    <label class="form-check-label" for="dropdownCheck2">
       Remember me
     </label>
   </div>
index d485ef4cddf2ff0f3d926d009bc2f67ff1f53b84..ee67ffa8005dc00d72c78dd93d820f1655c318ca 100644 (file)
@@ -26,10 +26,8 @@ Here's a quick example to demonstrate Bootstrap's form styles. Keep reading for
     <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
   </div>
   <div class="form-check">
-    <label class="form-check-label">
-      <input type="checkbox" class="form-check-input">
-      Check me out
-    </label>
+    <input type="checkbox" class="form-check-input" id="exampleCheck1">
+    <label class="form-check-label" for="exampleCheck1">Check me out</label>
   </div>
   <button type="submit" class="btn btn-primary">Submit</button>
 </form>
@@ -154,7 +152,9 @@ If you want to have `<input readonly>` elements in your form styled as plain tex
 
 Default checkboxes and radios are improved upon with the help of `.form-check`, **a single class for both input types that improves the layout and behavior of their HTML elements**. Checkboxes are for selecting one or several options in a list, while radios are for selecting one option from many.
 
-Disabled checkboxes and radios are supported, but to provide a `not-allowed` cursor on hover of the parent `<label>`, you'll need to add the `.disabled` class to the parent `.form-check`. The disabled class will also lighten the text color to help indicate the input's state.
+Disabled checkboxes and radios are supported, but to provide a `not-allowed` cursor on hover of the parent `<label>`, you'll need to add the `disabled` attribute to the `.form-check-input`. The disabled attribute will apply a lighter color to help indicate the input's state.
+
+Checkboxes and radios use are built to support HTML-based form validation and provide concise, accessible labels. As such, our `<input>`s and `<label>`s are sibling elements as opposed to an `<input>` within a `<label>`. This is slightly more verbose as you must specify `id` and `for` attributes to relate the `<input>` and `<label>`.
 
 ### Default (stacked)
 
@@ -162,36 +162,36 @@ By default, any number of checkboxes and radios that are immediate sibling will
 
 {% example html %}
 <div class="form-check">
-  <label class="form-check-label">
-    <input class="form-check-input" type="checkbox" value="">
-    Option one is this and that&mdash;be sure to include why it's great
+  <input class="form-check-input" type="checkbox" value="" id="defaultCheck1">
+  <label class="form-check-label" for="defaultCheck1">
+    Default checkbox
   </label>
 </div>
-<div class="form-check disabled">
-  <label class="form-check-label">
-    <input class="form-check-input" type="checkbox" value="" disabled>
-    Option two is disabled
+<div class="form-check">
+  <input class="form-check-input" type="checkbox" value="" id="defaultCheck2" disabled>
+  <label class="form-check-label" for="defaultCheck2">
+    Disabled checkbox
   </label>
 </div>
 {% endexample %}
 
 {% example html %}
 <div class="form-check">
-  <label class="form-check-label">
-    <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios1" value="option1" checked>
-    Option one is this and that&mdash;be sure to include why it's great
+  <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios1" value="option1" checked>
+  <label class="form-check-label" for="exampleRadios1">
+    Default radio
   </label>
 </div>
 <div class="form-check">
-  <label class="form-check-label">
-    <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios2" value="option2">
-    Option two can be something else and selecting it will deselect option one
+  <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios2" value="option2">
+  <label class="form-check-label" for="exampleRadios2">
+    Second default radio
   </label>
 </div>
 <div class="form-check disabled">
-  <label class="form-check-label">
-    <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios3" value="option3" disabled>
-    Option three is disabled
+  <input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios3" value="option3" disabled>
+  <label class="form-check-label" for="exampleRadios3">
+    Disabled radio
   </label>
 </div>
 {% endexample %}
@@ -202,37 +202,31 @@ Group checkboxes or radios on the same horizontal row by adding `.form-check-inl
 
 {% example html %}
 <div class="form-check form-check-inline">
-  <label class="form-check-label">
-    <input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1"> 1
-  </label>
+  <input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1">
+  <label class="form-check-label" for="inlineCheckbox1">1</label>
 </div>
 <div class="form-check form-check-inline">
-  <label class="form-check-label">
-    <input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2"> 2
-  </label>
+  <input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2">
+  <label class="form-check-label" for="inlineCheckbox2">2</label>
 </div>
-<div class="form-check form-check-inline disabled">
-  <label class="form-check-label">
-    <input class="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3" disabled> 3
-  </label>
+<div class="form-check form-check-inline">
+  <input class="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3" disabled>
+  <label class="form-check-label" for="inlineCheckbox3">3 (disabled)</label>
 </div>
 {% endexample %}
 
 {% example html %}
 <div class="form-check form-check-inline">
-  <label class="form-check-label">
-    <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
-  </label>
+  <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
+  <label class="form-check-label" for="inlineRadio1">1</label>
 </div>
 <div class="form-check form-check-inline">
-  <label class="form-check-label">
-    <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2"> 2
-  </label>
+  <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
+  <label class="form-check-label" for="inlineRadio2">2</label>
 </div>
-<div class="form-check form-check-inline disabled">
-  <label class="form-check-label">
-    <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3" disabled> 3
-  </label>
+<div class="form-check form-check-inline">
+  <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3" disabled>
+  <label class="form-check-label" for="inlineRadio3">3 (disabled)</label>
 </div>
 {% endexample %}
 
@@ -242,14 +236,10 @@ Add `.position-static` to inputs within `.form-check` that don't have any label
 
 {% example html %}
 <div class="form-check">
-  <label class="form-check-label">
-    <input class="form-check-input position-static" type="checkbox" id="blankCheckbox" value="option1" aria-label="...">
-  </label>
+  <input class="form-check-input position-static" type="checkbox" id="blankCheckbox" value="option1" aria-label="...">
 </div>
 <div class="form-check">
-  <label class="form-check-label">
-    <input class="form-check-input position-static" type="radio" name="blankRadio" id="blankRadio1" value="option1" aria-label="...">
-  </label>
+  <input class="form-check-input position-static" type="radio" name="blankRadio" id="blankRadio1" value="option1" aria-label="...">
 </div>
 {% endexample %}
 
@@ -349,8 +339,9 @@ More complex layouts can also be created with the grid system.
   </div>
   <div class="form-group">
     <div class="form-check">
-      <label class="form-check-label">
-        <input class="form-check-input" type="checkbox"> Check me out
+      <input class="form-check-input" type="checkbox" id="gridCheck">
+      <label class="form-check-label" for="gridCheck">
+        Check me out
       </label>
     </div>
   </div>
@@ -383,21 +374,21 @@ At times, you maybe need to use margin or padding utilities to create that perfe
       <legend class="col-form-label col-sm-2 pt-0">Radios</legend>
       <div class="col-sm-10">
         <div class="form-check">
-          <label class="form-check-label">
-            <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios1" value="option1" checked>
-            Option one is this and that&mdash;be sure to include why it's great
+          <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios1" value="option1" checked>
+          <label class="form-check-label" for="gridRadios1">
+            First radio
           </label>
         </div>
         <div class="form-check">
-          <label class="form-check-label">
-            <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios2" value="option2">
-            Option two can be something else and selecting it will deselect option one
+          <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios2" value="option2">
+          <label class="form-check-label" for="gridRadios2">
+            Second radio
           </label>
         </div>
         <div class="form-check disabled">
-          <label class="form-check-label">
-            <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios3" value="option3" disabled>
-            Option three is disabled
+          <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios3" value="option3" disabled>
+          <label class="form-check-label" for="gridRadios3">
+            Third disabled radio
           </label>
         </div>
       </div>
@@ -407,8 +398,9 @@ At times, you maybe need to use margin or padding utilities to create that perfe
     <div class="col-sm-2">Checkbox</div>
     <div class="col-sm-10">
       <div class="form-check">
-        <label class="form-check-label">
-          <input class="form-check-input" type="checkbox"> Check me out
+        <input class="form-check-input" type="checkbox" id="gridCheck1">
+        <label class="form-check-label" for="gridCheck1">
+          Example checkbox
         </label>
       </div>
     </div>
@@ -490,8 +482,9 @@ The example below uses a flexbox utility to vertically center the contents and c
     </div>
     <div class="col-auto">
       <div class="form-check mb-2">
-        <label class="form-check-label">
-          <input class="form-check-input" type="checkbox"> Remember me
+        <input class="form-check-input" type="checkbox" id="autoSizingCheck">
+        <label class="form-check-label" for="autoSizingCheck">
+          Remember me
         </label>
       </div>
     </div>
@@ -507,27 +500,28 @@ You can then remix that once again with size-specific column classes.
 {% example html %}
 <form>
   <div class="form-row align-items-center">
-    <div class="col-sm-3">
+    <div class="col-sm-3 my-1">
       <label class="sr-only" for="inlineFormInputName">Name</label>
-      <input type="text" class="form-control mb-2 mb-sm-0" id="inlineFormInputName" placeholder="Jane Doe">
+      <input type="text" class="form-control" id="inlineFormInputName" placeholder="Jane Doe">
     </div>
-    <div class="col-sm-3">
+    <div class="col-sm-3 my-1">
       <label class="sr-only" for="inlineFormInputGroupUsername">Username</label>
-      <div class="input-group mb-2 mb-sm-0">
+      <div class="input-group">
         <div class="input-group-prepend">
           <div class="input-group-text">@</div>
         </div>
         <input type="text" class="form-control" id="inlineFormInputGroupUsername" placeholder="Username">
       </div>
     </div>
-    <div class="col-auto">
-      <div class="form-check mb-2 mb-sm-0">
-        <label class="form-check-label">
-          <input class="form-check-input" type="checkbox"> Remember me
+    <div class="col-auto my-1">
+      <div class="form-check">
+        <input class="form-check-input" type="checkbox" id="autoSizingCheck2">
+        <label class="form-check-label" for="autoSizingCheck2">
+          Remember me
         </label>
       </div>
     </div>
-    <div class="col-auto">
+    <div class="col-auto my-1">
       <button type="submit" class="btn btn-primary">Submit</button>
     </div>
   </div>
@@ -539,24 +533,23 @@ And of course [custom form controls](#custom-forms) are supported.
 {% example html %}
 <form>
   <div class="form-row align-items-center">
-    <div class="col-auto">
+    <div class="col-auto my-1">
       <label class="mr-sm-2" for="inlineFormCustomSelect">Preference</label>
-      <select class="custom-select mb-2 mr-sm-2" id="inlineFormCustomSelect">
+      <select class="custom-select mr-sm-2" id="inlineFormCustomSelect">
         <option selected>Choose...</option>
         <option value="1">One</option>
         <option value="2">Two</option>
         <option value="3">Three</option>
       </select>
     </div>
-    <div class="col-auto">
-      <label class="custom-control custom-checkbox mb-2 mr-sm-2">
-        <input type="checkbox" class="custom-control-input">
-        <span class="custom-control-indicator"></span>
-        <span class="custom-control-description">Remember my preference</span>
-      </label>
+    <div class="col-auto my-1">
+      <div class="custom-control custom-checkbox mr-sm-2">
+        <input type="checkbox" class="custom-control-input" id="customControlAutosizing">
+        <label class="custom-control-label" for="customControlAutosizing">Remember my preference</label>
+      </div>
     </div>
-    <div class="col-auto">
-      <button type="submit" class="btn btn-primary mb-2">Submit</button>
+    <div class="col-auto my-1">
+      <button type="submit" class="btn btn-primary">Submit</button>
     </div>
   </div>
 </form>
@@ -586,8 +579,9 @@ You may need to manually address the width and alignment of individual form cont
   </div>
 
   <div class="form-check mb-2 mr-sm-2">
-    <label class="form-check-label">
-      <input class="form-check-input" type="checkbox"> Remember me
+    <input class="form-check-input" type="checkbox" id="inlineFormCheck">
+    <label class="form-check-label" for="inlineFormCheck">
+      Remember me
     </label>
   </div>
 
@@ -599,21 +593,20 @@ Custom form controls and selects are also supported.
 
 {% example html %}
 <form class="form-inline">
-  <label class="mr-2" for="inlineFormCustomSelectPref">Preference</label>
-  <select class="custom-select mb-2 mr-sm-2" id="inlineFormCustomSelectPref">
+  <label class="my-1 mr-2" for="inlineFormCustomSelectPref">Preference</label>
+  <select class="custom-select my-1 mr-sm-2" id="inlineFormCustomSelectPref">
     <option selected>Choose...</option>
     <option value="1">One</option>
     <option value="2">Two</option>
     <option value="3">Three</option>
   </select>
 
-  <label class="custom-control custom-checkbox mb-2 mr-sm-2">
-    <input type="checkbox" class="custom-control-input">
-    <span class="custom-control-indicator"></span>
-    <span class="custom-control-description">Remember my preference</span>
-  </label>
+  <div class="custom-control custom-checkbox my-1 mr-sm-2">
+    <input type="checkbox" class="custom-control-input" id="customControlInline">
+    <label class="custom-control-label" for="customControlInline">Remember my preference</label>
+  </div>
 
-  <button type="submit" class="btn btn-primary mb-2">Submit</button>
+  <button type="submit" class="btn btn-primary my-1">Submit</button>
 </form>
 {% endexample %}
 
@@ -680,8 +673,9 @@ Add the `disabled` attribute to a `<fieldset>` to disable all the controls withi
       </select>
     </div>
     <div class="form-check">
-      <label class="form-check-label">
-        <input class="form-check-input" type="checkbox"> Can't check this
+      <input class="form-check-input" type="checkbox" id="disabledFieldsetCheck" disabled>
+      <label class="form-check-label" for="disabledFieldsetCheck">
+        Can't check this
       </label>
     </div>
     <button type="submit" class="btn btn-primary">Submit</button>
@@ -905,23 +899,18 @@ Our example forms show native textual `<input>`s above, but form validation styl
 
 {% example html %}
 <form class="was-validated">
-  <label class="custom-control custom-checkbox">
-    <input type="checkbox" class="custom-control-input" required>
-    <span class="custom-control-indicator"></span>
-    <span class="custom-control-description">Check this custom checkbox</span>
-  </label>
+  <div class="custom-control custom-checkbox">
+    <input type="checkbox" class="custom-control-input" id="customControlValidation1" required>
+    <label class="custom-control-label" for="customControlValidation1">Check this custom checkbox</label>
+  </div>
 
-  <div class="custom-controls-stacked d-block my-3">
-    <label class="custom-control custom-radio">
-      <input id="radioStacked1" name="radio-stacked" type="radio" class="custom-control-input" required>
-      <span class="custom-control-indicator"></span>
-      <span class="custom-control-description">Toggle this custom radio</span>
-    </label>
-    <label class="custom-control custom-radio">
-      <input id="radioStacked2" name="radio-stacked" type="radio" class="custom-control-input" required>
-      <span class="custom-control-indicator"></span>
-      <span class="custom-control-description">Or toggle this other custom radio</span>
-    </label>
+  <div class="custom-control custom-radio">
+    <input type="radio" class="custom-control-input" id="customControlValidation2" name="radio-stacked" required>
+    <label class="custom-control-label" for="customControlValidation2">Toggle this custom radio</label>
+  </div>
+  <div class="custom-control custom-radio">
+    <input type="radio" class="custom-control-input" id="customControlValidation3" name="radio-stacked" required>
+    <label class="custom-control-label" for="customControlValidation3">Or toggle this other custom radio</label>
   </div>
 
   <select class="custom-select d-block my-3" required>
@@ -944,36 +933,30 @@ For even more customization and cross browser consistency, use our completely cu
 
 ### Checkboxes and radios
 
-Each checkbox and radio is wrapped in a `<label>` for three reasons:
-
-- It provides a larger hit areas for checking the control.
-- It provides a helpful and semantic wrapper to help us replace the default `<input>`s.
-- It triggers the state of the `<input>` automatically, meaning no JavaScript is required.
+Each checkbox and radio is wrapped in a `<div>` with a sibling `<span>` to create our custom control and a `<label>` for the accompanying text. Structurally, this is the same approach as our default `.form-check`.
 
-We hide the default `<input>` with `opacity` and use the `.custom-control-indicator` to build a new custom form indicator in its place. Unfortunately we can't build a custom one from just the `<input>` because CSS's `content` doesn't work on that element.
+We use the sibling selector (`~`) for all our `<input>` states—like `:checked`—to properly style our custom form indicator. When combined with the `.custom-control-label` class, we can also style the text for each item based on the `<input>`'s state.
 
-We use the sibling selector (`~`) for all our `<input>` states—like `:checked`—to properly style our custom form indicator. When combined with the `.custom-control-description` class, we can also style the text for each item based on the `<input>`'s state.
+We hide the default `<input>` with `opacity` and use the `.custom-control-label` to build a new custom form indicator in its place with `::before` and `::after`. Unfortunately we can't build a custom one from just the `<input>` because CSS's `content` doesn't work on that element.
 
 In the checked states, we use **base64 embedded SVG icons** from [Open Iconic](https://useiconic.com/open). This provides us the best control for styling and positioning across browsers and devices.
 
 #### Checkboxes
 
 {% example html %}
-<label class="custom-control custom-checkbox">
-  <input type="checkbox" class="custom-control-input">
-  <span class="custom-control-indicator"></span>
-  <span class="custom-control-description">Check this custom checkbox</span>
-</label>
+<div class="custom-control custom-checkbox">
+  <input type="checkbox" class="custom-control-input" id="customCheck1">
+  <label class="custom-control-label" for="customCheck1">Check this custom checkbox</label>
+</div>
 {% endexample %}
 
 Custom checkboxes can also utilize the `:indeterminate` pseudo class when manually set via JavaScript (there is no available HTML attribute for specifying it).
 
 <div class="bd-example bd-example-indeterminate">
-  <label class="custom-control custom-checkbox">
-    <input type="checkbox" class="custom-control-input">
-    <span class="custom-control-indicator"></span>
-    <span class="custom-control-description">Check this custom checkbox</span>
-  </label>
+  <div class="custom-control custom-checkbox">
+    <input type="checkbox" class="custom-control-input" id="customCheck2">
+    <label class="custom-control-label" for="customCheck2">Check this custom checkbox</label>
+  </div>
 </div>
 
 If you're using jQuery, something like this should suffice:
@@ -985,52 +968,42 @@ $('.your-checkbox').prop('indeterminate', true)
 #### Radios
 
 {% example html %}
-<label class="custom-control custom-radio">
-  <input id="radio1" name="radio" type="radio" class="custom-control-input">
-  <span class="custom-control-indicator"></span>
-  <span class="custom-control-description">Toggle this custom radio</span>
-</label>
-<label class="custom-control custom-radio">
-  <input id="radio2" name="radio" type="radio" class="custom-control-input">
-  <span class="custom-control-indicator"></span>
-  <span class="custom-control-description">Or toggle this other custom radio</span>
-</label>
+<div class="custom-control custom-radio">
+  <input type="radio" id="customRadio1" name="customRadio" class="custom-control-input">
+  <label class="custom-control-label" for="customRadio1">Toggle this custom radio</label>
+</div>
+<div class="custom-control custom-radio">
+  <input type="radio" id="customRadio2" name="customRadio" class="custom-control-input">
+  <label class="custom-control-label" for="customRadio2">Or toggle this other custom radio</label>
+</div>
 {% endexample %}
 
-#### Disabled
-
-Custom checkboxes and radios can also be disabled. Add the `disabled` boolean attribute to the `<input>` and the custom indicator and label description will be automatically styled.
+#### Inline
 
 {% example html %}
-<label class="custom-control custom-checkbox">
-  <input type="checkbox" class="custom-control-input" disabled>
-  <span class="custom-control-indicator"></span>
-  <span class="custom-control-description">Check this custom checkbox</span>
-</label>
-
-<label class="custom-control custom-radio">
-  <input id="radio3" name="radioDisabled" type="radio" class="custom-control-input" disabled>
-  <span class="custom-control-indicator"></span>
-  <span class="custom-control-description">Toggle this custom radio</span>
-</label>
+<div class="custom-control custom-radio custom-control-inline">
+  <input type="radio" id="customRadioInline1" name="customRadioInline1" class="custom-control-input">
+  <label class="custom-control-label" for="customRadioInline1">Toggle this custom radio</label>
+</div>
+<div class="custom-control custom-radio custom-control-inline">
+  <input type="radio" id="customRadioInline2" name="customRadioInline1" class="custom-control-input">
+  <label class="custom-control-label" for="customRadioInline2">Or toggle this other custom radio</label>
+</div>
 {% endexample %}
 
-#### Stacked
+#### Disabled
 
-Custom checkboxes and radios are inline to start. Add a parent with class `.custom-controls-stacked` to ensure each form control is on separate lines.
+Custom checkboxes and radios can also be disabled. Add the `disabled` boolean attribute to the `<input>` and the custom indicator and label description will be automatically styled.
 
 {% example html %}
-<div class="custom-controls-stacked">
-  <label class="custom-control custom-radio">
-    <input id="radioStacked3" name="radio-stacked" type="radio" class="custom-control-input">
-    <span class="custom-control-indicator"></span>
-    <span class="custom-control-description">Toggle this custom radio</span>
-  </label>
-  <label class="custom-control custom-radio">
-    <input id="radioStacked4" name="radio-stacked" type="radio" class="custom-control-input">
-    <span class="custom-control-indicator"></span>
-    <span class="custom-control-description">Or toggle this other custom radio</span>
-  </label>
+<div class="custom-control custom-checkbox">
+  <input type="checkbox" class="custom-control-input" id="customCheckDisabled" disabled>
+  <label class="custom-control-label" for="customCheckDisabled">Check this custom checkbox</label>
+</div>
+
+<div class="custom-control custom-radio">
+  <input type="radio" id="radio3" name="radioDisabled" id="customRadioDisabled" class="custom-control-input" disabled>
+  <label class="custom-control-label" for="customRadioDisabled">Toggle this custom radio</label>
 </div>
 {% endexample %}
 
index 370cb3a0185ce77a519d25e2b7c3c69a6eeb3709..eccddccf9b00d2b30d722a3067d53a5ea88e1e25 100644 (file)
@@ -10,10 +10,21 @@ toc: true
 
 While Beta 2 saw the bulk of our breaking changes during the beta phase, but we still have a few that needed to be addressed in the Beta 3 release. These changes apply if you're updating to Beta 3 from Beta 2 or any older version of Bootstrap.
 
+### Miscellaneous
+
 - Removed the unused `$thumbnail-transition` variable. We weren't transitioning anything, so it was just extra code.
-- Changed the CSS for managing multiple `background-image`s on custom form checkboxes and radios. Previously, the `.custom-control-indicator` element had the background color, gradient, and SVG icon. Customizing the background gradient meant replacing all of those every time you needed to change just one. Now, we have `.custom-control-indicator` for the fill and gradient and `.custom-control-indicator::before` handles the icon.
 - The npm package no longer includes any files other than our source and dist files; if you relied on them and were running our scripts via the `node_modules` folder, you should adapt your workflow.
+
+### Forms
+
+- Rewrote both custom and default checkboxes and radios. Now, both have matching HTML structure (outer `<div>` with sibling `<input>` and `<label>`) and the same layout styles (stacked default, inline with modifier class). This allows us to style the label based on the input's state, simplifying support for the `disabled` attribute (previously requiring a parent class) and better supporting our form validation.
+
+  As part of this, we've changed the CSS for managing multiple `background-image`s on custom form checkboxes and radios. Previously, the now removed `.custom-control-indicator` element had the background color, gradient, and SVG icon. Customizing the background gradient meant replacing all of those every time you needed to change just one. Now, we have `.custom-control-label::before` for the fill and gradient and `.custom-control-label::after` handles the icon.
+
+  To make a custom check inline, add `.custom-control-inline`.
+
 - Updated selector for input-based button groups. Instead of `[data-toggle="buttons"] { }` for style and behavior, we use the `data` attribute just for JS behaviors and rely on a new `.btn-group-toggle` class for styling.
+
 - Removed `.col-form-legend` in favor of a slightly improved `.col-form-label`. This way `.col-form-label-sm` and `.col-form-label-lg` can be used on `<legend>` elements with ease.
 
 ### Input groups
index c752a9f1121b24ce1cff39ab0cbe40e444441521..e66638ba338a6197b7c7c4dc820864ae9777719a 100644 (file)
@@ -9,9 +9,13 @@
 
 .custom-control {
   position: relative;
-  display: inline-flex;
+  display: block;
   min-height: (1rem * $line-height-base);
   padding-left: $custom-control-gutter;
+}
+
+.custom-control-inline {
+  display: inline-flex;
   margin-right: $custom-control-spacer-x;
 }
 
   z-index: -1; // Put the input behind the label so it doesn't overlay text
   opacity: 0;
 
-  &:checked ~ .custom-control-indicator {
+  &:checked ~ .custom-control-label::before {
     color: $custom-control-indicator-checked-color;
     @include gradient-bg($custom-control-indicator-checked-bg);
     @include box-shadow($custom-control-indicator-checked-box-shadow);
   }
 
-  &:focus ~ .custom-control-indicator {
+  &:focus ~ .custom-control-label::before {
     // the mixin is not used here to make sure there is feedback
     box-shadow: $custom-control-indicator-focus-box-shadow;
   }
 
-  &:active ~ .custom-control-indicator {
+  &:active ~ .custom-control-label::before {
     color: $custom-control-indicator-active-color;
     background-color: $custom-control-indicator-active-bg;
     @include box-shadow($custom-control-indicator-active-box-shadow);
   }
 
   &:disabled {
-    ~ .custom-control-indicator {
-      background-color: $custom-control-indicator-disabled-bg;
-    }
+    ~ .custom-control-label {
+      color: $custom-control-label-disabled-color;
 
-    ~ .custom-control-description {
-      color: $custom-control-description-disabled-color;
+      &::before {
+        background-color: $custom-control-indicator-disabled-bg;
+      }
     }
   }
 }
 
-// Custom indicator
+// Custom control indicators
 //
-// Generates a shadow element to create our makeshift checkbox/radio background.
+// Build the custom controls out of psuedo-elements.
 
-.custom-control-indicator {
-  position: absolute;
-  top: (($line-height-base - $custom-control-indicator-size) / 2);
-  left: 0;
-  display: block;
-  width: $custom-control-indicator-size;
-  height: $custom-control-indicator-size;
-  pointer-events: none;
-  user-select: none;
-  background-color: $custom-control-indicator-bg;
-  @include box-shadow($custom-control-indicator-box-shadow);
+.custom-control-label {
+  margin-bottom: 0;
 
+  // Background-color and (when enabled) gradient
   &::before {
+    position: absolute;
+    top: (($line-height-base - $custom-control-indicator-size) / 2);
+    left: 0;
+    display: block;
+    width: $custom-control-indicator-size;
+    height: $custom-control-indicator-size;
+    pointer-events: none;
+    content: "";
+    user-select: none;
+    background-color: $custom-control-indicator-bg;
+    @include box-shadow($custom-control-indicator-box-shadow);
+  }
+
+  // Foreground (icon)
+  &::after {
+    position: absolute;
+    top: (($line-height-base - $custom-control-indicator-size) / 2);
+    left: 0;
     display: block;
     width: $custom-control-indicator-size;
     height: $custom-control-indicator-size;
   }
 }
 
+
 // Checkboxes
 //
 // Tweak just a few things for checkboxes.
 
 .custom-checkbox {
-  .custom-control-indicator {
+  .custom-control-label::before {
     @include border-radius($custom-checkbox-indicator-border-radius);
   }
 
-  .custom-control-input:checked ~ .custom-control-indicator {
-    @include gradient-bg($custom-control-indicator-checked-bg);
-
+  .custom-control-input:checked ~ .custom-control-label {
     &::before {
+      @include gradient-bg($custom-control-indicator-checked-bg);
+    }
+    &::after {
       background-image: $custom-checkbox-indicator-icon-checked;
     }
   }
 
-  .custom-control-input:indeterminate ~ .custom-control-indicator {
-    @include gradient-bg($custom-checkbox-indicator-indeterminate-bg);
-    @include box-shadow($custom-checkbox-indicator-indeterminate-box-shadow);
-
+  .custom-control-input:indeterminate ~ .custom-control-label {
     &::before {
+      @include gradient-bg($custom-checkbox-indicator-indeterminate-bg);
+      @include box-shadow($custom-checkbox-indicator-indeterminate-box-shadow);
+    }
+    &::after {
       background-image: $custom-checkbox-indicator-icon-indeterminate;
     }
   }
 // Tweak just a few things for radios.
 
 .custom-radio {
-  .custom-control-indicator {
+  .custom-control-label::before {
     border-radius: $custom-radio-indicator-border-radius;
   }
 
-  .custom-control-input:checked ~ .custom-control-indicator {
-    @include gradient-bg($custom-control-indicator-checked-bg);
-
+  .custom-control-input:checked ~ .custom-control-label {
     &::before {
-      background-image: $custom-radio-indicator-icon-checked;
+      @include gradient-bg($custom-control-indicator-checked-bg);
     }
-  }
-}
-
-
-// Layout options
-//
-// By default radios and checkboxes are `inline-block` with no additional spacing
-// set. Use these optional classes to tweak the layout.
-
-.custom-controls-stacked {
-  display: flex;
-  flex-direction: column;
-
-  .custom-control {
-    margin-bottom: $custom-control-spacer-y;
-
-    + .custom-control {
-      margin-left: 0;
+    &::after {
+      background-image: $custom-radio-indicator-icon-checked;
     }
   }
 }
index 9cec3ac9c710d4f111e4e50d19d0086f93df90d6..72bde5784e03843a0b94608867784adf45a582b1 100644 (file)
@@ -207,33 +207,35 @@ select.form-control-lg {
 .form-check {
   position: relative;
   display: block;
-  margin-bottom: $form-check-margin-bottom;
-
-  &.disabled {
-    .form-check-label {
-      color: $text-muted;
-    }
-  }
-}
-
-.form-check-label {
   padding-left: $form-check-input-gutter;
-  margin-bottom: 0; // Override default `<label>` bottom margin
 }
 
 .form-check-input {
   position: absolute;
   margin-top: $form-check-input-margin-y;
   margin-left: -$form-check-input-gutter;
+
+  &:disabled ~ .form-check-label {
+    color: $text-muted;
+  }
+}
+
+.form-check-label {
+  margin-bottom: 0; // Override default `<label>` bottom margin
 }
 
-// Radios and checkboxes on same line
 .form-check-inline {
-  display: inline-block;
+  display: inline-flex;
+  align-items: center;
+  padding-left: 0; // Override base .form-check
   margin-right: $form-check-inline-margin-x;
 
-  .form-check-label {
-    vertical-align: middle;
+  // Undo .form-check-input defaults and add some `margin-right`.
+  .form-check-input {
+    position: static;
+    margin-top: 0;
+    margin-right: $form-check-inline-input-margin-x;
+    margin-left: 0;
   }
 }
 
@@ -310,10 +312,6 @@ select.form-control-lg {
       align-items: center;
       justify-content: center;
       width: auto;
-      margin-top: 0;
-      margin-bottom: 0;
-    }
-    .form-check-label {
       padding-left: 0;
     }
     .form-check-input {
@@ -323,23 +321,12 @@ select.form-control-lg {
       margin-left: 0;
     }
 
-    // Custom form controls
     .custom-control {
-      display: flex;
       align-items: center;
       justify-content: center;
-      padding-left: 0;
-    }
-    .custom-control-indicator {
-      position: static;
-      display: inline-block;
-      margin-right: $form-check-input-margin-x; // Flexbox alignment means we lose our HTML space here, so we compensate.
-      vertical-align: text-bottom;
     }
-
-    // Re-override the feedback icon.
-    .has-feedback .form-control-feedback {
-      top: 0;
+    .custom-control-label {
+      margin-bottom: 0;
     }
   }
 }
index be3dd69b5f39cfb92cafc344285fb5e5d5a78d12..8290c96fc08a67819729d7d1a07bd17c6699d78a 100644 (file)
@@ -423,12 +423,12 @@ $input-transition:                      border-color .15s ease-in-out, box-shado
 
 $form-text-margin-top:                  .25rem !default;
 
-$form-check-margin-bottom:              .5rem !default;
 $form-check-input-gutter:               1.25rem !default;
-$form-check-input-margin-y:             .25rem !default;
+$form-check-input-margin-y:             .3rem !default;
 $form-check-input-margin-x:             .25rem !default;
 
 $form-check-inline-margin-x:            .75rem !default;
+$form-check-inline-input-margin-x:      .3125rem !default;
 
 $form-group-margin-bottom:              1rem !default;
 
@@ -437,7 +437,6 @@ $input-group-addon-bg:                  $gray-200 !default;
 $input-group-addon-border-color:        $input-border-color !default;
 
 $custom-control-gutter:                 1.5rem !default;
-$custom-control-spacer-y:               .25rem !default;
 $custom-control-spacer-x:               1rem !default;
 
 $custom-control-indicator-size:         1rem !default;
@@ -446,7 +445,7 @@ $custom-control-indicator-bg-size:      50% 50% !default;
 $custom-control-indicator-box-shadow:   inset 0 .25rem .25rem rgba($black, .1) !default;
 
 $custom-control-indicator-disabled-bg:          $gray-200 !default;
-$custom-control-description-disabled-color:     $gray-600 !default;
+$custom-control-label-disabled-color:     $gray-600 !default;
 
 $custom-control-indicator-checked-color:        $white !default;
 $custom-control-indicator-checked-bg:           theme-color("primary") !default;
index 470f80c15c4237984c492475ee41db7c890e9d83..ba1b16d6a1a08d883af40db6d905b09f7d178a72 100644 (file)
     }
   }
 
-
-  // TODO: redo check markup lol crap
   .form-check-input {
     .was-validated &:#{$state},
     &.is-#{$state} {
-      + .form-check-label {
+      ~ .form-check-label {
         color: $color;
       }
     }
   }
 
-  // custom radios and checks
   .custom-control-input {
     .was-validated &:#{$state},
     &.is-#{$state} {
-      ~ .custom-control-indicator {
-        background-color: lighten($color, 25%);
-      }
-      ~ .custom-control-description {
+      ~ .custom-control-label {
         color: $color;
+
+        &::before {
+          background-color: lighten($color, 25%);
+        }
       }
       &:checked {
-        ~ .custom-control-indicator {
+        ~ .custom-control-label::before {
           @include gradient-bg(lighten($color, 10%));
         }
       }
       &:focus {
-        ~ .custom-control-indicator {
+        ~ .custom-control-label::before {
           box-shadow: 0 0 0 1px $body-bg, 0 0 0 $input-focus-width rgba($color, .25);
         }
       }