@import "forms/form-control";
@import "forms/form-select";
@import "forms/form-check";
-@import "forms/form-file";
@import "forms/form-range";
@import "forms/floating-labels";
@import "forms/input-group";
$form-range-thumb-disabled-bg: $gray-500 !default;
$form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
-$form-file-height: $input-height !default;
-$form-file-focus-border-color: $input-focus-border-color !default;
-$form-file-focus-box-shadow: $input-focus-box-shadow !default;
-$form-file-disabled-bg: $input-disabled-bg !default;
-$form-file-disabled-border-color: $input-disabled-border-color !default;
-
-$form-file-padding-y: $input-padding-y !default;
-$form-file-padding-x: $input-padding-x !default;
-$form-file-line-height: $input-line-height !default;
-$form-file-font-family: $input-font-family !default;
-$form-file-font-weight: $input-font-weight !default;
-$form-file-color: $input-color !default;
-$form-file-bg: $input-bg !default;
-$form-file-border-width: $input-border-width !default;
-$form-file-border-color: $input-border-color !default;
-$form-file-border-radius: $input-border-radius !default;
-$form-file-box-shadow: $input-box-shadow !default;
-$form-file-button-color: $form-file-color !default;
+$form-file-button-color: $input-color !default;
$form-file-button-bg: $input-group-addon-bg !default;
-
-$form-file-padding-y-sm: $input-padding-y-sm !default;
-$form-file-padding-x-sm: $input-padding-x-sm !default;
-$form-file-font-size-sm: $input-font-size-sm !default;
-$form-file-height-sm: $input-height-sm !default;
-
-$form-file-padding-y-lg: $input-padding-y-lg !default;
-$form-file-padding-x-lg: $input-padding-x-lg !default;
-$form-file-font-size-lg: $input-font-size-lg !default;
-$form-file-height-lg: $input-height-lg !default;
+$form-file-button-hover-bg: shade-color($input-group-addon-bg, 5%) !default;
$form-floating-height: add(3.5rem, $input-height-border) !default;
$form-floating-padding-x: $input-padding-x !default;
@include box-shadow($input-box-shadow);
@include transition($input-transition);
+ &[type="file"] {
+ overflow: hidden; // prevent pseudo element button overlap
+
+ &:not(:disabled):not([readonly]) {
+ cursor: pointer;
+ }
+ }
+
// Customize the `:focus` state to imitate native WebKit styles.
&:focus {
color: $input-focus-color;
// iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
opacity: 1;
}
+
+ // File input buttons theming
+ // stylelint-disable-next-line selector-pseudo-element-no-unknown
+ &::file-selector-button {
+ padding: $input-padding-y $input-padding-x;
+ margin: (-$input-padding-y) (-$input-padding-x);
+ margin-inline-end: $input-padding-x;
+ line-height: inherit;
+ color: $form-file-button-color;
+ @include gradient-bg($form-file-button-bg);
+ pointer-events: none;
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+ border-inline-end-width: $input-border-width;
+ border-radius: 0; // stylelint-disable-line property-disallowed-list
+ @include transition($btn-transition);
+ }
+
+ // stylelint-disable-next-line selector-pseudo-element-no-unknown
+ &:hover:not(:disabled):not([readonly])::file-selector-button {
+ background-color: $form-file-button-hover-bg;
+ }
+
+ &::-webkit-file-upload-button {
+ padding: $input-padding-y $input-padding-x;
+ margin: (-$input-padding-y) (-$input-padding-x);
+ margin-inline-end: $input-padding-x;
+ line-height: inherit;
+ color: $form-file-button-color;
+ @include gradient-bg($form-file-button-bg);
+ pointer-events: none;
+ border-color: inherit;
+ border-style: solid;
+ border-width: 0;
+ border-inline-end-width: $input-border-width;
+ border-radius: 0; // stylelint-disable-line property-disallowed-list
+ @include transition($btn-transition);
+ }
+
+ &:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {
+ background-color: $form-file-button-hover-bg;
+ }
}
// Readonly controls as plain text
padding: $input-padding-y-sm $input-padding-x-sm;
@include font-size($input-font-size-sm);
@include border-radius($input-border-radius-sm);
+
+ // stylelint-disable-next-line selector-pseudo-element-no-unknown
+ &::file-selector-button {
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
+ margin-inline-end: $input-padding-x-sm;
+ }
+
+ &::-webkit-file-upload-button {
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
+ margin-inline-end: $input-padding-x-sm;
+ }
}
.form-control-lg {
padding: $input-padding-y-lg $input-padding-x-lg;
@include font-size($input-font-size-lg);
@include border-radius($input-border-radius-lg);
+
+ // stylelint-disable-next-line selector-pseudo-element-no-unknown
+ &::file-selector-button {
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
+ margin-inline-end: $input-padding-x-lg;
+ }
+
+ &::-webkit-file-upload-button {
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
+ margin-inline-end: $input-padding-x-lg;
+ }
}
.form-control-color {
+++ /dev/null
-.form-file {
- --#{$variable-prefix}form-file-height: #{$form-file-height};
- position: relative;
-}
-
-.form-file-input {
- position: relative;
- z-index: 2;
- width: 100%;
- height: var(--#{$variable-prefix}form-file-height);
- margin: 0;
- opacity: 0;
-
- &:focus-within ~ .form-file-label {
- border-color: $form-file-focus-border-color;
- box-shadow: $form-file-focus-box-shadow;
- }
-
- // Use disabled attribute in addition of :disabled pseudo-class
- // See: https://github.com/twbs/bootstrap/issues/28247
- &[disabled] ~ .form-file-label .form-file-text,
- &:disabled ~ .form-file-label .form-file-text {
- background-color: $form-file-disabled-bg;
- border-color: $form-file-disabled-border-color;
- }
-}
-
-.form-file-label {
- position: absolute;
- top: 0;
- right: 0;
- left: 0;
- z-index: 1;
- display: flex;
- height: var(--#{$variable-prefix}form-file-height);
- border-color: $form-file-border-color;
- @include border-radius($form-file-border-radius);
- @include box-shadow($form-file-box-shadow);
-}
-
-.form-file-text {
- display: block;
- flex-grow: 1;
- padding: $form-file-padding-y $form-file-padding-x;
- overflow: hidden;
- font-family: $form-file-font-family;
- font-weight: $form-file-font-weight;
- line-height: $form-file-line-height;
- color: $form-file-color;
- text-overflow: ellipsis;
- white-space: nowrap;
- background-color: $form-file-bg;
- border-color: inherit;
- border-style: solid;
- border-width: $form-file-border-width;
- @include border-left-radius(inherit);
-}
-
-.form-file-button {
- display: block;
- flex-shrink: 0;
- padding: $form-file-padding-y $form-file-padding-x;
- margin-left: -$form-file-border-width;
- line-height: $form-file-line-height;
- color: $form-file-button-color;
- @include gradient-bg($form-file-button-bg);
- border-color: inherit;
- border-style: solid;
- border-width: $form-file-border-width;
- @include border-right-radius(inherit);
-}
-
-.form-file-sm {
- --#{$variable-prefix}form-file-height: #{$form-file-height-sm};
- @include font-size($form-file-font-size-sm);
-
- .form-file-text,
- .form-file-button {
- padding: $form-file-padding-y-sm $form-file-padding-x-sm;
- }
-}
-
-.form-file-lg {
- --#{$variable-prefix}form-file-height: #{$form-file-height-lg};
- @include font-size($form-file-font-size-lg);
-
- .form-file-text,
- .form-file-button {
- padding: $form-file-padding-y-lg $form-file-padding-x-lg;
- }
-}
width: 100%;
> .form-control,
- > .form-select,
- > .form-file {
+ > .form-select {
position: relative; // For focus state's z-index
flex: 1 1 auto;
width: 1%;
// Bring the "active" form control to the top of surrounding elements
> .form-control:focus,
- > .form-select:focus,
- > .form-file .form-file-input:focus ~ .form-file-label {
+ > .form-select:focus {
z-index: 3;
}
- // Bring the custom file input above the label
- > .form-file {
- > .form-file-input:focus {
- z-index: 4;
- }
-
- &:not(:last-child) > .form-file-label {
- @include border-right-radius(0);
- }
-
- &:not(:first-child) > .form-file-label {
- @include border-left-radius(0);
- }
- }
-
// Ensure buttons are always above inputs for more visually pleasing borders.
// This isn't needed for `.input-group-text` since it shares the same border-color
// as our inputs.
margin-left: .5em;
}
}
-
- // custom file
- .form-file-input {
- @include form-validation-state-selector($state) {
- ~ .form-file-label {
- border-color: $color;
- }
-
- &:focus {
- ~ .form-file-label {
- border-color: $color;
- box-shadow: 0 0 0 $input-focus-width rgba($color, .25);
- }
- }
- }
- }
}
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
-/* global ClipboardJS: false, anchors: false, bootstrap: false, bsCustomFileInput: false */
+/* global ClipboardJS: false, anchors: false, bootstrap: false */
(function () {
'use strict'
icon: '#'
}
anchors.add('.bd-content > h2, .bd-content > h3, .bd-content > h4, .bd-content > h5')
-
- bsCustomFileInput.init()
})()
+++ /dev/null
-/*!
- * bsCustomFileInput v1.3.4 (https://github.com/Johann-S/bs-custom-file-input)
- * Copyright 2018 - 2020 Johann-S <johann.servoire@gmail.com>
- * Licensed under MIT (https://github.com/Johann-S/bs-custom-file-input/blob/master/LICENSE)
- */
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).bsCustomFileInput=t()}(this,function(){"use strict";var s={CUSTOMFILE:'.custom-file input[type="file"]',CUSTOMFILELABEL:".custom-file-label",FORM:"form",INPUT:"input"},l=function(e){if(0<e.childNodes.length)for(var t=[].slice.call(e.childNodes),n=0;n<t.length;n++){var l=t[n];if(3!==l.nodeType)return l}return e},u=function(e){var t=e.bsCustomFileInput.defaultText,n=e.parentNode.querySelector(s.CUSTOMFILELABEL);n&&(l(n).textContent=t)},n=!!window.File,r=function(e){if(e.hasAttribute("multiple")&&n)return[].slice.call(e.files).map(function(e){return e.name}).join(", ");if(-1===e.value.indexOf("fakepath"))return e.value;var t=e.value.split("\\");return t[t.length-1]};function d(){var e=this.parentNode.querySelector(s.CUSTOMFILELABEL);if(e){var t=l(e),n=r(this);n.length?t.textContent=n:u(this)}}function v(){for(var e=[].slice.call(this.querySelectorAll(s.INPUT)).filter(function(e){return!!e.bsCustomFileInput}),t=0,n=e.length;t<n;t++)u(e[t])}var p="bsCustomFileInput",m="reset",h="change";return{init:function(e,t){void 0===e&&(e=s.CUSTOMFILE),void 0===t&&(t=s.FORM);for(var n,l,r=[].slice.call(document.querySelectorAll(e)),i=[].slice.call(document.querySelectorAll(t)),o=0,u=r.length;o<u;o++){var c=r[o];Object.defineProperty(c,p,{value:{defaultText:(n=void 0,n="",(l=c.parentNode.querySelector(s.CUSTOMFILELABEL))&&(n=l.textContent),n)},writable:!0}),d.call(c),c.addEventListener(h,d)}for(var f=0,a=i.length;f<a;f++)i[f].addEventListener(m,v),Object.defineProperty(i[f],p,{value:!0,writable:!0})},destroy:function(){for(var e=[].slice.call(document.querySelectorAll(s.FORM)).filter(function(e){return!!e.bsCustomFileInput}),t=[].slice.call(document.querySelectorAll(s.INPUT)).filter(function(e){return!!e.bsCustomFileInput}),n=0,l=t.length;n<l;n++){var r=t[n];u(r),r[p]=void 0,r.removeEventListener(h,d)}for(var i=0,o=e.length;i<o;i++)e[i].removeEventListener(m,v),e[i][p]=void 0}}});
-//# sourceMappingURL=bs-custom-file-input.min.js.map
+++ /dev/null
----
-layout: docs
-title: File browser
-description: Use our custom file inputs for consistent cross-browser styling, built-in customization, and lightweight JavaScript.
-group: forms
-toc: true
----
-
-{{< callout info >}}
-The recommended plugin to animate custom file inputs is [bs-custom-file-input](https://www.npmjs.com/package/bs-custom-file-input); it's what we use here in our docs.
-{{< /callout >}}
-
-## Default
-
-The file input is the most gnarly of the bunch and requires additional JavaScript if you'd like to hook them up with functional *Choose file...* and selected file name text.
-
-{{< example >}}
-<div class="form-file">
- <input type="file" class="form-file-input" id="customFile">
- <label class="form-file-label" for="customFile">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
-</div>
-{{< /example >}}
-
-Add the `disabled` attribute to the `<input>` and the custom markup will be updated to appear disabled.
-
-{{< example >}}
-<div class="form-file">
- <input type="file" class="form-file-input" id="customFileDisabled" disabled>
- <label class="form-file-label" for="customFileDisabled">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
-</div>
-{{< /example >}}
-
-Longer placeholder text is truncated and an ellipsis is added when there's not enough space.
-
-{{< example >}}
-<div class="form-file">
- <input type="file" class="form-file-input" id="customFileLong">
- <label class="form-file-label" for="customFileLong">
- <span class="form-file-text">Lorem ipsum posuere consectetur est at lobortis nulla vitae elit libero a pharetra augue fusce dapibus tellus ac cursus commodo tortor mauris condimentum nibh ut fermentum massa justo sit amet risus cras mattis consectetur purus sit amet fermentum</span>
- <span class="form-file-button">Browse</span>
- </label>
-</div>
-{{< /example >}}
-
-We hide the default file `<input>` via `opacity` and instead style the `<label>`, and declare a `width` and `height` on the `<input>` for proper spacing for surrounding content.
-
-## Sizing
-
-You may also choose from small and large file inputs to match our similarly sized text inputs.
-
-{{< example >}}
-<div class="form-file form-file-lg mb-3">
- <input type="file" class="form-file-input" id="customFileLg">
- <label class="form-file-label" for="customFileLg">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
-</div>
-
-<div class="form-file form-file-sm">
- <input type="file" class="form-file-input" id="customFileSm">
- <label class="form-file-label" for="customFileSm">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
-</div>
-{{< /example >}}
</form>
{{< /example >}}
+## File input
+
+{{< example >}}
+<div class="mb-3">
+ <label for="formFile" class="form-label">Default file input example</label>
+ <input class="form-control" type="file" id="formFile">
+</div>
+<div class="mb-3">
+ <label for="formFileMultiple" class="form-label">Multiple files input example</label>
+ <input class="form-control" type="file" id="formFileMultiple" multiple>
+</div>
+<div class="mb-3">
+ <label for="formFileDisabled" class="form-label">Disabled file input example</label>
+ <input class="form-control" type="file" id="formFileDisabled" disabled>
+</div>
+<div class="mb-3">
+ <label for="formFileSm" class="form-label">Small file input example</label>
+ <input class="form-control form-control-sm" id="formFileSm" type="file">
+</div>
+<div>
+ <label for="formFileLg" class="form-label">Large file input example</label>
+ <input class="form-control form-control-lg" id="formFileLg" type="file">
+</div>
+{{< /example >}}
+
## Color
{{< example >}}
{{< example >}}
<div class="input-group mb-3">
<span class="input-group-text" id="inputGroupFileAddon01">Upload</span>
- <div class="form-file">
- <input type="file" class="form-file-input" id="inputGroupFile01" aria-describedby="inputGroupFileAddon01">
- <label class="form-file-label" for="inputGroupFile01">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
- </div>
+ <input type="file" class="form-control" id="inputGroupFile01" aria-describedby="inputGroupFileAddon01">
</div>
<div class="input-group mb-3">
- <div class="form-file">
- <input type="file" class="form-file-input" id="inputGroupFile02">
- <label class="form-file-label" for="inputGroupFile02" aria-describedby="inputGroupFileAddon02">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
- </div>
+ <input type="file" class="form-control" id="inputGroupFile02" aria-describedby="inputGroupFileAddon02">
<span class="input-group-text" id="inputGroupFileAddon02">Upload</span>
</div>
<div class="input-group mb-3">
<button class="btn btn-outline-secondary" type="button" id="inputGroupFileAddon03">Button</button>
- <div class="form-file">
- <input type="file" class="form-file-input" id="inputGroupFile03" aria-describedby="inputGroupFileAddon03">
- <label class="form-file-label" for="inputGroupFile03">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
- </div>
+ <input type="file" class="form-control" id="inputGroupFile03" aria-describedby="inputGroupFileAddon03">
</div>
<div class="input-group">
- <div class="form-file">
- <input type="file" class="form-file-input" id="inputGroupFile04" aria-describedby="inputGroupFileAddon04">
- <label class="form-file-label" for="inputGroupFile04">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
- </div>
+ <input type="file" class="form-control" id="inputGroupFile04" aria-describedby="inputGroupFileAddon04">
<button class="btn btn-outline-secondary" type="button" id="inputGroupFileAddon04">Button</button>
</div>
{{< /example >}}
description: Improve browser default select elements with a custom initial appearance.
- title: Checks & radios
description: Use our custom radio buttons and checkboxes in forms for selecting input options.
- - title: File
- description: Replace browser default file inputs with our custom version with optional JavaScript.
- title: Range
description: Replace browser default range inputs with our custom version.
- title: Input group
- `<input>`s and `<textarea>`s with `.form-control` (including up to one `.form-control` in input groups)
- `<select>`s with `.form-select`
- `.form-check`s
-- `.form-file`
{{< example >}}
<form class="was-validated">
<div class="invalid-feedback">Example invalid select feedback</div>
</div>
- <div class="form-file mb-3">
- <input type="file" class="form-file-input" id="validationFormFile" required>
- <label class="form-file-label" for="validationFormFile">
- <span class="form-file-text">Choose file...</span>
- <span class="form-file-button">Browse</span>
- </label>
+ <div class="mb-3">
+ <input type="file" class="form-control" aria-label="file example" required>
<div class="invalid-feedback">Example invalid form file feedback</div>
</div>
- The longstanding [Missing border radius on input group with validation feedback bug](https://github.com/twbs/bootstrap/issues/25110) is finally fixed by adding an additional `.has-validation` class to input groups with validation.
- Promoted the Floating labels example to fully supported form component. [See the new Floating labels page.]({{< docsref "/forms/floating-labels" >}})
+- File inputs now use the `.form-control` class and don't require JavaScript, additional HTML, or additional classes. [See #31955](https://github.com/twbs/bootstrap/pull/31955).
### Utilities
- Updated file input component with the same overall design, but improved HTML.
- Refactored `.form-file` markup to resolve some visual bugs while allowing translation and button text changes via HTML instead of CSS.
- Dropped native `.form-control-file` and `.form-control-range` components entirely.
- - Renamed `.custom-file` to `.form-file` (including variables).
+ - Renamed `.custom-file` to `.form-file` (including variables). **Watch out:** `.form-file` was later on dropped in `v5.0.0-alpha3`, now you can use `.form-control`.
- Added support for `:focus` and `:disabled` styles.
- Renamed `.custom-range` to `.form-range` (including variables).
- Dropped `.form-group` for margin utilities (we've replaced our docs examples with `.mb-3`).
- title: Form control
- title: Select
- title: Checks & radios
- - title: File
- title: Range
- title: Input group
- title: Floating labels