rstudio / bslib

Tools for theming Shiny and R Markdown via Bootstrap 3, 4, or 5.
https://rstudio.github.io/bslib/
Other
466 stars 57 forks source link

Shiny preset: Spacing in check/radio inputs #712

Closed cpsievert closed 1 year ago

cpsievert commented 1 year ago
  1. Labels are too close to the choices (because of this https://github.com/rstudio/shiny/blob/main/inst/www/shared/shiny_scss/shiny.scss#L344-L356)

Screenshot 2023-07-21 at 10 52 43 AM

  1. Choices are too close to one another when inline = TRUE

Screenshot 2023-07-21 at 2 21 31 PM

Screenshot 2023-07-21 at 2 21 20 PM

gadenbuie commented 1 year ago

These styles are making the checkboxes too close to the checkbox group label

/* For checkbox groups and radio buttons, bring the options closer to label,
   if label is present. */
.shiny-input-checkboxgroup label ~ .shiny-options-group,
.shiny-input-radiogroup label ~ .shiny-options-group {
  margin-top: -10px;
}

/* Checkbox groups and radios that are inline need less negative margin to
   separate from label. */
.shiny-input-checkboxgroup.shiny-input-container-inline label ~ .shiny-options-group,
.shiny-input-radiogroup.shiny-input-container-inline label ~ .shiny-options-group {
  margin-top: -1px;
}

They were added back when migrating to Bootstrap 3

They also interact with these rules

https://github.com/rstudio/bslib/blob/ea7d5509ef3b12ee753807de95374710a3b8f88f/inst/lib/bs5/scss/forms/_form-check.scss#L29-L32

where $form-check-input-selector is set over here

https://github.com/rstudio/bslib/blob/ea7d5509ef3b12ee753807de95374710a3b8f88f/inst/lib/bs5/scss/_forms.scss#L5-L7

In short: the negative margin-top adjusts the placement of the checkbox to be in line with the text label. Without that margin-top adjustment, the check boxes would be correctly spaced, but out of line with their labels.

The above is actually for BS5. BS4 takes a different path in both Bootstrap and bslib. These lines in Bootstrap set the margin

https://github.com/rstudio/bslib/blob/ea7d5509ef3b12ee753807de95374710a3b8f88f/inst/lib/bs4/scss/_forms.scss#L229-L231

and then bslib extends the .form-check-input class for BS4.

https://github.com/rstudio/bslib/blob/ea7d5509ef3b12ee753807de95374710a3b8f88f/inst/bs3compat/_shiny_input.scss#L10-L21

To set the correct negative margin for all versions of Bootstrap in a themeable way, we need to undo both the margin-top adjustment and also take into account the top border width of the checkbox/radio input. The border width is driven by $input-border-width in BS4 and $input-btn-border-width in BS5

gadenbuie commented 1 year ago

After much investigation, I think most of the issues described in this thread are the result of the markup choices for checkbox and radio groups.

Where we use

<div id="checkbox2" class="form-group shiny-input-checkboxgroup shiny-input-container shiny-bound-input" role="group" aria-labelledby="checkbox2-label">
  <label class="control-label" id="checkbox2-label" for="checkbox2">Check this out</label>
  <div class="shiny-options-group">
    <div class="checkbox">
      <label>
        <input type="checkbox" name="checkbox2" value="aenean">
        <span>aenean</span>
      </label>
    </div>
    <div class="checkbox">
      <label>
        <input type="checkbox" name="checkbox2" value="magna nullam sem">
        <span>magna nullam sem</span>
      </label>
    </div>
  </div>
</div>

in Bootstrap 5 (at least), the preferred markup is closer to the following where the primary difference is using <fieldset><legend></legend><!-- radio groups --></fieldset> (note: the following example is for styling purpose and doesn't include all required id/aria attributes)

<fieldset>
  <legend class="col-form-label">Check this out</legend>
  <div class="shiny-option-group">
    <div class="form-check">
      <label class="form-check-label">
        <input class="form-check-input" type="checkbox" name="checkbox2" value="aenean">
        <span>aenean</span>
      </label>
    </div>
    <div class="form-check">
      <label class="form-check-label">
        <input class="form-check-input" type="checkbox" name="checkbox2" value="magna nullam sem">
        <span>magna nullam sem</span>
      </label>
    </div>
  </div>
</fieldset>

The biggest advantage of using the above markup is that inline radio and check groups are super easy and do everything you'd expect (wrapping inputs, indented wrapped lines in labels, etc.):

.shiny-input-container-inline .shiny-option-group {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  flex-direction: row;
}

For now I've worked on fixing behavior in bslib for Bootstrap 4 and 5 and specifically styling the Shiny preset in the way I'd like to see all inline check/radio groups styled. Without changing the markup, we're swimming against Bootstrap styles with hacks like the negative margin-top in shiny's css, and the cleanest way to ensure consistent behavior across all Bootswatch themes would be to use the <fieldset> markup.

cpsievert commented 1 year ago

Thanks @gadenbuie. Would you mind creating a new issue with that last comment and use it as an argument for bslib to gain a input_checkbox(), etc. (that or have Shiny change it's markup)?

github-actions[bot] commented 11 months ago

This issue has been automatically locked. If you have found a related problem, please open a new issue (with a reproducible example or feature request) and link to this issue. :raising_hand: Need help? Connect with us on Discord or Posit Community.