twbs / bootstrap

The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.
https://getbootstrap.com
MIT License
170.92k stars 78.89k forks source link

Switch to logical properties to handle LTR/RTL at once #38024

Open BSarmady opened 1 year ago

BSarmady commented 1 year ago

Prerequisites

Proposal

Referring to this comment and this SO answer, and CanIUse inset-inline-end and others, it seems these following properties are supported in all major browsers (or green?) since mid 2020

inset-inline-start (in place of left property) inset-inline-end (in place of right property) margin-inline-start (in place of margin-left property) margin-inline-end (in place of margin-right property) margin-inline (in place of margin shorthand property) padding-inline-start (in place of padding-left property) padding-inline-end (in place of padding-right property) padding-inline (in place of padding shorthand property) text-align: start; (in place of left value) text-align: end; (in place of right value) float: inline-start; (in place of left value) float: inline-end; (in place of right value)

following styles are still using left and right me-0 ... 'me-auto', ms-0 ... ms-auto, .start ... .start-100 .end ... end-100

however using above mentioned attribute and properties will allow easier transition between LTR and RTL

so instead of LTR .start-0 { left: 0 !important; } RTL .start-0 { right: 0 !important; }

We can have just .start-0 { inset-inline-start: 0 !important; }

and so on.

Currently I'm testing these transitions with following html, which allows me immediate transition between RTL and LTR

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    <link rel="stylesheet" href="bootstrap-5.3.0/bootstrap.css">
    <link rel="stylesheet" href="this/main.css">
    <script src="jquery.1.11.3/jquery.min.js"></script>
    <script src="bootstrap-5.3.0/bootstrap.bundle.js"></script>
    <script>
        $(function () {
            const $html = $('html');
            if (localStorage.getItem('dir') === 'rtl') {
                $html.attr('dir', 'rtl');
            } else {
                $html.removeAttr('dir')
            }
            $('#btnDir').click(function () {
                if ($html.attr('dir') === 'rtl') {
                    $html.removeAttr('dir');
                    localStorage.removeItem('dir')
                } else {
                    $html.attr('dir', 'rtl');
                    localStorage.setItem('dir', 'rtl')
                }
            });
        })
    </script>
</head>
<body>
    <input type="button" id="btnDir" class="btn btn-primary" value="            direction            ">
    <!-- Elements being tested below-->
    <div class="toast-container p-3 bottom-0 end-0" aria-live="polite" aria-atomic="true">
        <div class="toast show text-bg-danger" role="alert" aria-live="assertive" aria-atomic="true">
            <div class="toast-header">
                <strong class="me-auto">Toast-Header</strong>
                <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
            </div>
            <div class="toast-body">Toast Body</div>
        </div>
        <div class="toast text-bg-danger border-0 show" role="alert" aria-live="assertive" aria-atomic="true">
            <div class="d-flex">
                <div class="toast-body">
                    Hello, world! This is a toast message.
                </div>
                <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
            </div>
        </div>
        <div class="toast text-bg-primary border-0 show" role="alert" aria-live="assertive" aria-atomic="true">
            <div class="d-flex">
                <div class="toast-body">Toast Body</div>
                <button type="button" class="btn-close btn-close-white m-auto ms-auto me-2" data-bs-dismiss="toast" aria-label="Close"></button>
            </div>
        </div>
    </div>
</body>
</html>

Content of main.css is something like following (obviously for these to work I need to comment their counter part in bootstrap.css)

.text-start{ text-align:start !important; }
.text-end{ text-align:end !important; }

.me-0{ margin-inline-end:0 !important }
.me-1{ margin-inline-end:0.25rem !important }
.me-2{ margin-inline-end:0.5rem !important }
.me-3{ margin-inline-end:1rem !important }
.me-4{ margin-inline-end:1.5rem !important }
.me-5{ margin-inline-end:3rem !important }
.me-auto{ margin-inline-end:auto !important }

.start-0{ inset-inline-start:0 !important; }
.start-50{ inset-inline-start:50% !important;}
.start-100{ inset-inline-start:100% !important;}

.end-0{ inset-inline-end:0 !important; }
.end-50{ inset-inline-end:50% !important;}
.end-100{ inset-inline-end:100% !important;}

.toast-header .btn-close {
    margin-inline-end: calc(-0.5 * var(--bs-toast-padding-x));
    margin-inline-start: var(--bs-toast-padding-x);
}

Motivation and context

I think it allows transition from having two separate CSS for LTR and RTL to one single file. and in case in future you want to support top to bottom languages ;)

ffoodd commented 1 year ago

This has been considered 3 years ago while implementing RTL for v5 (see #30918). Support was still short at that time, but switching to logical properties should be one of the main move coming in V6.

I'm tagging this for v6 as I noticed there's currently no dedicated issue!

ayyash commented 9 months ago

Or what would have made more sense is keep the selector and change the property, so while in LTR, design for LTR, and it will magically flip on RTL

text-left { text-align: start}
text-right {text-align: end}

I really expected (hoped) for this, don't know why you went for the extreme of renaming selectors, but keeping the properties

text-start {text-align: left}

why?