Using RTLCSS directives, renaming things to use logical names and following best practices.
},
{
"path": "./dist/css/bootstrap-reboot.css",
- "maxSize": "2 kB"
+ "maxSize": "2.25 kB"
},
{
"path": "./dist/css/bootstrap-reboot.min.css",
},
{
"path": "./dist/js/bootstrap.min.js",
- "maxSize": "15.5 kB"
+ "maxSize": "15.75 kB"
}
],
"ci": {
const CLASS_NAME_CAROUSEL = 'carousel'
const CLASS_NAME_ACTIVE = 'active'
const CLASS_NAME_SLIDE = 'slide'
-const CLASS_NAME_RIGHT = 'carousel-item-right'
-const CLASS_NAME_LEFT = 'carousel-item-left'
+const CLASS_NAME_END = 'carousel-item-end'
+const CLASS_NAME_START = 'carousel-item-start'
const CLASS_NAME_NEXT = 'carousel-item-next'
const CLASS_NAME_PREV = 'carousel-item-prev'
const CLASS_NAME_POINTER_EVENT = 'pointer-event'
let eventDirectionName
if (direction === DIRECTION_NEXT) {
- directionalClassName = CLASS_NAME_LEFT
+ directionalClassName = CLASS_NAME_START
orderClassName = CLASS_NAME_NEXT
eventDirectionName = DIRECTION_LEFT
} else {
- directionalClassName = CLASS_NAME_RIGHT
+ directionalClassName = CLASS_NAME_END
orderClassName = CLASS_NAME_PREV
eventDirectionName = DIRECTION_RIGHT
}
getElementFromSelector,
isElement,
isVisible,
+ isRTL,
noop,
typeCheckConfig
} from './util/index'
const CLASS_NAME_DISABLED = 'disabled'
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_DROPUP = 'dropup'
-const CLASS_NAME_DROPRIGHT = 'dropright'
-const CLASS_NAME_DROPLEFT = 'dropleft'
-const CLASS_NAME_MENURIGHT = 'dropdown-menu-right'
+const CLASS_NAME_DROPEND = 'dropend'
+const CLASS_NAME_DROPSTART = 'dropstart'
+const CLASS_NAME_MENUEND = 'dropdown-menu-end'
const CLASS_NAME_NAVBAR = 'navbar'
const CLASS_NAME_POSITION_STATIC = 'position-static'
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
-const PLACEMENT_TOP = 'top-start'
-const PLACEMENT_TOPEND = 'top-end'
-const PLACEMENT_BOTTOM = 'bottom-start'
-const PLACEMENT_BOTTOMEND = 'bottom-end'
-const PLACEMENT_RIGHT = 'right-start'
-const PLACEMENT_LEFT = 'left-start'
+const PLACEMENT_TOP = isRTL ? 'top-end' : 'top-start'
+const PLACEMENT_TOPEND = isRTL ? 'top-start' : 'top-end'
+const PLACEMENT_BOTTOM = isRTL ? 'bottom-end' : 'bottom-start'
+const PLACEMENT_BOTTOMEND = isRTL ? 'bottom-start' : 'bottom-end'
+const PLACEMENT_RIGHT = isRTL ? 'left-start' : 'right-start'
+const PLACEMENT_LEFT = isRTL ? 'right-start' : 'left-start'
const Default = {
offset: 0,
// Handle dropup
if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
- placement = this._menu.classList.contains(CLASS_NAME_MENURIGHT) ?
+ placement = this._menu.classList.contains(CLASS_NAME_MENUEND) ?
PLACEMENT_TOPEND :
PLACEMENT_TOP
- } else if (parentDropdown.classList.contains(CLASS_NAME_DROPRIGHT)) {
+ } else if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
placement = PLACEMENT_RIGHT
- } else if (parentDropdown.classList.contains(CLASS_NAME_DROPLEFT)) {
+ } else if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {
placement = PLACEMENT_LEFT
- } else if (this._menu.classList.contains(CLASS_NAME_MENURIGHT)) {
+ } else if (this._menu.classList.contains(CLASS_NAME_MENUEND)) {
placement = PLACEMENT_BOTTOMEND
}
getElementFromSelector,
getTransitionDurationFromElement,
isVisible,
+ isRTL,
reflow,
typeCheckConfig
} from './util/index'
const isModalOverflowing =
this._element.scrollHeight > document.documentElement.clientHeight
- if (!this._isBodyOverflowing && isModalOverflowing) {
+ if ((!this._isBodyOverflowing && isModalOverflowing && !isRTL) || (this._isBodyOverflowing && !isModalOverflowing && isRTL)) {
this._element.style.paddingLeft = `${this._scrollbarWidth}px`
}
- if (this._isBodyOverflowing && !isModalOverflowing) {
+ if ((this._isBodyOverflowing && !isModalOverflowing && !isRTL) || (!this._isBodyOverflowing && isModalOverflowing && isRTL)) {
this._element.style.paddingRight = `${this._scrollbarWidth}px`
}
}
// Private
_addAttachmentClass(attachment) {
- this.getTipElement().classList.add(`${CLASS_PREFIX}-${attachment}`)
+ this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`)
}
_getContent() {
getTransitionDurationFromElement,
getUID,
isElement,
+ isRTL,
noop,
typeCheckConfig
} from './util/index'
const AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
- RIGHT: 'right',
+ RIGHT: isRTL ? 'left' : 'right',
BOTTOM: 'bottom',
- LEFT: 'left'
+ LEFT: isRTL ? 'right' : 'left'
}
const Default = {
return title
}
+ updateAttachment(attachment) {
+ if (attachment === 'right') {
+ return 'end'
+ }
+
+ if (attachment === 'left') {
+ return 'start'
+ }
+
+ return attachment
+ }
+
// Private
_getPopperConfig(attachment) {
}
_addAttachmentClass(attachment) {
- this.getTipElement().classList.add(`${CLASS_PREFIX}-${attachment}`)
+ this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`)
}
_getOffset() {
}
}
+const isRTL = document.documentElement.dir === 'rtl'
+
export {
TRANSITION_END,
getUID,
noop,
reflow,
getjQuery,
- onDOMContentLoaded
+ onDOMContentLoaded,
+ isRTL
}
fixtureEl.innerHTML = [
'<div class="dropdown">',
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu dropdown-menu-right">',
+ ' <div class="dropdown-menu dropdown-menu-end">',
' <a class="dropdown-item" href="#">Secondary link</a>',
' </div>',
'</div>'
fixtureEl.innerHTML = [
'<div class="dropup">',
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu dropdown-menu-right">',
+ ' <div class="dropdown-menu dropdown-menu-end">',
' <a class="dropdown-item" href="#">Secondary link</a>',
' </div>',
'</div>'
dropdown.toggle()
})
- it('should toggle a dropright', done => {
+ it('should toggle a dropend', done => {
fixtureEl.innerHTML = [
- '<div class="dropright">',
+ '<div class="dropend">',
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
' <div class="dropdown-menu">',
' <a class="dropdown-item" href="#">Secondary link</a>',
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const droprightEl = fixtureEl.querySelector('.dropright')
+ const dropendEl = fixtureEl.querySelector('.dropend')
const dropdown = new Dropdown(btnDropdown)
- droprightEl.addEventListener('shown.bs.dropdown', () => {
+ dropendEl.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
dropdown.toggle()
})
- it('should toggle a dropleft', done => {
+ it('should toggle a dropstart', done => {
fixtureEl.innerHTML = [
- '<div class="dropleft">',
+ '<div class="dropstart">',
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
' <div class="dropdown-menu">',
' <a class="dropdown-item" href="#">Secondary link</a>',
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropleftEl = fixtureEl.querySelector('.dropleft')
+ const dropstartEl = fixtureEl.querySelector('.dropstart')
const dropdown = new Dropdown(btnDropdown)
- dropleftEl.addEventListener('shown.bs.dropdown', () => {
+ dropstartEl.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
it('should close toast when close element with data-bs-dismiss attribute is set', done => {
fixtureEl.innerHTML = [
'<div class="toast" data-bs-delay="1" data-bs-autohide="false" data-bs-animation="false">',
- ' <button type="button" class="ml-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
+ ' <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
'</div>'
].join('')
fixtureEl.innerHTML = [
'<div class="toast" data-bs-autohide="false" data-bs-animation="false">',
- ' <button type="button" class="ml-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
+ ' <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
'</div>'
].join('')
})
})
+ describe('updateAttachment', () => {
+ it('should use end class name when right placement specified', done => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ placement: 'right'
+ })
+
+ tooltipEl.addEventListener('inserted.bs.tooltip', () => {
+ expect(tooltip.getTipElement().classList.contains('bs-tooltip-end')).toEqual(true)
+ done()
+ })
+
+ tooltip.show()
+ })
+
+ it('should use start class name when left placement specified', done => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ placement: 'left'
+ })
+
+ tooltipEl.addEventListener('inserted.bs.tooltip', () => {
+ expect(tooltip.getTipElement().classList.contains('bs-tooltip-start')).toEqual(true)
+ done()
+ })
+
+ tooltip.show()
+ })
+ })
+
describe('setElementContent', () => {
it('should do nothing if the element is null', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
<div class="btn-group">
<button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
- This dropdown's menu is right-aligned
+ This dropdown's menu is end-aligned
</button>
- <div class="dropdown-menu dropdown-menu-right">
+ <div class="dropdown-menu dropdown-menu-end">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here</button>
<div class="col-sm-12 mt-4">
<div class="btn-group dropup" role="group">
- <a href="#" class="btn btn-secondary">Dropup split align right</a>
+ <a href="#" class="btn btn-secondary">Dropup split align end</a>
<button type="button" id="dropdown-page-subheader-button-3" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Product actions</span>
</button>
- <div class="dropdown-menu dropdown-menu-right">
+ <div class="dropdown-menu dropdown-menu-end">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here with a long text</button>
</div>
</div>
<div class="btn-group dropup">
- <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropup align right</button>
- <div class="dropdown-menu dropdown-menu-right">
+ <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropup align end</button>
+ <div class="dropdown-menu dropdown-menu-end">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here with a long text</button>
</div>
<div class="col-sm-12 mt-4">
- <div class="btn-group dropright" role="group">
- <a href="#" class="btn btn-secondary">Dropright split</a>
+ <div class="btn-group dropend" role="group">
+ <a href="#" class="btn btn-secondary">Dropend split</a>
<button type="button" id="dropdown-page-subheader-button-4" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Product actions</span>
</button>
<button class="dropdown-item" type="button">Something else here with a long text</button>
</div>
</div>
- <div class="btn-group dropright">
+ <div class="btn-group dropend">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuRight" data-bs-toggle="dropdown" aria-expanded="false">
- Dropright
+ Dropend
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuRight">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Something else here</button>
</div>
</div>
- <!-- dropleft -->
- <div class="btn-group dropleft" role="group">
- <a href="#" class="btn btn-secondary">Dropleft split</a>
+ <!-- dropstart -->
+ <div class="btn-group dropstart" role="group">
+ <a href="#" class="btn btn-secondary">Dropstart split</a>
<button type="button" id="dropdown-page-subheader-button-5" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Product actions</span>
</button>
<button class="dropdown-item" type="button">Something else here with a long text</button>
</div>
</div>
- <div class="btn-group dropleft">
- <button class="btn btn-secondary dropdown-toggle" type="button" id="dropleftMenu" data-bs-toggle="dropdown" aria-expanded="false">
- Dropleft
+ <div class="btn-group dropstart">
+ <button class="btn btn-secondary dropdown-toggle" type="button" id="dropstartMenu" data-bs-toggle="dropdown" aria-expanded="false">
+ Dropstart
</button>
- <div class="dropdown-menu" aria-labelledby="dropleftMenu">
+ <div class="dropdown-menu" aria-labelledby="dropstartMenu">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here</button>
</button>
<button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="right" data-bs-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
- Popover on right
+ Popover on end
</button>
<button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="bottom" data-bs-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
</button>
<button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="left" data-bs-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
- Popover on left
+ Popover on start
</button>
</div>
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse" id="navbarSupportedContent">
- <ul class="navbar-nav mr-auto">
+ <ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="#fat">@fat</a>
</li>
<div class="notifications">
<div id="toastAutoHide" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="2000">
<div class="toast-header">
- <span class="d-block bg-primary rounded mr-2" style="width: 20px; height: 20px;"></span>
- <strong class="mr-auto">Bootstrap</strong>
+ <span class="d-block bg-primary rounded me-2" style="width: 20px; height: 20px;"></span>
+ <strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
</div>
<div class="toast-body">
<div class="toast" data-bs-autohide="false" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
- <span class="d-block bg-primary rounded mr-2" style="width: 20px; height: 20px;"></span>
- <strong class="mr-auto">Bootstrap</strong>
+ <span class="d-block bg-primary rounded me-2" style="width: 20px; height: 20px;"></span>
+ <strong class="me-auto">Bootstrap</strong>
<small class="text-muted">2 seconds ago</small>
- <button type="button" class="ml-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
+ <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Heads up, toasts will stack automatically
border: 1px solid;
width: 100px;
height: 50px;
- border: 1px solid;
margin-left: 50px;
transform: rotate(270deg);
margin-top: 100px;
Tooltip on top
</button>
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="right" title="Tooltip on right">
- Tooltip on right
+ Tooltip on end
</button>
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Tooltip on bottom">
Tooltip on bottom
</button>
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="left" title="Tooltip on left">
- Tooltip on left
+ Tooltip on start
</button>
</p>
</div>
float: left; // Suppress inline spacings and underlining of the separator
padding-right: $breadcrumb-item-padding-x;
color: $breadcrumb-divider-color;
- content: var(--#{$variable-prefix}breadcrumb-divider, escape-svg($breadcrumb-divider));
+ content: var(--#{$variable-prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$variable-prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"};
}
}
// Reset rounded corners
> .btn:not(:last-child):not(.dropdown-toggle),
> .btn-group:not(:last-child) > .btn {
- @include border-right-radius(0);
+ @include border-end-radius(0);
}
// The left radius should be 0 if the button is:
> .btn:nth-child(n + 3),
> :not(.btn-check) + .btn,
> .btn-group:not(:first-child) > .btn {
- @include border-left-radius(0);
+ @include border-start-radius(0);
}
}
&::after,
.dropup &::after,
- .dropright &::after {
+ .dropend &::after {
margin-left: 0;
}
- .dropleft &::before {
+ .dropstart &::before {
margin-right: 0;
}
}
}
+ .card-link {
- margin-left: $card-spacer-x;
+ margin-left: $card-spacer-x #{"/* rtl:ignore */"};
}
}
// Handle rounded corners
@if $enable-rounded {
&:not(:last-child) {
- @include border-right-radius(0);
+ @include border-end-radius(0);
.card-img-top,
.card-header {
}
&:not(:first-child) {
- @include border-left-radius(0);
+ @include border-start-radius(0);
.card-img-top,
.card-header {
// 1. .carousel.pointer-event should ideally be pan-y (to allow for users to scroll vertically)
// even when their scroll action started on a carousel, but for compatibility (with Firefox)
// we're preventing all actions instead
-// 2. The .carousel-item-left and .carousel-item-right is used to indicate where
+// 2. The .carousel-item-start and .carousel-item-end is used to indicate where
// the active slide is heading.
// 3. .active.carousel-item is the current slide.
-// 4. .active.carousel-item-left and .active.carousel-item-right is the current
+// 4. .active.carousel-item-start and .active.carousel-item-end is the current
// slide in its in-transition state. Only one of these occurs at a time.
-// 5. .carousel-item-next.carousel-item-left and .carousel-item-prev.carousel-item-right
+// 5. .carousel-item-next.carousel-item-start and .carousel-item-prev.carousel-item-end
// is the upcoming slide in transition.
.carousel {
display: block;
}
-.carousel-item-next:not(.carousel-item-left),
-.active.carousel-item-right {
+/* rtl:begin:ignore */
+.carousel-item-next:not(.carousel-item-start),
+.active.carousel-item-end {
transform: translateX(100%);
}
-.carousel-item-prev:not(.carousel-item-right),
-.active.carousel-item-left {
+.carousel-item-prev:not(.carousel-item-end),
+.active.carousel-item-start {
transform: translateX(-100%);
}
+/* rtl:end:ignore */
+
//
// Alternate transitions
}
.carousel-item.active,
- .carousel-item-next.carousel-item-left,
- .carousel-item-prev.carousel-item-right {
+ .carousel-item-next.carousel-item-start,
+ .carousel-item-prev.carousel-item-end {
z-index: 1;
opacity: 1;
}
- .active.carousel-item-left,
- .active.carousel-item-right {
+ .active.carousel-item-start,
+ .active.carousel-item-end {
z-index: 0;
opacity: 0;
@include transition(opacity 0s $carousel-transition-duration);
background-position: 50%;
background-size: 100% 100%;
}
+
+/* rtl:options: {
+ "autoRename": true,
+ "stringMap":[ {
+ "name" : "prev-next",
+ "search" : "prev",
+ "replace" : "next"
+ } ]
+} */
.carousel-control-prev-icon {
background-image: escape-svg($carousel-control-prev-icon-bg);
}
background-image: escape-svg($carousel-control-next-icon-bg);
}
-
// Optional indicator pips
//
// Add an ordered list with the following class and add a list item for each
// The dropdown wrapper (`<div>`)
.dropup,
-.dropright,
+.dropend,
.dropdown,
-.dropleft {
+.dropstart {
position: relative;
}
@include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
- .dropdown-menu#{$infix}-left {
- right: auto;
- left: 0;
+ .dropdown-menu#{$infix}-start {
+ right: auto #{"/* rtl:ignore */"};
+ left: 0 #{"/* rtl:ignore */"};
}
- .dropdown-menu#{$infix}-right {
- right: 0;
- left: auto;
+ .dropdown-menu#{$infix}-end {
+ right: 0 #{"/* rtl:ignore */"};
+ left: auto #{"/* rtl:ignore */"};
}
}
}
}
}
-.dropright {
+.dropend {
.dropdown-menu {
top: 0;
right: auto;
}
.dropdown-toggle {
- @include caret(right);
+ @include caret(end);
&::after {
vertical-align: 0;
}
}
}
-.dropleft {
+.dropstart {
.dropdown-menu {
top: 0;
right: 100%;
}
.dropdown-toggle {
- @include caret(left);
+ @include caret(start);
&::before {
vertical-align: 0;
}
&[x-placement^="left"] {
right: auto;
bottom: auto;
+ left: auto;
}
}
+
// Dividers (basically an `<hr>`) within the dropdown
.dropdown-divider {
height: 0;
> .list-group-item {
&:first-child {
- @include border-bottom-left-radius($list-group-border-radius);
- @include border-top-right-radius(0);
+ @include border-bottom-start-radius($list-group-border-radius);
+ @include border-top-end-radius(0);
}
&:last-child {
- @include border-top-right-radius($list-group-border-radius);
- @include border-bottom-left-radius(0);
+ @include border-top-end-radius($list-group-border-radius);
+ @include border-bottom-start-radius(0);
}
&.active {
.navbar-brand {
padding-top: $navbar-brand-padding-y;
padding-bottom: $navbar-brand-padding-y;
- margin-right: $navbar-brand-margin-right;
+ margin-right: $navbar-brand-margin-end;
@include font-size($navbar-brand-font-size);
text-decoration: if($link-decoration == none, null, none);
white-space: nowrap;
.page-item {
&:not(:first-child) .page-link {
- margin-left: $pagination-margin-left;
+ margin-left: $pagination-margin-start;
}
&.active .page-link {
.popover {
position: absolute;
top: 0;
- left: 0;
+ left: 0 #{"/* rtl:ignore */"};
z-index: $zindex-popover;
display: block;
max-width: $popover-max-width;
}
}
-.bs-popover-right {
- margin-left: $popover-arrow-height;
+.bs-popover-end {
+ margin-left: $popover-arrow-height #{"/* rtl:ignore */"};
> .popover-arrow {
- left: subtract(-$popover-arrow-height, $popover-border-width);
+ left: subtract(-$popover-arrow-height, $popover-border-width) #{"/* rtl:ignore */"};
width: $popover-arrow-height;
height: $popover-arrow-width;
margin: $popover-border-radius 0; // make sure the arrow does not touch the popover's rounded corners
&::before {
- left: 0;
- border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0;
- border-right-color: $popover-arrow-outer-color;
+ left: 0 #{"/* rtl:ignore */"};
+ border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0 #{"/* rtl:ignore */"};
+ border-right-color: $popover-arrow-outer-color #{"/* rtl:ignore */"};
}
&::after {
- left: $popover-border-width;
- border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0;
- border-right-color: $popover-arrow-color;
+ left: $popover-border-width #{"/* rtl:ignore */"};
+ border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0 #{"/* rtl:ignore */"};
+ border-right-color: $popover-arrow-color #{"/* rtl:ignore */"};
}
}
}
}
}
-.bs-popover-left {
- margin-right: $popover-arrow-height;
+.bs-popover-start {
+ margin-right: $popover-arrow-height #{"/* rtl:ignore */"};
> .popover-arrow {
- right: subtract(-$popover-arrow-height, $popover-border-width);
+ right: subtract(-$popover-arrow-height, $popover-border-width) #{"/* rtl:ignore */"};
width: $popover-arrow-height;
height: $popover-arrow-width;
margin: $popover-border-radius 0; // make sure the arrow does not touch the popover's rounded corners
&::before {
- right: 0;
- border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height;
- border-left-color: $popover-arrow-outer-color;
+ right: 0 #{"/* rtl:ignore */"};
+ border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height #{"/* rtl:ignore */"};
+ border-left-color: $popover-arrow-outer-color #{"/* rtl:ignore */"};
}
&::after {
- right: $popover-border-width;
- border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height;
- border-left-color: $popover-arrow-color;
+ right: $popover-border-width #{"/* rtl:ignore */"};
+ border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height #{"/* rtl:ignore */"};
+ border-left-color: $popover-arrow-color #{"/* rtl:ignore */"};
}
}
}
@extend .bs-popover-top;
}
&[x-placement^="right"] {
- @extend .bs-popover-right;
+ @extend .bs-popover-end;
}
&[x-placement^="bottom"] {
@extend .bs-popover-bottom;
}
&[x-placement^="left"] {
- @extend .bs-popover-left;
+ @extend .bs-popover-start;
}
}
-
// Offset the popover to account for the popover arrow
.popover-header {
padding: $popover-header-padding-y $popover-header-padding-x;
samp {
font-family: $font-family-code;
@include font-size(1em); // Correct the odd `em` font sizing in all browsers.
+ direction: ltr #{"/* rtl:ignore */"};
+ unicode-bidi: bidi-override;
}
// 1. Remove browser default top margin
-webkit-appearance: textfield; // 2
}
+// 1. A few input types should stay LTR
+// See https://rtlstyling.com/posts/rtl-styling#form-inputs
+// 2. RTL only output
+// See https://rtlcss.com/learn/usage-guide/control-directives/#raw
+
+/* rtl:raw:
+[type="tel"],
+[type="url"],
+[type="email"],
+[type="number"] {
+ direction: ltr;
+}
+*/
+
// Remove the inner padding in Chrome and Safari on macOS.
::-webkit-search-decoration {
//
@keyframes spinner-border {
- to { transform: rotate(360deg); }
+ to { transform: rotate(360deg) #{"/* rtl:ignore */"}; }
}
.spinner-border {
}
}
-.bs-tooltip-right {
+.bs-tooltip-end {
padding: 0 $tooltip-arrow-height;
.tooltip-arrow {
- left: 0;
+ left: 0 #{"/* rtl:ignore */"};
width: $tooltip-arrow-height;
height: $tooltip-arrow-width;
&::before {
- right: 0;
- border-width: ($tooltip-arrow-width / 2) $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
- border-right-color: $tooltip-arrow-color;
+ right: 0 #{"/* rtl:ignore */"};
+ border-width: ($tooltip-arrow-width / 2) $tooltip-arrow-height ($tooltip-arrow-width / 2) 0 #{"/* rtl:ignore */"};
+ border-right-color: $tooltip-arrow-color #{"/* rtl:ignore */"};
}
}
}
}
}
-.bs-tooltip-left {
+.bs-tooltip-start {
padding: 0 $tooltip-arrow-height;
.tooltip-arrow {
- right: 0;
+ right: 0 #{"/* rtl:ignore */"};
width: $tooltip-arrow-height;
height: $tooltip-arrow-width;
&::before {
- left: 0;
- border-width: ($tooltip-arrow-width / 2) 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
- border-left-color: $tooltip-arrow-color;
+ left: 0 #{"/* rtl:ignore */"};
+ border-width: ($tooltip-arrow-width / 2) 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height #{"/* rtl:ignore */"};
+ border-left-color: $tooltip-arrow-color #{"/* rtl:ignore */"};
}
}
}
@extend .bs-tooltip-top;
}
&[x-placement^="right"] {
- @extend .bs-tooltip-right;
+ @extend .bs-tooltip-end;
}
&[x-placement^="bottom"] {
@extend .bs-tooltip-bottom;
}
&[x-placement^="left"] {
- @extend .bs-tooltip-left;
+ @extend .bs-tooltip-start;
}
}
"float": (
responsive: true,
property: float,
- values: left right none
+ values: (
+ start: left,
+ end: right,
+ none: none,
+ )
),
"overflow": (
property: overflow,
property: bottom,
values: $position-values
),
- "left": (
+ "start": (
property: left,
+ class: start,
values: $position-values
),
- "right": (
+ "end": (
property: right,
+ class: end,
values: $position-values
),
"translate-middle": (
0: 0,
)
),
- "border-right": (
+ "border-end": (
property: border-right,
+ class: border-end,
values: (
null: $border-width solid $border-color,
0: 0,
0: 0,
)
),
- "border-left": (
+ "border-start": (
property: border-left,
+ class: border-start,
values: (
null: $border-width solid $border-color,
0: 0,
class: mt,
values: map-merge($spacers, (auto: auto))
),
- "margin-right": (
+ "margin-end": (
responsive: true,
property: margin-right,
- class: mr,
+ class: me,
values: map-merge($spacers, (auto: auto))
),
"margin-bottom": (
class: mb,
values: map-merge($spacers, (auto: auto))
),
- "margin-left": (
+ "margin-start": (
responsive: true,
property: margin-left,
- class: ml,
+ class: ms,
values: map-merge($spacers, (auto: auto))
),
// Negative margin utilities
class: mt,
values: $negative-spacers
),
- "negative-margin-right": (
+ "negative-margin-end": (
responsive: true,
property: margin-right,
- class: mr,
+ class: me,
values: $negative-spacers
),
"negative-margin-bottom": (
class: mb,
values: $negative-spacers
),
- "negative-margin-left": (
+ "negative-margin-start": (
responsive: true,
property: margin-left,
- class: ml,
+ class: ms,
values: $negative-spacers
),
// Padding utilities
class: pt,
values: $spacers
),
- "padding-right": (
+ "padding-end": (
responsive: true,
property: padding-right,
- class: pr,
+ class: pe,
values: $spacers
),
"padding-bottom": (
class: pb,
values: $spacers
),
- "padding-left": (
+ "padding-start": (
responsive: true,
property: padding-left,
- class: pl,
+ class: ps,
values: $spacers
),
// Text
responsive: true,
property: text-align,
class: text,
- values: left right center
+ values: (
+ start: left,
+ end: right,
+ center: center,
+ )
),
"color": (
property: color,
"word-wrap": (
property: word-wrap word-break,
class: text,
- values: (break: break-word)
+ values: (break: break-word),
+ rtl: false
),
"font-family": (
property: font-family,
class: rounded-top,
values: (null: $border-radius)
),
- "rounded-right": (
+ "rounded-end": (
property: border-top-right-radius border-bottom-right-radius,
- class: rounded-right,
+ class: rounded-end,
values: (null: $border-radius)
),
"rounded-bottom": (
class: rounded-bottom,
values: (null: $border-radius)
),
- "rounded-left": (
+ "rounded-start": (
property: border-bottom-left-radius border-top-left-radius,
- class: rounded-left,
+ class: rounded-start,
values: (null: $border-radius)
),
"visibility": (
$form-check-input-width: 1em !default;
$form-check-min-height: $font-size-base * $line-height-base !default;
-$form-check-padding-left: $form-check-input-width + .5em !default;
+$form-check-padding-start: $form-check-input-width + .5em !default;
$form-check-margin-bottom: .125rem !default;
$form-check-label-color: null !default;
$form-check-label-cursor: null !default;
$form-switch-color: rgba(0, 0, 0, .25) !default;
$form-switch-width: 2em !default;
-$form-switch-padding-left: $form-switch-width + .5em !default;
+$form-switch-padding-start: $form-switch-width + .5em !default;
$form-switch-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color}'/></svg>") !default;
$form-switch-border-radius: $form-switch-width !default;
$form-switch-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-checked-color}'/></svg>") !default;
$form-switch-checked-bg-position: right center !default;
-$form-check-inline-margin-right: 1rem !default;
+$form-check-inline-margin-end: 1rem !default;
$input-group-addon-padding-y: $input-padding-y !default;
$input-group-addon-padding-x: $input-padding-x !default;
$form-select-indicator-color: $gray-800 !default;
$form-select-indicator: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$form-select-indicator-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/></svg>") !default;
-$form-select-feedback-icon-padding-right: add(1em * .75, (2 * $form-select-padding-y * .75) + $form-select-padding-x + $form-select-indicator-padding) !default;
-$form-select-feedback-icon-position: center right ($form-select-padding-x + $form-select-indicator-padding) !default;
-$form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;
+$form-select-feedback-icon-padding-end: add(1em * .75, (2 * $form-select-padding-y * .75) + $form-select-padding-x + $form-select-indicator-padding) !default;
+$form-select-feedback-icon-position: center right ($form-select-padding-x + $form-select-indicator-padding) !default;
+$form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;
$form-select-border-width: $input-border-width !default;
$form-select-border-color: $input-border-color !default;
$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;
$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;
$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;
-$navbar-brand-margin-right: 1rem !default;
+$navbar-brand-margin-end: 1rem !default;
$navbar-toggler-padding-y: .25rem !default;
$navbar-toggler-padding-x: .75rem !default;
$pagination-bg: $white !default;
$pagination-border-width: $border-width !default;
$pagination-border-radius: $border-radius !default;
-$pagination-margin-left: -$pagination-border-width !default;
+$pagination-margin-start: -$pagination-border-width !default;
$pagination-border-color: $gray-300 !default;
$pagination-focus-color: $link-hover-color !default;
$breadcrumb-divider-color: $gray-600 !default;
$breadcrumb-active-color: $gray-600 !default;
$breadcrumb-divider: quote("/") !default;
+$breadcrumb-divider-flipped: $breadcrumb-divider !default;
$breadcrumb-border-radius: null !default;
// Carousel
"margin-x",
"margin-y",
"margin-top",
- "margin-right",
+ "margin-end",
"margin-bottom",
- "margin-left",
+ "margin-start",
"negative-margin",
"negative-margin-x",
"negative-margin-y",
"negative-margin-top",
- "negative-margin-right",
+ "negative-margin-end",
"negative-margin-bottom",
- "negative-margin-left",
+ "negative-margin-start",
"padding",
"padding-x",
"padding-y",
"padding-top",
- "padding-right",
+ "padding-end",
"padding-bottom",
- "padding-left",
+ "padding-start",
)
);
.form-check {
display: block;
min-height: $form-check-min-height;
- padding-left: $form-check-padding-left;
+ padding-left: $form-check-padding-start;
margin-bottom: $form-check-margin-bottom;
.form-check-input {
float: left;
- margin-left: $form-check-padding-left * -1;
+ margin-left: $form-check-padding-start * -1;
}
}
//
.form-switch {
- padding-left: $form-switch-padding-left;
+ padding-left: $form-switch-padding-start;
.form-check-input {
width: $form-switch-width;
- margin-left: $form-switch-padding-left * -1;
+ margin-left: $form-switch-padding-start * -1;
background-image: escape-svg($form-switch-bg-image);
background-position: left center;
@include border-radius($form-switch-border-radius);
.form-check-inline {
display: inline-block;
- margin-right: $form-check-inline-margin-right;
+ margin-right: $form-check-inline-margin-end;
}
.btn-check {
&:not(.has-validation) {
> :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu),
> .dropdown-toggle:nth-last-child(n + 3) {
- @include border-right-radius(0);
+ @include border-end-radius(0);
}
}
&.has-validation {
> :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu),
> .dropdown-toggle:nth-last-child(n + 4) {
- @include border-right-radius(0);
+ @include border-end-radius(0);
}
}
> :not(:first-child):not(.dropdown-menu)#{$validation-messages} {
margin-left: -$input-border-width;
- @include border-left-radius(0);
+ @include border-start-radius(0);
}
}
}
}
-@mixin border-right-radius($radius: $border-radius) {
+@mixin border-end-radius($radius: $border-radius) {
@if $enable-rounded {
border-top-right-radius: valid-radius($radius);
border-bottom-right-radius: valid-radius($radius);
}
}
-@mixin border-left-radius($radius: $border-radius) {
+@mixin border-start-radius($radius: $border-radius) {
@if $enable-rounded {
border-top-left-radius: valid-radius($radius);
border-bottom-left-radius: valid-radius($radius);
}
}
-@mixin border-top-left-radius($radius: $border-radius) {
+@mixin border-top-start-radius($radius: $border-radius) {
@if $enable-rounded {
border-top-left-radius: valid-radius($radius);
}
}
-@mixin border-top-right-radius($radius: $border-radius) {
+@mixin border-top-end-radius($radius: $border-radius) {
@if $enable-rounded {
border-top-right-radius: valid-radius($radius);
}
}
-@mixin border-bottom-right-radius($radius: $border-radius) {
+@mixin border-bottom-end-radius($radius: $border-radius) {
@if $enable-rounded {
border-bottom-right-radius: valid-radius($radius);
}
}
-@mixin border-bottom-left-radius($radius: $border-radius) {
+@mixin border-bottom-start-radius($radius: $border-radius) {
@if $enable-rounded {
border-bottom-left-radius: valid-radius($radius);
}
border-left: $caret-width solid transparent;
}
-@mixin caret-right {
+@mixin caret-end {
border-top: $caret-width solid transparent;
border-right: 0;
border-bottom: $caret-width solid transparent;
border-left: $caret-width solid;
}
-@mixin caret-left {
+@mixin caret-start {
border-top: $caret-width solid transparent;
border-right: $caret-width solid;
border-bottom: $caret-width solid transparent;
@include caret-down();
} @else if $direction == up {
@include caret-up();
- } @else if $direction == right {
- @include caret-right();
+ } @else if $direction == end {
+ @include caret-end();
}
}
- @if $direction == left {
+ @if $direction == start {
&::after {
display: none;
}
margin-right: $caret-spacing;
vertical-align: $caret-vertical-align;
content: "";
- @include caret-left();
+ @include caret-start();
}
}
border-color: $color;
@if $enable-validation-icons {
- padding-right: $form-select-feedback-icon-padding-right;
+ padding-right: $form-select-feedback-icon-padding-end;
background-image: escape-svg($form-select-indicator), escape-svg($icon);
background-position: $form-select-bg-position, $form-select-feedback-icon-position;
background-size: $form-select-bg-size, $form-select-feedback-icon-size;
}
.page-item {
- @if $pagination-margin-left == (-$pagination-border-width) {
+ @if $pagination-margin-start == (-$pagination-border-width) {
&:first-child {
.page-link {
- @include border-left-radius($border-radius);
+ @include border-start-radius($border-radius);
}
}
&:last-child {
.page-link {
- @include border-right-radius($border-radius);
+ @include border-end-radius($border-radius);
}
}
} @else {
}
}
+ $is-rtl: map-get($utility, rtl);
+
@if $value != null {
+ @if $is-rtl == false {
+ /* rtl:begin:remove */
+ }
.#{$property-class + $infix + $property-class-modifier} {
@each $property in $properties {
#{$property}: $value if($enable-important-utilities, !important, null);
}
}
}
+ @if $is-rtl == false {
+ /* rtl:end:remove */
+ }
}
}
}