this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Preliminary step for RTL

+654 -383
+163 -158
src/app.css
··· 162 162 white-space: nowrap; 163 163 } 164 164 .deck > header .header-grid > .header-side:last-of-type { 165 - text-align: right; 165 + text-align: end; 166 166 grid-column: 3; 167 167 } 168 168 .deck > header .header-grid :is(button, .button).plain { ··· 181 181 grid-template-columns: 1fr max-content; 182 182 } 183 183 .deck > header .header-grid-2 h1 { 184 - text-align: left; 185 - padding-left: 8px; 184 + text-align: start; 185 + padding-inline-start: 8px; 186 186 } 187 187 .deck > header .header-grid h1:has(.ancestors-indicator) { 188 188 display: flex; ··· 217 217 opacity: 0.25; 218 218 } 219 219 } 220 + @keyframes indeterminate-bar-rtl { 221 + 0% { 222 + transform: translateX(50%); 223 + opacity: 0.25; 224 + } 225 + 50% { 226 + opacity: 1; 227 + } 228 + 100% { 229 + transform: translateX(-50%); 230 + opacity: 0.25; 231 + } 232 + } 220 233 .deck > header.loading:after { 221 234 pointer-events: none; 222 235 content: ''; ··· 232 245 transparent 233 246 ); 234 247 animation: indeterminate-bar 1s ease-in-out infinite alternate; 248 + &:dir(rtl) { 249 + animation-name: indeterminate-bar-rtl; 250 + } 235 251 } 236 252 @media (min-width: 40em) { 237 253 .deck > header.loading:after { ··· 268 284 width: 95vw; 269 285 max-width: calc(320px * 3.3); 270 286 transform: translateX(calc(-50% + var(--main-width) / 2)); 287 + &:dir(rtl) { 288 + transform: translateX(calc(50% - var(--main-width) / 2)); 289 + } 271 290 } 272 291 } 273 292 ··· 346 365 margin: 0; 347 366 padding: 0; 348 367 border-bottom: var(--hairline-width) solid var(--divider-color); 368 + --line-dir: var(--to-forward); 349 369 } 350 370 .timeline.flat > li { 351 371 border-bottom: none; ··· 362 382 --avatar-size: 50px; 363 383 --avatar-margin-start: 16px; 364 384 --avatar-margin-end: 12px; 385 + --line-curve: 45deg; 386 + :dir(rtl) & { 387 + --line-curve: -45deg; 388 + } 365 389 } 366 390 .timeline.contextual > li { 367 391 background-image: linear-gradient( 368 - to right, 392 + var(--line-dir), 369 393 transparent, 370 394 transparent var(--line-start), 371 395 var(--comment-line-color) var(--line-start), ··· 394 418 .timeline.contextual 395 419 > li.descendant:not(.thread) 396 420 > :is(.status-link, .status-focus) { 397 - padding-left: 40px; 421 + padding-inline-start: 40px; 398 422 } 399 423 .timeline.contextual .replies[data-scroll-left]:not([data-scroll-left='0']) { 400 424 background-color: var(--bg-color); ··· 408 432 } 409 433 .timeline.contextual .replies[data-comments-level='4']:has(.replies) { 410 434 overflow-x: auto; 411 - mask-image: linear-gradient(to left, transparent, black 32px); 435 + mask-image: linear-gradient(var(--to-backward), transparent, black 32px); 412 436 } 413 437 .timeline.contextual 414 438 .replies[data-comments-level='4']:has(.replies) ··· 426 450 > :is(.status-link, .status-focus) 427 451 + .replies 428 452 .replies-summary { 429 - margin-left: calc( 453 + margin-inline-start: calc( 430 454 var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + 431 455 (var(--line-margin-end) * (var(--comments-level) - 1)) 432 456 ); 433 457 } 434 - /* .timeline.contextual 435 - > li.descendant.thread 436 - > .status-link 437 - + .replies 438 - .replies 439 - > .replies-summary { 440 - margin-left: calc( 441 - var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + 442 - var(--line-margin-end) 443 - ); 444 - } 445 - .timeline.contextual 446 - > li.descendant.thread 447 - > .status-link 448 - + .replies 449 - .replies 450 - .replies 451 - > .replies-summary { 452 - margin-left: calc( 453 - var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + 454 - (var(--line-margin-end) * 2) 455 - ); 456 - } */ 457 458 .timeline.contextual 458 459 > li.descendant.thread 459 460 > :is(.status-link, .status-focus) 460 461 + .replies 461 462 :is(.status-link, .status-focus) { 462 - padding-left: calc( 463 + padding-inline-start: calc( 463 464 var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + 464 465 (var(--line-margin-end) * (var(--comments-level) - 1)) 465 466 ); 466 467 } 467 - /* .timeline.contextual 468 - > li.descendant.thread 469 - > .status-link 470 - + .replies 471 - .replies 472 - .status-link { 473 - padding-left: calc( 474 - var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + 475 - var(--line-margin-end) 476 - ); 477 - } 478 - .timeline.contextual 479 - > li.descendant.thread 480 - > .status-link 481 - + .replies 482 - .replies 483 - .replies 484 - .status-link { 485 - padding-left: calc( 486 - var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) + 487 - (var(--line-margin-end) * 2) 488 - ); 489 - } */ 490 468 .timeline.contextual 491 469 > li.descendant:not(.thread) 492 470 > :is(.status-link, .status-focus) 493 471 + .replies 494 472 .replies-summary { 495 - margin-left: calc( 473 + margin-inline-start: calc( 496 474 var(--thread-start) + var(--line-margin-end) * var(--comments-level) 497 475 ); 498 476 } 499 - /* .timeline.contextual 500 - > li.descendant:not(.thread) 501 - > .status-link 502 - + .replies 503 - .replies 504 - > .replies-summary { 505 - margin-left: calc( 506 - var(--thread-start) + var(--line-margin-end) + var(--line-margin-end) 507 - ); 508 - } 509 - .timeline.contextual 510 - > li.descendant:not(.thread) 511 - > .status-link 512 - + .replies 513 - .replies 514 - .replies 515 - > .replies-summary { 516 - margin-left: calc( 517 - var(--thread-start) + var(--line-margin-end) + (var(--line-margin-end) * 2) 518 - ); 519 - } */ 520 477 .timeline.contextual 521 478 > li.descendant:not(.thread) 522 479 > :is(.status-link, .status-focus) 523 480 + .replies 524 481 :is(.status-link, .status-focus) { 525 - padding-left: calc( 482 + padding-inline-start: calc( 526 483 var(--thread-start) + var(--line-margin-end) * var(--comments-level) 527 484 ); 528 485 } 529 - /* .timeline.contextual 530 - > li.descendant:not(.thread) 531 - > .status-link 532 - + .replies 533 - .replies 534 - .status-link { 535 - padding-left: calc(var(--thread-start) + (var(--line-margin-end) * 2)); 536 - } 537 - .timeline.contextual 538 - > li.descendant:not(.thread) 539 - > .status-link 540 - + .replies 541 - .replies 542 - .replies 543 - .status-link { 544 - padding-left: calc(var(--thread-start) + (var(--line-margin-end) * 3)); 545 - } */ 546 486 .timeline.contextual > li.descendant:not(.thread):before { 547 487 content: ''; 548 488 position: absolute; 549 489 top: 10px; 550 - left: var(--line-start); 490 + inset-inline-start: var(--line-start); 551 491 width: var(--line-diameter); 552 492 height: var(--line-diameter); 553 493 border-radius: var(--line-radius); 554 494 border-style: solid; 555 495 border-width: var(--line-width); 556 496 border-color: transparent transparent var(--comment-line-color) transparent; 557 - transform: rotate(45deg); 497 + transform: rotate(var(--line-curve)); 558 498 } 559 499 .timeline.contextual > li .replies-link { 560 500 color: var(--text-insignificant-color); 561 - margin-left: 16px; 501 + margin-inline-start: 16px; 562 502 margin-top: -12px; 563 503 padding-bottom: 12px; 564 504 font-size: 90%; 565 505 } 566 506 .timeline.contextual > li.ancestor .replies-link { 567 - margin-left: calc( 507 + margin-inline-start: calc( 568 508 var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) 569 509 ); 570 510 } ··· 572 512 > li.thread 573 513 > :is(.status-link, .status-focus) 574 514 .replies-link { 575 - margin-left: calc( 515 + margin-inline-start: calc( 576 516 var(--avatar-size) + var(--avatar-margin-start) + var(--avatar-margin-end) 577 517 ); 578 518 } ··· 603 543 list-style: none; 604 544 gap: 8px; 605 545 align-items: center; 606 - margin-right: calc(44px + 8px); 546 + margin-inline-end: calc(44px + 8px); 607 547 608 548 b { 609 549 font-weight: 500; ··· 618 558 transition: transform 0.3s ease; 619 559 620 560 &:not(:first-child) { 621 - margin: 0 0 0 -4px; 561 + transform: rotate(0deg); 562 + margin: 0; 563 + margin-inline-start: -4px; 622 564 } 623 565 } 624 566 } ··· 637 579 638 580 .replies-parent-link { 639 581 position: absolute; 640 - right: 4px; 582 + inset-inline-end: 4px; 641 583 height: 100%; 642 584 z-index: 2; 643 585 font-size: 16px; ··· 648 590 align-items: center; 649 591 padding: var(--summary-padding) calc(var(--summary-padding) * 2); 650 592 transform: translateX(100%); 651 - margin: calc(-1 * var(--summary-padding)) calc(-1 * var(--summary-padding)) 652 - calc(-1 * var(--summary-padding)) 0; 593 + &:dir(rtl) { 594 + transform: translateX(-100%); 595 + } 596 + margin: calc(-1 * var(--summary-padding)) 0; 597 + margin-inline-end: calc(-1 * var(--summary-padding)); 653 598 border-radius: 8px; 654 599 background-color: var(--link-bg-color); 655 600 ··· 681 626 color: var(--text-color); 682 627 background-color: var(--comment-line-color); 683 628 background-image: linear-gradient( 684 - to top right, 629 + to top var(--forward), 685 630 var(--comment-line-color), 686 631 var(--bg-faded-color) 687 632 ); ··· 697 642 } 698 643 } 699 644 .timeline.contextual > li .replies[open] > .replies-summary { 700 - border-bottom-left-radius: 0; 645 + border-end-start-radius: 0; 701 646 702 647 .avatars { 703 648 opacity: 0.5; ··· 727 672 ); 728 673 --line-end: calc(var(--line-start) + var(--line-width)); 729 674 background-image: linear-gradient( 730 - to right, 675 + var(--line-dir), 731 676 transparent, 732 677 transparent var(--line-start), 733 678 var(--comment-line-color) var(--line-start), ··· 768 713 content: ''; 769 714 position: absolute; 770 715 top: 10px; 771 - left: var(--line-start); 716 + inset-inline-start: var(--line-start); 772 717 width: var(--line-diameter); 773 718 height: var(--line-diameter); 774 719 border-radius: var(--line-radius); 775 720 border-style: solid; 776 721 border-width: var(--line-width); 777 722 border-color: transparent transparent var(--comment-line-color) transparent; 778 - transform: rotate(45deg); 723 + transform: rotate(var(--line-curve)); 779 724 } 780 725 /* .timeline.contextual > li .replies .replies li:before { 781 726 --line-start: calc( ··· 814 759 > ul > li:only-child { 815 760 > .replies { 816 761 > ul > li:only-child { 817 - margin-left: calc(-1 * var(--line-margin-end)); 818 - background-position: calc(16px) 0; 762 + margin-inline-start: calc(-1 * var(--line-margin-end)); 763 + background-position: 16px 0; 764 + &:dir(rtl) { 765 + background-position: -16px 0; 766 + } 819 767 background-size: 100% calc(20px + 8px); 820 768 821 769 &:before { ··· 856 804 --line-width: 3px; 857 805 --line-end: calc(var(--line-start) + var(--line-width)); 858 806 background-image: linear-gradient( 859 - to right, 807 + var(--line-dir), 860 808 transparent, 861 809 transparent var(--line-start), 862 810 var(--comment-line-color) var(--line-start), ··· 868 816 } 869 817 .timeline:not(.flat) > li.timeline-item-container-start { 870 818 margin-bottom: 0; 871 - border-bottom-left-radius: 0; 872 - border-bottom-right-radius: 0; 819 + border-end-start-radius: 0; 820 + border-end-end-radius: 0; 873 821 border-bottom: 0; 874 822 background-position: 0 calc(16px + var(--avatar-size)); 875 823 } ··· 882 830 } 883 831 .timeline:not(.flat) > li.timeline-item-container-end { 884 832 margin-top: 0; 885 - border-top-left-radius: 0; 886 - border-top-right-radius: 0; 833 + border-start-start-radius: 0; 834 + border-start-end-radius: 0; 887 835 border-top: 0; 888 836 background-size: 100% 16px; 889 837 ··· 909 857 } 910 858 911 859 .timeline .show-more { 912 - padding-left: calc(var(--line-end) + var(--line-margin-end)) !important; 913 - text-align: left; 860 + padding-inline-start: calc( 861 + var(--line-end) + var(--line-margin-end) 862 + ) !important; 863 + text-align: start; 914 864 background-color: transparent !important; 915 865 backdrop-filter: none !important; 916 866 position: relative; ··· 918 868 padding-block: 16px !important; 919 869 920 870 .avatars-bunch > .avatar:not(:first-child) { 921 - margin-left: -4px; 871 + margin-inline-start: -4px; 922 872 } 923 873 } 924 874 .timeline .show-more:hover { ··· 930 880 content: ''; 931 881 position: absolute; 932 882 top: 10px; 933 - left: var(--line-start); 883 + inset-inline-start: var(--line-start); 934 884 width: var(--line-diameter); 935 885 height: var(--line-diameter); 936 886 border-radius: var(--line-radius); 937 887 border-style: solid; 938 888 border-width: var(--line-width); 939 889 border-color: transparent transparent var(--comment-line-color) transparent; 940 - transform: rotate(45deg); 890 + transform: rotate(var(--line-curve)); 941 891 } 942 892 943 893 .status-loading { ··· 988 938 .status-carousel { 989 939 --carousel-faded-color: var(--bg-faded-color); 990 940 background: linear-gradient( 991 - to bottom right, 941 + to bottom var(--forward), 992 942 var(--carousel-faded-color), 993 943 transparent 994 944 ); ··· 1058 1008 display: none; 1059 1009 } 1060 1010 .status-carousel .status-carousel-beacon { 1061 - margin-right: calc(-1 * var(--carousel-gap)); 1011 + margin-inline-end: calc(-1 * var(--carousel-gap)); 1062 1012 pointer-events: none; 1063 1013 opacity: 0; 1064 1014 1065 1015 ~ .status-carousel-beacon { 1066 - margin-left: calc(-1 * var(--carousel-gap)); 1016 + margin-inline-start: calc(-1 * var(--carousel-gap)); 1067 1017 } 1068 1018 } 1069 1019 /* ··· 1107 1057 .status-carousel.boosts-carousel > ul > li:before { 1108 1058 content: counter(index); 1109 1059 position: absolute; 1110 - left: 0; 1060 + inset-inline-start: 0; 1111 1061 font-size: 10px; 1112 1062 color: var(--text-insignificant-color); 1113 1063 padding: 6px; ··· 1147 1097 box-shadow: 0 1px var(--bg-color); 1148 1098 1149 1099 &:has(.status-badge:not(:empty)) { 1150 - border-top-right-radius: 8px; 1100 + border-start-end-radius: 8px; 1151 1101 } 1152 1102 1153 - .status-carousel.boosts-carousel & { 1154 - border-top-left-radius: 8px; 1103 + .status-carousel.boosts-carousel &:not(.timeline-item-carousel-group &) { 1104 + border-start-start-radius: 8px; 1155 1105 } 1156 1106 } 1157 1107 .status-carousel-link::focus { ··· 1189 1139 transform: translate3d(0, 0, 0); 1190 1140 } 1191 1141 } 1142 + @keyframes slide-in-rtl { 1143 + 0% { 1144 + transform: translate3d(-100%, 0, 0); 1145 + } 1146 + 100% { 1147 + transform: translate3d(0, 0, 0); 1148 + } 1149 + } 1192 1150 .deck-backdrop .deck { 1193 1151 width: var(--main-width); 1194 1152 max-width: 100vw; 1195 1153 background-color: var(--bg-color); 1196 1154 box-shadow: -1px 0 var(--bg-color); 1155 + &:dir(rtl) { 1156 + box-shadow: 1px 0 var(--bg-color); 1157 + } 1197 1158 } 1198 1159 .deck-backdrop .deck.slide-in:not(.deck-view-full) { 1199 1160 animation: slide-in 0.5s var(--timing-function); 1161 + 1162 + &:dir(rtl) { 1163 + animation-name: slide-in-rtl; 1164 + } 1200 1165 } 1201 1166 .deck-backdrop .deck .status { 1202 1167 max-width: var(--main-width); ··· 1240 1205 content: ''; 1241 1206 display: inline-block; 1242 1207 position: absolute; 1243 - right: 10px; 1208 + inset-inline-end: 10px; 1244 1209 width: 4px; 1245 1210 height: 4px; 1246 1211 border-radius: 50%; ··· 1519 1484 .media-modal-container + .status-deck { 1520 1485 /* display: none; */ 1521 1486 position: absolute; 1522 - right: 0; 1487 + inset-inline-end: 0; 1523 1488 z-index: -1; 1524 1489 pointer-events: none; 1525 1490 user-select: none; ··· 1547 1512 ) 1548 1513 #modal-container 1549 1514 > div { 1550 - left: 0; 1551 - right: 350px; 1515 + inset-inline-start: 0; 1516 + inset-inline-end: 350px; 1552 1517 width: auto; 1553 1518 } 1554 1519 /* ✨ New */ ··· 1579 1544 position: fixed; 1580 1545 bottom: 16px; 1581 1546 bottom: max(16px, env(safe-area-inset-bottom)); 1582 - right: 16px; 1583 - right: max(16px, env(safe-area-inset-right)); 1547 + inset-inline-end: 16px; 1548 + inset-inline-end: max(16px, env(safe-area-inset-right)); 1584 1549 padding: 16px; 1585 1550 background-color: var(--button-bg-blur-color); 1586 1551 /* backdrop-filter: blur(16px); */ ··· 1629 1594 display: block; 1630 1595 position: absolute; 1631 1596 top: 0; 1632 - right: 0; 1597 + inset-inline-end: 0; 1633 1598 width: 14px; 1634 1599 height: 14px; 1635 1600 border-radius: 50%; ··· 1696 1661 border-radius: 0; 1697 1662 padding: 0; 1698 1663 right: env(safe-area-inset-right); 1664 + &:dir(rtl) { 1665 + right: auto; 1666 + left: env(safe-area-inset-left); 1667 + } 1699 1668 width: 44px; 1700 1669 height: 44px; 1701 1670 display: inline-flex; ··· 1737 1706 } 1738 1707 .sheet .sheet-close:not(.outer) + header { 1739 1708 padding-right: max(44px, env(safe-area-inset-right)); 1709 + 1710 + &:dir(rtl) { 1711 + padding-right: max(16px, env(safe-area-inset-right)); 1712 + padding-left: max(44px, env(safe-area-inset-left)); 1713 + } 1740 1714 } 1741 1715 .sheet header :is(h1, h2, h3) { 1742 1716 margin: 0; ··· 1773 1747 contain: none; 1774 1748 width: 100%; 1775 1749 height: 100%; 1750 + } 1751 + 1752 + :dir(rtl) &.rtl-flip { 1753 + transform: scaleX(-1); 1776 1754 } 1777 1755 } 1778 1756 ··· 1839 1817 border: 1px solid var(--outline-color); 1840 1818 border-radius: 8px; 1841 1819 box-shadow: 0 3px 16px -3px var(--drop-shadow-color); 1842 - text-align: left; 1820 + text-align: start; 1843 1821 /* animation: appear-smooth 0.15s ease-in-out; */ 1844 1822 width: 16em; 1845 1823 max-width: 90vw; ··· 1975 1953 } 1976 1954 .szh-menu .menu-horizontal > .szh-menu__item:not(:only-child):first-child, 1977 1955 .szh-menu .menu-horizontal > *:not(:only-child):first-child .szh-menu__item { 1978 - padding-right: 4px !important; 1956 + padding-inline-end: 4px !important; 1979 1957 } 1980 1958 .szh-menu 1981 1959 .menu-horizontal ··· 1984 1962 .menu-horizontal 1985 1963 > *:not(:only-child):not(:first-child):not(:last-child) 1986 1964 .szh-menu__item { 1987 - padding-left: 8px !important; 1988 - padding-right: 4px !important; 1965 + padding-inline-start: 8px !important; 1966 + padding-inline-end: 4px !important; 1989 1967 } 1990 1968 .szh-menu .menu-horizontal > .szh-menu__item:not(:only-child):last-child, 1991 1969 .szh-menu .menu-horizontal > *:not(:only-child):last-child .szh-menu__item { 1992 - padding-left: 8px !important; 1970 + padding-inline-start: 8px !important; 1993 1971 } 1994 1972 .szh-menu .szh-menu__item .menu-shortcut { 1995 1973 opacity: 0.5; ··· 2053 2031 text-align: center; 2054 2032 opacity: 0.5; 2055 2033 text-overflow: clip; 2056 - mask-image: linear-gradient(to left, transparent, black 16px); 2034 + mask-image: linear-gradient( 2035 + var(--to-backward), 2036 + transparent, 2037 + black 16px 2038 + ); 2057 2039 } 2058 2040 } 2059 2041 } ··· 2069 2051 } 2070 2052 2071 2053 > [class^='szh-menu']:first-child { 2072 - border-top-left-radius: 8px; 2054 + border-start-start-radius: 8px; 2073 2055 } 2074 2056 > [class^='szh-menu']:last-child { 2075 - border-top-right-radius: 8px; 2057 + border-start-end-radius: 8px; 2076 2058 } 2077 2059 } 2078 2060 } ··· 2153 2135 background-image: var(--middle-circle), 2154 2136 conic-gradient(var(--color) var(--fill), var(--outline-color) 0); 2155 2137 transform: scale(0.7); 2138 + &:dir(rtl) { 2139 + transform: scale(-0.7, 0.7); 2140 + } 2156 2141 transition: transform 0.2s ease-in-out; 2157 2142 2158 2143 &::-webkit-meter-inner-element, ··· 2362 2347 } 2363 2348 } 2364 2349 ul.link-list li:first-child a { 2365 - border-top-left-radius: var(--radius); 2366 - border-top-right-radius: var(--radius); 2350 + border-start-start-radius: var(--radius); 2351 + border-start-end-radius: var(--radius); 2367 2352 } 2368 2353 ul.link-list li:last-child a { 2369 - border-bottom-left-radius: var(--radius); 2370 - border-bottom-right-radius: var(--radius); 2354 + border-end-start-radius: var(--radius); 2355 + border-end-end-radius: var(--radius); 2371 2356 } 2372 2357 ul.link-list li a:is(:hover, :focus) { 2373 2358 color: var(--text-color); ··· 2404 2389 } 2405 2390 .nav-menu-button.with-avatar .icon { 2406 2391 position: absolute; 2407 - bottom: 4px; 2408 - right: 8px; 2392 + inset-block-end: 4px; 2393 + inset-inline-end: 8px; 2409 2394 background-color: var(--bg-color); 2410 2395 border-radius: 2px; 2411 2396 } ··· 2433 2418 } */ 2434 2419 #columns > * { 2435 2420 overscroll-behavior: auto; 2436 - scroll-snap-align: left; 2421 + scroll-snap-align: start; 2437 2422 scroll-snap-stop: always; 2438 2423 overscroll-behavior: auto; 2439 2424 flex-basis: min(100vw, 360px); 2440 2425 flex-shrink: 0; 2441 2426 box-shadow: -1px 0 var(--bg-color), -2px 0 var(--drop-shadow-color), 2442 2427 -3px 0 var(--bg-color); 2428 + &:dir(rtl) { 2429 + box-shadow: 1px 0 var(--bg-color), 2px 0 var(--drop-shadow-color), 2430 + 3px 0 var(--bg-color); 2431 + } 2443 2432 } 2444 2433 #columns:has(> :nth-child(3)) > *:nth-child(even), 2445 2434 #columns:has(> :nth-child(3)) ··· 2572 2561 gap: 8px; 2573 2562 overflow-x: auto; 2574 2563 mask-image: linear-gradient( 2575 - to right, 2564 + var(--to-forward), 2576 2565 transparent, 2577 2566 black 16px, 2578 2567 black calc(100% - 16px), ··· 2586 2575 width: 95vw; 2587 2576 max-width: calc(320px * 3.3); 2588 2577 transform: translateX(calc(-50% + var(--main-width) / 2)); 2578 + &:dir(rtl) { 2579 + transform: translateX(calc(50% - var(--main-width) / 2)); 2580 + } 2589 2581 } 2590 2582 } 2591 2583 ··· 2704 2696 min-width: 16px; 2705 2697 min-height: 16px; 2706 2698 padding: 4px; 2707 - margin: -4px -8px -4px 0; 2699 + margin: -4px 0; 2700 + margin-inline-end: -8px; 2708 2701 background-color: var(--bg-faded-color); 2709 2702 border-radius: 999px; 2710 2703 } ··· 2734 2727 .deck-container:has(~ .deck-backdrop .deck) { 2735 2728 transition: transform 0.4s ease-out; 2736 2729 transform: translate3d(-5vw, 0, 0); 2730 + &:dir(rtl) { 2731 + transform: translate3d(5vw, 0, 0); 2732 + } 2737 2733 } 2738 2734 .deck-backdrop .deck { 2739 2735 /* width: 50%; 2740 2736 min-width: var(--main-width); */ 2741 - border-left: 1px solid var(--divider-color); 2737 + border-inline-start: 1px solid var(--divider-color); 2742 2738 } 2743 2739 .timeline-deck { 2744 2740 border: 0; ··· 2799 2795 > li:not(.timeline-item-container-end, .timeline-item-container-middle):has( 2800 2796 .status-badge:not(:empty) 2801 2797 ) { 2802 - border-top-right-radius: 8px; 2798 + border-start-end-radius: 8px; 2803 2799 } 2804 2800 .timeline:not(.flat) > li:has(.status-link.is-active) { 2805 2801 transition: var(--back-transition); 2806 2802 transform: translate3d(-2.5vw, 0, 0); 2803 + &:dir(rtl) { 2804 + transform: translate3d(2.5vw, 0, 0); 2805 + } 2807 2806 } 2808 2807 .timeline:not(.flat) 2809 2808 > li.timeline-item-container:has(.status-link.is-active) { 2810 - border-top-left-radius: var(--item-radius); 2811 - border-bottom-left-radius: var(--item-radius); 2809 + border-start-start-radius: var(--item-radius); 2810 + border-end-start-radius: var(--item-radius); 2812 2811 } 2813 2812 .timeline:not(.flat) 2814 2813 > li:not(:has(.status-carousel)):has(+ li .status-link.is-active), ··· 2817 2816 + li { 2818 2817 transition: var(--back-transition); 2819 2818 transform: translate3d(-1.25vw, 0, 0); 2819 + &:dir(rtl) { 2820 + transform: translate3d(1.25vw, 0, 0); 2821 + } 2820 2822 } 2821 2823 .timeline:not(.flat) 2822 2824 > li.timeline-item-container:not(:has(.status-carousel)):has( 2823 2825 + li .status-link.is-active 2824 2826 ) { 2825 - border-top-left-radius: var(--item-radius); 2827 + border-start-start-radius: var(--item-radius); 2826 2828 } 2827 2829 .timeline:not(.flat) 2828 2830 > li.timeline-item-container:not(:has(.status-carousel)):has( 2829 2831 .status-link.is-active 2830 2832 ) 2831 2833 + li.timeline-item-container { 2832 - border-bottom-left-radius: var(--item-radius); 2834 + border-end-start-radius: var(--item-radius); 2833 2835 } 2834 2836 .box { 2835 2837 padding: 32px; ··· 2841 2843 width: 95vw; 2842 2844 max-width: calc(320px * 3.3); 2843 2845 transform: translateX(calc(-50% + var(--main-width) / 2)); 2846 + &:dir(rtl) { 2847 + transform: translateX(calc(50% - var(--main-width) / 2)); 2848 + } 2844 2849 } 2845 2850 }
+2
src/app.jsx
··· 132 132 setTimeout(() => { 133 133 if (Array.isArray(ICONS[icon])) { 134 134 ICONS[icon][0]?.(); 135 + } else if (typeof ICONS[icon] === 'object') { 136 + ICONS[icon].module?.(); 135 137 } else { 136 138 ICONS[icon]?.(); 137 139 }
+91 -26
src/components/ICONS.jsx
··· 6 6 'x-circle': () => import('@iconify-icons/mingcute/close-circle-line'), 7 7 transfer: () => import('@iconify-icons/mingcute/transfer-4-line'), 8 8 rocket: () => import('@iconify-icons/mingcute/rocket-line'), 9 - 'arrow-left': () => import('@iconify-icons/mingcute/arrow-left-line'), 10 - 'arrow-right': () => import('@iconify-icons/mingcute/arrow-right-line'), 9 + 'arrow-left': { 10 + module: () => import('@iconify-icons/mingcute/arrow-left-line'), 11 + rtl: true, 12 + }, 13 + 'arrow-right': { 14 + module: () => import('@iconify-icons/mingcute/arrow-right-line'), 15 + rtl: true, 16 + }, 11 17 'arrow-up': () => import('@iconify-icons/mingcute/arrow-up-line'), 12 18 'arrow-down': () => import('@iconify-icons/mingcute/arrow-down-line'), 13 19 earth: () => import('@iconify-icons/mingcute/earth-line'), ··· 16 22 'eye-close': () => import('@iconify-icons/mingcute/eye-close-line'), 17 23 'eye-open': () => import('@iconify-icons/mingcute/eye-2-line'), 18 24 message: () => import('@iconify-icons/mingcute/mail-line'), 19 - comment: () => import('@iconify-icons/mingcute/chat-3-line'), 20 - comment2: () => import('@iconify-icons/mingcute/comment-2-line'), 25 + comment: { 26 + module: () => import('@iconify-icons/mingcute/chat-3-line'), 27 + rtl: true, 28 + }, 29 + comment2: { 30 + module: () => import('@iconify-icons/mingcute/comment-2-line'), 31 + rtl: true, 32 + }, 21 33 home: () => import('@iconify-icons/mingcute/home-3-line'), 22 34 notification: () => import('@iconify-icons/mingcute/notification-line'), 23 35 follow: () => import('@iconify-icons/mingcute/user-follow-line'), ··· 31 43 gear: () => import('@iconify-icons/mingcute/settings-3-line'), 32 44 more: () => import('@iconify-icons/mingcute/more-3-line'), 33 45 more2: () => import('@iconify-icons/mingcute/more-1-fill'), 34 - external: () => import('@iconify-icons/mingcute/external-link-line'), 35 - popout: () => import('@iconify-icons/mingcute/external-link-line'), 36 - popin: [() => import('@iconify-icons/mingcute/external-link-line'), '180deg'], 46 + external: { 47 + module: () => import('@iconify-icons/mingcute/external-link-line'), 48 + rtl: true, 49 + }, 50 + popout: { 51 + module: () => import('@iconify-icons/mingcute/external-link-line'), 52 + rtl: true, 53 + }, 54 + popin: { 55 + module: () => import('@iconify-icons/mingcute/external-link-line'), 56 + rotate: '180deg', 57 + rtl: true, 58 + }, 37 59 plus: () => import('@iconify-icons/mingcute/add-circle-line'), 38 - 'chevron-left': () => import('@iconify-icons/mingcute/left-line'), 39 - 'chevron-right': () => import('@iconify-icons/mingcute/right-line'), 60 + 'chevron-left': { 61 + module: () => import('@iconify-icons/mingcute/left-line'), 62 + rtl: true, 63 + }, 64 + 'chevron-right': { 65 + module: () => import('@iconify-icons/mingcute/right-line'), 66 + rtl: true, 67 + }, 40 68 'chevron-down': () => import('@iconify-icons/mingcute/down-line'), 41 - reply: [ 42 - () => import('@iconify-icons/mingcute/share-forward-line'), 43 - '180deg', 44 - 'horizontal', 45 - ], 69 + reply: { 70 + module: () => import('@iconify-icons/mingcute/share-forward-line'), 71 + rotate: '180deg', 72 + flip: 'horizontal', 73 + rtl: true, 74 + }, 46 75 thread: () => import('@iconify-icons/mingcute/route-line'), 47 - group: () => import('@iconify-icons/mingcute/group-line'), 76 + group: { 77 + module: () => import('@iconify-icons/mingcute/group-line'), 78 + rtl: true, 79 + }, 48 80 bot: () => import('@iconify-icons/mingcute/android-2-line'), 49 81 menu: () => import('@iconify-icons/mingcute/rows-4-line'), 50 - list: () => import('@iconify-icons/mingcute/list-check-line'), 82 + list: { 83 + module: () => import('@iconify-icons/mingcute/list-check-line'), 84 + rtl: true, 85 + }, 51 86 search: () => import('@iconify-icons/mingcute/search-2-line'), 52 87 hashtag: () => import('@iconify-icons/mingcute/hashtag-line'), 53 88 info: () => import('@iconify-icons/mingcute/information-line'), ··· 62 97 share: () => import('@iconify-icons/mingcute/share-2-line'), 63 98 sparkles: () => import('@iconify-icons/mingcute/sparkles-line'), 64 99 sparkles2: () => import('@iconify-icons/mingcute/sparkles-2-line'), 65 - exit: () => import('@iconify-icons/mingcute/exit-line'), 100 + exit: { 101 + module: () => import('@iconify-icons/mingcute/exit-line'), 102 + rtl: true, 103 + }, 66 104 translate: () => import('@iconify-icons/mingcute/translate-line'), 67 105 play: () => import('@iconify-icons/mingcute/play-fill'), 68 106 trash: () => import('@iconify-icons/mingcute/delete-2-line'), 69 - mute: () => import('@iconify-icons/mingcute/volume-mute-line'), 70 - unmute: () => import('@iconify-icons/mingcute/volume-line'), 107 + mute: { 108 + module: () => import('@iconify-icons/mingcute/volume-mute-line'), 109 + rtl: true, 110 + }, 111 + unmute: { 112 + module: () => import('@iconify-icons/mingcute/volume-line'), 113 + rtl: true, 114 + }, 71 115 block: () => import('@iconify-icons/mingcute/forbid-circle-line'), 72 116 unblock: [ 73 117 () => import('@iconify-icons/mingcute/forbid-circle-line'), ··· 81 125 filters: () => import('@iconify-icons/mingcute/filter-line'), 82 126 chart: () => import('@iconify-icons/mingcute/chart-line-line'), 83 127 react: () => import('@iconify-icons/mingcute/react-line'), 84 - layout4: () => import('@iconify-icons/mingcute/layout-4-line'), 128 + layout4: { 129 + module: () => import('@iconify-icons/mingcute/layout-4-line'), 130 + rtl: true, 131 + }, 85 132 layout5: () => import('@iconify-icons/mingcute/layout-5-line'), 86 - announce: () => import('@iconify-icons/mingcute/announcement-line'), 133 + announce: { 134 + module: () => import('@iconify-icons/mingcute/announcement-line'), 135 + rtl: true, 136 + }, 87 137 alert: () => import('@iconify-icons/mingcute/alert-line'), 88 138 round: () => import('@iconify-icons/mingcute/round-fill'), 89 139 'arrow-up-circle': () => 90 140 import('@iconify-icons/mingcute/arrow-up-circle-line'), 91 141 'arrow-down-circle': () => 92 142 import('@iconify-icons/mingcute/arrow-down-circle-line'), 93 - clipboard: () => import('@iconify-icons/mingcute/clipboard-line'), 143 + clipboard: { 144 + module: () => import('@iconify-icons/mingcute/clipboard-line'), 145 + rtl: true, 146 + }, 94 147 'account-edit': () => import('@iconify-icons/mingcute/user-edit-line'), 95 148 'account-warning': () => import('@iconify-icons/mingcute/user-warning-line'), 96 149 keyboard: () => import('@iconify-icons/mingcute/keyboard-line'), 97 150 cloud: () => import('@iconify-icons/mingcute/cloud-line'), 98 - month: () => import('@iconify-icons/mingcute/calendar-month-line'), 151 + month: { 152 + module: () => import('@iconify-icons/mingcute/calendar-month-line'), 153 + rtl: true, 154 + }, 99 155 media: () => import('@iconify-icons/mingcute/photo-album-line'), 100 156 speak: () => import('@iconify-icons/mingcute/radar-line'), 101 157 building: () => import('@iconify-icons/mingcute/building-5-line'), 102 - history2: () => import('@iconify-icons/mingcute/history-2-line'), 158 + history2: { 159 + module: () => import('@iconify-icons/mingcute/history-2-line'), 160 + rtl: true, 161 + }, 103 162 document: () => import('@iconify-icons/mingcute/document-line'), 104 - 'arrows-right': () => import('@iconify-icons/mingcute/arrows-right-line'), 163 + 'arrows-right': { 164 + module: () => import('@iconify-icons/mingcute/arrows-right-line'), 165 + rtl: true, 166 + }, 105 167 code: () => import('@iconify-icons/mingcute/code-line'), 106 168 copy: () => import('@iconify-icons/mingcute/copy-2-line'), 107 - quote: () => import('@iconify-icons/mingcute/quote-left-line'), 169 + quote: { 170 + module: () => import('@iconify-icons/mingcute/quote-left-line'), 171 + rtl: true, 172 + }, 108 173 settings: () => import('@iconify-icons/mingcute/settings-6-line'), 109 174 'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'), 110 175 'user-x': () => import('@iconify-icons/mingcute/user-x-line'),
+2
src/components/account-block.css
··· 29 29 line-clamp: 1; 30 30 text-overflow: ellipsis; 31 31 overflow: hidden; 32 + unicode-bidi: isolate; 33 + direction: initial; 32 34 } 33 35 34 36 a {
+1 -1
src/components/account-block.jsx
··· 120 120 )} 121 121 </> 122 122 )}{' '} 123 - <span class="account-block-acct"> 123 + <span class="account-block-acct bidi-isolate"> 124 124 {acct2 ? '' : '@'} 125 125 {acct1} 126 126 <wbr />
+20 -12
src/components/account-info.css
··· 57 57 background-repeat: no-repeat; 58 58 animation: swoosh-bg-image 0.3s ease-in-out 0.3s both; 59 59 background-image: linear-gradient( 60 - to right, 60 + var(--to-forward), 61 61 var(--original-color) 0%, 62 62 var(--original-color) calc(var(--originals-percentage) - var(--gap)), 63 63 var(--gap-color) calc(var(--originals-percentage) - var(--gap)), ··· 181 181 opacity: 1; 182 182 } 183 183 .sheet .account-container .header-banner { 184 - border-top-left-radius: 16px; 185 - border-top-right-radius: 16px; 184 + border-start-start-radius: 16px; 185 + border-start-end-radius: 16px; 186 186 } 187 187 .account-container .header-banner.header-is-avatar { 188 188 mask-image: linear-gradient( ··· 288 288 align-self: center !important; 289 289 /* clip a dog ear on top right */ 290 290 clip-path: polygon(0 0, calc(100% - 4px) 0, 100% 4px, 100% 100%, 0 100%); 291 + &:dir(rtl) { 292 + /* top left */ 293 + clip-path: polygon(4px 0, 100% 0, 100% 100%, 0 100%, 0 4px); 294 + } 291 295 /* 4x4px square on top right */ 292 296 background-size: 4px 4px; 293 297 background-repeat: no-repeat; 294 298 background-position: top right; 299 + &:dir(rtl) { 300 + background-position: top left; 301 + } 295 302 background-image: linear-gradient( 296 303 to bottom, 297 304 var(--private-note-border-color), ··· 311 318 box-orient: vertical; 312 319 -webkit-line-clamp: 2; 313 320 line-clamp: 2; 314 - text-align: left; 321 + text-align: start; 315 322 } 316 323 317 324 &:hover:not(:active) { ··· 370 377 animation: appear 1s both ease-in-out; 371 378 372 379 > *:not(:first-child) { 373 - margin: 0 0 0 -4px; 380 + margin: 0; 381 + margin-inline-start: -4px; 374 382 } 375 383 } 376 384 } ··· 422 430 } 423 431 424 432 &:has(+ .account-metadata-box) { 425 - border-bottom-left-radius: 4px; 426 - border-bottom-right-radius: 4px; 433 + border-end-start-radius: 4px; 434 + border-end-end-radius: 4px; 427 435 } 428 436 429 437 + .account-metadata-box { 430 - border-top-left-radius: 4px; 431 - border-top-right-radius: 4px; 432 - border-bottom-left-radius: 16px; 433 - border-bottom-right-radius: 16px; 438 + border-start-start-radius: 4px; 439 + border-start-end-radius: 4px; 440 + border-end-start-radius: 16px; 441 + border-end-end-radius: 16px; 434 442 } 435 443 } 436 444 ··· 805 813 width: 100%; 806 814 807 815 th { 808 - text-align: left; 816 + text-align: start; 809 817 color: var(--text-insignificant-color); 810 818 font-weight: normal; 811 819 font-size: 0.8em;
+9 -2
src/components/account-info.jsx
··· 568 568 </div> 569 569 <MenuItem 570 570 onClick={() => { 571 - const handleWithInstance = acct.includes('@') ? `@${acct}` : `@${acct}@${instance}`; 571 + const handleWithInstance = acct.includes('@') 572 + ? `@${acct}` 573 + : `@${acct}@${instance}`; 572 574 try { 573 575 navigator.clipboard.writeText(handleWithInstance); 574 576 showToast('Handle copied'); ··· 1221 1223 <small> 1222 1224 Copy handle 1223 1225 <br /> 1224 - <span class="more-insignificant"> 1226 + <span class="more-insignificant bidi-isolate"> 1225 1227 @{currentInfo?.acct || acctWithInstance} 1226 1228 </span> 1227 1229 </small> ··· 1895 1897 ref={textareaRef} 1896 1898 name="note" 1897 1899 disabled={uiState === 'loading'} 1900 + dir="auto" 1898 1901 > 1899 1902 {initialNote} 1900 1903 </textarea> ··· 2017 2020 defaultValue={displayName} 2018 2021 maxLength={30} 2019 2022 disabled={uiState === 'loading'} 2023 + dir="auto" 2020 2024 /> 2021 2025 </label> 2022 2026 </p> ··· 2029 2033 maxLength={500} 2030 2034 rows="5" 2031 2035 disabled={uiState === 'loading'} 2036 + dir="auto" 2032 2037 /> 2033 2038 </label> 2034 2039 </p> ··· 2092 2097 disabled={disabled} 2093 2098 maxLength={255} 2094 2099 required={hasValue} 2100 + dir="auto" 2095 2101 /> 2096 2102 </td> 2097 2103 <td> ··· 2102 2108 disabled={disabled} 2103 2109 maxLength={255} 2104 2110 onChange={(e) => setHasValue(!!e.currentTarget.value)} 2111 + dir="auto" 2105 2112 /> 2106 2113 </td> 2107 2114 </tr>
+15 -13
src/components/compose.css
··· 16 16 } 17 17 18 18 #compose-container .compose-top { 19 - text-align: right; 20 19 display: flex; 21 20 justify-content: space-between; 22 21 gap: 8px; ··· 62 61 box-shadow: 0 -3px 12px -3px var(--drop-shadow-color); 63 62 } 64 63 #compose-container .status-preview:has(.status-badge:not(:empty)) { 65 - border-top-right-radius: 8px; 64 + border-start-end-radius: 8px; 66 65 } 67 66 #compose-container .status-preview :is(.content-container, .time) { 68 67 pointer-events: none; ··· 216 215 line-height: 1em; 217 216 } 218 217 #compose-container .toolbar-button:not(.show-field) select { 219 - right: 0; 220 - left: auto !important; 218 + inset-inline-end: 0; 219 + inset-inline-start: auto !important; 221 220 } 222 221 #compose-container 223 222 .toolbar-button:not(:disabled):is( ··· 303 302 } 304 303 #compose-container .text-expander-menu li[aria-selected] { 305 304 box-shadow: inset 4px 0 0 0 var(--button-bg-color); 305 + :dir(rtl) & { 306 + box-shadow: inset -4px 0 0 0 var(--button-bg-color); 307 + } 306 308 } 307 309 #compose-container .text-expander-menu li[data-more] { 308 310 &:not(:hover, :focus, [aria-selected]) { ··· 494 496 display: flex; 495 497 gap: 4px; 496 498 align-items: center; 497 - border-left: 1px solid var(--outline-color); 498 - padding-left: 8px; 499 + border-inline-start: 1px solid var(--outline-color); 500 + padding-inline-start: 8px; 499 501 } 500 502 501 503 #compose-container .expires-in { 502 504 flex-grow: 1; 503 - border-left: 1px solid var(--outline-color); 504 - padding-left: 8px; 505 + border-inline-start: 1px solid var(--outline-color); 506 + padding-inline-start: 8px; 505 507 display: flex; 506 508 gap: 4px; 507 509 flex-wrap: wrap; ··· 646 648 647 649 &:hover { 648 650 background-image: linear-gradient( 649 - to right, 651 + var(--to-forward), 650 652 transparent 75%, 651 653 var(--link-bg-color) 652 654 ); ··· 654 656 655 657 &.selected { 656 658 background-image: linear-gradient( 657 - to right, 659 + var(--to-forward), 658 660 var(--bg-faded-color) 75%, 659 661 var(--link-bg-color) 660 662 ); ··· 666 668 border-top: var(--hairline-width) solid var(--divider-color); 667 669 position: absolute; 668 670 bottom: 0; 669 - left: 58px; 670 - right: 0; 671 + inset-inline-start: 58px; 672 + inset-inline-end: 0; 671 673 } 672 674 673 675 &:has(+ li:is(.selected, :hover)):before, ··· 951 953 overflow-x: auto; 952 954 overflow-y: hidden; 953 955 mask-image: linear-gradient( 954 - to right, 956 + var(--to-forward), 955 957 transparent 2px, 956 958 black 16px, 957 959 black calc(100% - 16px),
+8 -2
src/components/compose.jsx
··· 27 27 import { api } from '../utils/api'; 28 28 import db from '../utils/db'; 29 29 import emojifyText from '../utils/emojify-text'; 30 + import isRTL from '../utils/is-rtl'; 30 31 import localeMatch from '../utils/locale-match'; 31 32 import localeCode2Text from '../utils/localeCode2Text'; 32 33 import openCompose from '../utils/open-compose'; ··· 103 104 const { left, width } = entry.boundingClientRect; 104 105 const { innerWidth } = window; 105 106 if (left + width > innerWidth) { 106 - menu.style.left = innerWidth - width - windowMargin + 'px'; 107 + const insetInlineStart = isRTL() ? 'right' : 'left'; 108 + menu.style[insetInlineStart] = innerWidth - width - windowMargin + 'px'; 107 109 } 108 110 } 109 111 }); ··· 1127 1129 setVisibility(e.target.value); 1128 1130 }} 1129 1131 disabled={uiState === 'loading' || !!editStatus} 1132 + dir="auto" 1130 1133 > 1131 1134 <option value="public"> 1132 1135 Public <Icon icon="earth" /> ··· 1383 1386 store.session.set('currentLanguage', value || DEFAULT_LANG); 1384 1387 }} 1385 1388 disabled={uiState === 'loading'} 1389 + dir="auto" 1386 1390 > 1387 1391 {topSupportedLanguages.map(([code, common, native]) => ( 1388 1392 <option value={code} key={code}> ··· 1716 1720 </span> 1717 1721 <span> 1718 1722 <b>${displayNameWithEmoji || username}</b> 1719 - <br>@${encodeHTML(acct)} 1723 + <br><span class="bidi-isolate">@${encodeHTML( 1724 + acct, 1725 + )}</span> 1720 1726 </span> 1721 1727 </li> 1722 1728 `;
+1 -1
src/components/drafts.css
··· 27 27 background-color: var(--bg-color); 28 28 color: var(--text-color); 29 29 border: 1px solid var(--link-faded-color); 30 - text-align: left; 30 + text-align: start; 31 31 padding: 0; 32 32 } 33 33 button.draft-item:is(:hover, :focus) {
+3 -3
src/components/generic-accounts.css
··· 62 62 border-top: var(--hairline-width) solid var(--divider-color); 63 63 position: absolute; 64 64 bottom: calc(-1 * var(--list-gap) / 2); 65 - left: 40px; 66 - right: 0; 65 + inset-inline-start: 40px; 66 + inset-inline-end: 0; 67 67 } 68 68 69 69 &:has(.reactions-block):before { 70 70 /* avatar + reactions + gap */ 71 - left: calc(40px + 16px + 8px); 71 + inset-inline-start: calc(40px + 16px + 8px); 72 72 } 73 73 } 74 74
+8 -2
src/components/icon.jsx
··· 53 53 return null; 54 54 } 55 55 56 - let rotate, flip; 56 + let rotate, 57 + flip, 58 + rtl = false; 57 59 if (Array.isArray(iconBlock)) { 58 60 [iconBlock, rotate, flip] = iconBlock; 61 + } else if (typeof iconBlock === 'object') { 62 + ({ rotate, flip, rtl } = iconBlock); 63 + iconBlock = iconBlock.module; 59 64 } 60 65 61 66 const [iconData, setIconData] = useState(ICONDATA[icon]); ··· 72 77 73 78 return ( 74 79 <span 75 - class={`icon ${className}`} 80 + class={`icon ${className} ${rtl ? 'rtl-flip' : ''}`} 76 81 title={title || alt} 77 82 style={{ 78 83 width: `${iconSize}px`, 79 84 height: `${iconSize}px`, 80 85 ...style, 81 86 }} 87 + data-icon={icon} 82 88 > 83 89 {iconData && ( 84 90 // <svg
+14 -6
src/components/media-modal.jsx
··· 10 10 import { useHotkeys } from 'react-hotkeys-hook'; 11 11 12 12 import { oklab2rgb, rgb2oklab } from '../utils/color-utils'; 13 + import isRTL from '../utils/is-rtl'; 13 14 import showToast from '../utils/show-toast'; 14 15 import states from '../utils/states'; 15 16 ··· 54 55 const differentStatusID = prevStatusID.current !== statusID; 55 56 if (differentStatusID) prevStatusID.current = statusID; 56 57 carouselRef.current.scrollTo({ 57 - left: scrollLeft, 58 + left: scrollLeft * (isRTL() ? -1 : 1), 58 59 behavior: differentStatusID ? 'auto' : 'smooth', 59 60 }); 60 61 carouselRef.current.focus(); ··· 91 92 useEffect(() => { 92 93 let handleScroll = () => { 93 94 const { clientWidth, scrollLeft } = carouselRef.current; 94 - const index = Math.round(scrollLeft / clientWidth); 95 + const index = Math.round(Math.abs(scrollLeft) / clientWidth); 95 96 setCurrentIndex(index); 96 97 }; 97 98 if (carouselRef.current) { ··· 178 179 ? { 179 180 backgroundAttachment: 'local', 180 181 backgroundImage: `linear-gradient( 181 - to right, ${mediaAccentGradient})`, 182 + to ${isRTL() ? 'left' : 'right'}, ${mediaAccentGradient})`, 182 183 } 183 184 : {} 184 185 } ··· 257 258 e.preventDefault(); 258 259 e.stopPropagation(); 259 260 carouselRef.current.scrollTo({ 260 - left: carouselRef.current.clientWidth * i, 261 + left: 262 + carouselRef.current.clientWidth * i * (isRTL() ? -1 : 1), 261 263 behavior: 'smooth', 262 264 }); 263 265 carouselRef.current.focus(); ··· 368 370 e.stopPropagation(); 369 371 carouselRef.current.focus(); 370 372 carouselRef.current.scrollTo({ 371 - left: carouselRef.current.clientWidth * (currentIndex - 1), 373 + left: 374 + carouselRef.current.clientWidth * 375 + (currentIndex - 1) * 376 + (isRTL() ? -1 : 1), 372 377 behavior: 'smooth', 373 378 }); 374 379 }} ··· 384 389 e.stopPropagation(); 385 390 carouselRef.current.focus(); 386 391 carouselRef.current.scrollTo({ 387 - left: carouselRef.current.clientWidth * (currentIndex + 1), 392 + left: 393 + carouselRef.current.clientWidth * 394 + (currentIndex + 1) * 395 + (isRTL() ? -1 : 1), 388 396 behavior: 'smooth', 389 397 }); 390 398 }}
+1 -1
src/components/media-post.css
··· 23 23 pointer-events: none; 24 24 position: absolute; 25 25 top: 0; 26 - left: 0; 26 + inset-inline-start: 0; 27 27 z-index: 1; 28 28 background-color: var(--bg-blur-color); 29 29 margin: 8px;
+13 -1
src/components/menu2.jsx
··· 1 1 import { Menu } from '@szhsin/react-menu'; 2 2 import { useRef } from 'preact/hooks'; 3 3 4 + import isRTL from '../utils/is-rtl'; 4 5 import safeBoundingBoxPadding from '../utils/safe-bounding-box-padding'; 5 6 import useWindowSize from '../utils/useWindowSize'; 6 7 7 8 // It's like Menu but with sensible defaults, bug fixes and improvements. 8 9 function Menu2(props) { 9 - const { containerProps, instanceRef: _instanceRef } = props; 10 + const { containerProps, instanceRef: _instanceRef, align } = props; 10 11 const size = useWindowSize(); 11 12 const instanceRef = _instanceRef?.current ? _instanceRef : useRef(); 12 13 14 + // Values: start, end, center 15 + // Note: don't mess with 'center' 16 + const rtlAlign = isRTL() 17 + ? align === 'end' 18 + ? 'start' 19 + : align === 'start' 20 + ? 'end' 21 + : align 22 + : align; 23 + 13 24 return ( 14 25 <Menu 15 26 boundingBoxPadding={safeBoundingBoxPadding()} 16 27 repositionFlag={`${size.width}x${size.height}`} 17 28 unmountOnClose 18 29 {...props} 30 + align={rtlAlign} 19 31 instanceRef={instanceRef} 20 32 containerProps={{ 21 33 onClick: (e) => {
+14 -5
src/components/modal.css
··· 1 1 #modal-container > div { 2 2 position: fixed; 3 3 top: 0; 4 - right: 0; 4 + inset-inline-end: 0; 5 5 height: 100%; 6 6 width: 100%; 7 7 z-index: 1000; ··· 26 26 user-select: none; 27 27 overflow: hidden; 28 28 transform: scale(0); 29 - --right: max( 29 + --end: max( 30 30 var(--compose-button-dimension-margin), 31 31 env(safe-area-inset-right) 32 32 ); 33 + :dir(rtl) & { 34 + --end: max( 35 + var(--compose-button-dimension-margin), 36 + env(safe-area-inset-left) 37 + ); 38 + } 33 39 --bottom: max( 34 40 var(--compose-button-dimension-margin), 35 41 env(safe-area-inset-bottom) 36 42 ); 37 - --origin-right: calc( 38 - 100% - var(--compose-button-dimension-half) - var(--right) 43 + --origin-end: calc( 44 + 100% - var(--compose-button-dimension-half) - var(--end) 39 45 ); 46 + :dir(rtl) & { 47 + --origin-end: calc(var(--compose-button-dimension-half) + var(--end)); 48 + } 40 49 --origin-bottom: calc( 41 50 100% - var(--compose-button-dimension-half) - var(--bottom) 42 51 ); 43 - transform-origin: var(--origin-right) var(--origin-bottom); 52 + transform-origin: var(--origin-end) var(--origin-bottom); 44 53 } 45 54 46 55 .sheet {
+3 -3
src/components/name-text.jsx
··· 88 88 )} 89 89 {displayName && !short ? ( 90 90 <> 91 - <b> 91 + <b dir="auto"> 92 92 <EmojiText text={displayName} emojis={emojis} /> 93 93 </b> 94 94 {!showAcct && !hideUsername && ( 95 95 <> 96 96 {' '} 97 - <i>@{username}</i> 97 + <i class="bidi-isolate">@{username}</i> 98 98 </> 99 99 )} 100 100 </> ··· 106 106 {showAcct && ( 107 107 <> 108 108 <br /> 109 - <i> 109 + <i class="bidi-isolate"> 110 110 {acct2 ? '' : '@'} 111 111 {acct1} 112 112 {!!acct2 && <span class="ib">{acct2}</span>}
+8 -4
src/components/nav-menu.css
··· 35 35 } 36 36 .nav-menu section:last-child { 37 37 background-image: linear-gradient( 38 - to right, 38 + var(--to-forward), 39 39 var(--divider-color) 1px, 40 40 transparent 1px 41 41 ), 42 - linear-gradient(to bottom left, var(--bg-blur-color), transparent), 42 + linear-gradient( 43 + to bottom var(--backward), 44 + var(--bg-blur-color), 45 + transparent 46 + ), 43 47 url(../assets/phanpy-bg.svg); 44 48 background-repeat: no-repeat; 45 49 /* background-size: auto, auto, 200%; */ ··· 49 53 position: sticky; 50 54 top: 0; 51 55 animation: phanpying 0.2s ease-in-out both; 52 - border-top-right-radius: inherit; 53 - border-bottom-right-radius: inherit; 56 + border-start-end-radius: inherit; 57 + border-end-end-radius: inherit; 54 58 margin-bottom: 0; 55 59 display: flex; 56 60 flex-direction: column;
-3
src/components/poll.jsx
··· 187 187 type="button" 188 188 class="plain small" 189 189 disabled={uiState === 'loading'} 190 - style={{ 191 - marginLeft: -8, 192 - }} 193 190 onClick={(e) => { 194 191 e.preventDefault(); 195 192 setUIState('loading');
+2 -2
src/components/report-modal.css
··· 92 92 pointer-events: none; 93 93 user-select: none; 94 94 position: absolute; 95 - right: 32px; 95 + inset-inline-end: 32px; 96 96 margin-top: -48px; 97 97 animation: rubber-stamp 0.3s ease-in both; 98 98 position: absolute; ··· 148 148 } 149 149 150 150 .report-rules { 151 - margin-left: 1.75em; 151 + margin-inline-start: 1.75em; 152 152 } 153 153 } 154 154
+1
src/components/search-form.jsx
··· 273 273 class={`search-popover-item ${i === 0 ? 'focus' : ''}`} 274 274 // hidden={hidden} 275 275 onClick={(e) => { 276 + console.log('onClick', e); 276 277 props?.onSubmit?.(e); 277 278 }} 278 279 >
+9 -9
src/components/shortcuts-settings.css
··· 18 18 counter-increment: index; 19 19 display: inline-block; 20 20 width: 1.2em; 21 - text-align: right; 22 - margin-right: 8px; 21 + text-align: end; 22 + margin-inline-end: 8px; 23 23 color: var(--text-insignificant-color); 24 24 font-size: 90%; 25 25 flex-shrink: 0; ··· 55 55 justify-content: center; 56 56 } 57 57 #shortcuts-settings-container .shortcuts-view-mode label:first-child { 58 - border-top-left-radius: 16px; 59 - border-bottom-left-radius: 16px; 58 + border-start-start-radius: 16px; 59 + border-end-start-radius: 16px; 60 60 } 61 61 #shortcuts-settings-container .shortcuts-view-mode label:last-child { 62 - border-top-right-radius: 16px; 63 - border-bottom-right-radius: 16px; 62 + border-start-end-radius: 16px; 63 + border-end-end-radius: 16px; 64 64 } 65 65 #shortcuts-settings-container .shortcuts-view-mode label img { 66 66 max-height: 64px; ··· 114 114 } 115 115 #shortcut-settings-form label > span:first-child { 116 116 flex-basis: 5em; 117 - text-align: right; 117 + text-align: end; 118 118 } 119 119 #shortcut-settings-form :is(input[type='text'], select) { 120 120 flex-grow: 1; ··· 185 185 counter-increment: index; 186 186 display: inline-block; 187 187 width: 1.2em; 188 - text-align: right; 189 - margin-right: 8px; 188 + text-align: end; 189 + margin-inline-end: 8px; 190 190 color: var(--text-insignificant-color); 191 191 font-size: 90%; 192 192 flex-shrink: 0;
+5
src/components/shortcuts-settings.jsx
··· 612 612 }} 613 613 defaultValue={editMode ? shortcut.type : undefined} 614 614 name="type" 615 + dir="auto" 615 616 > 616 617 <option></option> 617 618 {TYPES.map((type) => ( ··· 632 633 required={!notRequired} 633 634 disabled={disabled || uiState === 'loading'} 634 635 defaultValue={editMode ? shortcut.id : undefined} 636 + dir="auto" 635 637 > 636 638 <option value=""></option> 637 639 {lists.map((list) => ( ··· 663 665 autocapitalize="off" 664 666 spellCheck={false} 665 667 pattern={pattern} 668 + dir="auto" 666 669 /> 667 670 {currentType === 'hashtag' && 668 671 followedHashtags.length > 0 && ( ··· 780 783 onInput={(e) => { 781 784 setImportShortcutStr(e.target.value); 782 785 }} 786 + dir="auto" 783 787 /> 784 788 {states.settings.shortcutSettingsCloudImportExport && ( 785 789 <button ··· 996 1000 showToast('Unable to copy shortcuts'); 997 1001 } 998 1002 }} 1003 + dir="auto" 999 1004 /> 1000 1005 </p> 1001 1006 <p>
+5 -5
src/components/shortcuts.css
··· 2 2 position: fixed; 3 3 bottom: 16px; 4 4 bottom: max(16px, env(safe-area-inset-bottom)); 5 - left: 16px; 6 - left: max(16px, env(safe-area-inset-left)); 5 + inset-inline-start: 16px; 6 + inset-inline-start: max(16px, env(safe-area-inset-left)); 7 7 padding: 16px; 8 8 background-color: var(--bg-faded-blur-color); 9 9 z-index: 101; ··· 34 34 35 35 @media (min-width: calc(40em + 56px + 8px)) { 36 36 #shortcuts-button { 37 - right: 16px; 38 - right: max(16px, env(safe-area-inset-right)); 39 - left: auto; 37 + inset-inline-end: 16px; 38 + inset-inline-end: max(16px, env(safe-area-inset-right)); 39 + inset-inline-start: auto; 40 40 top: 16px; 41 41 top: max(16px, env(safe-area-inset-top)); 42 42 bottom: auto;
+107 -65
src/components/status.css
··· 1 1 /* REBLOG + REPLY-TO */ 2 2 3 + :root { 4 + --post-gradient-angle: 160deg; 5 + --post-gradient-chip-angle: -20deg; 6 + &:dir(rtl) { 7 + --post-gradient-angle: -160deg; 8 + --post-gradient-chip-angle: 20deg; 9 + } 10 + } 11 + 3 12 .status-reblog { 4 13 background: linear-gradient( 5 - 160deg, 14 + var(--post-gradient-angle), 6 15 var(--reblog-faded-color), 7 16 transparent min(160px, 50%) 8 17 ); 9 18 } 10 19 .status-group { 11 20 background: linear-gradient( 12 - 160deg, 21 + var(--post-gradient-angle), 13 22 var(--group-faded-color), 14 23 transparent min(160px, 50%) 15 24 ); 16 25 } 17 26 .status-followed-tags { 18 27 background: linear-gradient( 19 - 160deg, 28 + var(--post-gradient-angle), 20 29 var(--hashtag-faded-color), 21 30 transparent min(160px, 50%) 22 31 ); ··· 33 42 } 34 43 .status-reply-to { 35 44 background: linear-gradient( 36 - 160deg, 45 + var(--post-gradient-angle), 37 46 var(--reply-to-faded-color), 38 47 transparent min(160px, 50%) 39 48 ); 40 49 } 41 50 :is(.status-reblog, .status-group, .status-followed-tags) .status-reply-to { 42 51 background: linear-gradient( 43 - -20deg, 52 + var(--post-gradient-chip-angle), 44 53 var(--reply-to-faded-color), 45 54 transparent min(160px, 50%) 46 55 ); ··· 72 81 } 73 82 .status-reblog .status-pre-meta .icon { 74 83 color: var(--reblog-color); 75 - margin-right: 4px; 84 + margin-inline-end: 4px; 76 85 vertical-align: text-bottom; 77 86 } 78 87 .status-group .status-pre-meta .icon { 79 88 color: var(--group-color); 80 - margin-right: 4px; 89 + margin-inline-end: 4px; 81 90 vertical-align: text-bottom; 82 91 } 83 92 .status-followed-tags { ··· 91 100 92 101 .icon { 93 102 color: var(--hashtag-color); 94 - margin-right: 4px; 103 + margin-inline-end: 4px; 95 104 vertical-align: text-bottom; 96 105 } 97 106 a { ··· 208 217 /* filter: drop-shadow(0 2px 4px var(--bg-faded-color)); */ 209 218 } 210 219 .status-card:has(.status-badge:not(:empty)) { 211 - border-top-right-radius: 8px; 220 + border-start-end-radius: 8px; 212 221 } 213 222 .status-card > * { 214 223 pointer-events: none; ··· 276 285 align-items: center; 277 286 278 287 .status-carousel & { 279 - padding: 16px 16px 16px 24px; 288 + padding: 16px; 289 + padding-inline-start: 24px; 280 290 } 281 291 } 282 292 .status.filtered .status-filtered-info { ··· 286 296 white-space: nowrap; 287 297 overflow: hidden; 288 298 text-overflow: ellipsis; 289 - mask-image: linear-gradient(to right, black 90%, transparent); 299 + mask-image: linear-gradient(var(--to-forward), black 90%, transparent); 290 300 position: relative; 291 301 } 292 302 .status.filtered .avatar { ··· 312 322 opacity: 0; 313 323 transform: translateX(8px); 314 324 position: absolute; 315 - left: 0; 325 + inset-inline-start: 0; 316 326 } 317 327 .status.filtered:is(:hover, :focus, :active) .status-filtered-info-2 { 318 328 opacity: 0.75; ··· 353 363 padding-bottom: 0; 354 364 margin-bottom: calc(-1 * var(--top-padding) / 2); 355 365 background-image: linear-gradient( 356 - 160deg, 366 + var(--post-gradient-angle), 357 367 transparent 2.5%, 358 368 var(--reply-to-faded-color) 10%, 359 369 transparent ··· 381 391 content: ''; 382 392 position: absolute; 383 393 top: calc(var(--top-padding) + var(--avatar-size)); 384 - left: var(--line-start); 394 + inset-inline-start: var(--line-start); 385 395 width: var(--line-width); 386 396 height: calc( 387 397 100% - var(--top-padding) - var(--avatar-size) + (var(--top-padding) / 2) ··· 392 402 } 393 403 394 404 .avatar { 395 - margin-left: calc((50px - var(--avatar-size)) / 2); 405 + margin-inline-start: calc((50px - var(--avatar-size)) / 2); 396 406 justify-self: center; 397 407 z-index: 1; 398 408 } ··· 433 443 min-width: 0; 434 444 } 435 445 .status:not(.small) > .container { 436 - padding-left: 12px; 446 + padding-inline-start: 12px; 437 447 } 438 448 439 449 .status > .container > .meta { ··· 451 461 /* text-overflow: ellipsis; */ 452 462 } 453 463 .status > .container > .meta .meta-name { 454 - mask-image: linear-gradient(to left, transparent, black 16px); 464 + mask-image: linear-gradient(var(--to-backward), transparent, black 16px); 455 465 flex-grow: 1; 456 466 457 467 .name-text b { ··· 470 480 text-align: end; 471 481 text-decoration: none; 472 482 flex-shrink: 0; 473 - margin-left: 4px; 483 + margin-inline-start: 4px; 474 484 white-space: nowrap; 475 485 } 476 486 .status > .container > .meta a.time { ··· 482 492 font-size: 90%; 483 493 484 494 .more { 485 - margin-left: 4px; 495 + margin-inline-start: 4px; 486 496 transition: transform 0.2s ease-out; 487 497 } 488 498 } ··· 509 519 510 520 .status-reply-badge { 511 521 display: inline-flex; 512 - margin: 2px 0 2px 4px; 522 + margin: 2px 0; 523 + margin-inline-start: 4px; 513 524 gap: 4px; 514 525 align-items: center; 515 526 vertical-align: middle; ··· 609 620 position: absolute; 610 621 width: 100%; 611 622 top: calc(100% + 2px); 612 - left: 0; 623 + inset-inline-start: 0; 613 624 text-align: center; 614 625 } 615 626 .status-filtered-badge.horizontal.badge-meta > span + span { ··· 618 629 } 619 630 620 631 .status.large > .container > .content-container { 621 - margin-left: calc(-50px - 16px); 632 + margin-inline-start: calc(-50px - 16px); 622 633 padding-top: 10px; 623 634 padding-bottom: 10px; 624 635 } ··· 1005 1016 .media-gt2 1006 1017 ) { 1007 1018 /* 50px = avatar size */ 1008 - margin-left: calc(-1 * ((50px / 2))); 1019 + margin-inline-start: calc(-1 * ((50px / 2))); 1009 1020 /* 1010 1021 outer padding = 16px 1011 1022 gap = 12px 1012 1023 so... 16 - 12 = 4 1013 1024 */ 1014 - margin-right: -4px; 1025 + margin-inline-end: -4px; 1015 1026 } 1016 1027 .status.large :is(.media-container, .media-container.media-gt2) { 1017 1028 height: auto; ··· 1121 1132 } 1122 1133 /* Special media borders */ 1123 1134 .status .media-container.media-eq2 .media:first-of-type { 1124 - border-radius: var(--media-radius) var(--media-radius-inner) 1125 - var(--media-radius-inner) var(--media-radius); 1135 + border-start-end-radius: var(--media-radius-inner); 1136 + border-end-end-radius: var(--media-radius-inner); 1126 1137 } 1127 1138 .status .media-container.media-eq2 .media:last-of-type { 1128 - border-radius: var(--media-radius-inner) var(--media-radius) 1129 - var(--media-radius) var(--media-radius-inner); 1139 + border-start-start-radius: var(--media-radius-inner); 1140 + border-end-start-radius: var(--media-radius-inner); 1130 1141 } 1131 1142 .status .media-container.media-eq3 .media:first-of-type { 1132 - border-radius: var(--media-radius) var(--media-radius-inner) 1133 - var(--media-radius-inner) var(--media-radius); 1143 + border-start-end-radius: var(--media-radius-inner); 1144 + border-end-end-radius: var(--media-radius-inner); 1134 1145 } 1135 1146 .status .media-container.media-eq3 .media:nth-of-type(2) { 1136 - border-radius: var(--media-radius-inner) var(--media-radius) 1137 - var(--media-radius-inner) var(--media-radius-inner); 1147 + border-start-start-radius: var(--media-radius-inner); 1148 + border-end-end-radius: var(--media-radius-inner); 1149 + border-end-start-radius: var(--media-radius-inner); 1138 1150 } 1139 1151 .status .media-container.media-eq3 .media:last-of-type { 1140 - border-radius: var(--media-radius-inner) var(--media-radius-inner) 1141 - var(--media-radius) var(--media-radius-inner); 1152 + border-start-start-radius: var(--media-radius-inner); 1153 + border-start-end-radius: var(--media-radius-inner); 1154 + border-end-start-radius: var(--media-radius-inner); 1142 1155 } 1143 1156 .status .media-container.media-eq4 .media:first-of-type { 1144 - border-radius: var(--media-radius) var(--media-radius-inner) 1145 - var(--media-radius-inner) var(--media-radius-inner); 1157 + border-start-end-radius: var(--media-radius-inner); 1158 + border-end-end-radius: var(--media-radius-inner); 1159 + border-end-start-radius: var(--media-radius-inner); 1146 1160 } 1147 1161 .status .media-container.media-eq4 .media:nth-of-type(2) { 1148 - border-radius: var(--media-radius-inner) var(--media-radius) 1149 - var(--media-radius-inner) var(--media-radius-inner); 1162 + border-start-start-radius: var(--media-radius-inner); 1163 + border-end-end-radius: var(--media-radius-inner); 1164 + border-end-start-radius: var(--media-radius-inner); 1150 1165 } 1151 1166 .status .media-container.media-eq4 .media:nth-of-type(3) { 1152 - border-radius: var(--media-radius-inner) var(--media-radius-inner) 1153 - var(--media-radius-inner) var(--media-radius); 1167 + border-start-start-radius: var(--media-radius-inner); 1168 + border-start-end-radius: var(--media-radius-inner); 1169 + border-end-end-radius: var(--media-radius-inner); 1154 1170 } 1155 1171 .status .media-container.media-eq4 .media:last-of-type { 1156 - border-radius: var(--media-radius-inner) var(--media-radius-inner) 1157 - var(--media-radius) var(--media-radius-inner); 1172 + border-start-start-radius: var(--media-radius-inner); 1173 + border-start-end-radius: var(--media-radius-inner); 1174 + border-end-start-radius: var(--media-radius-inner); 1158 1175 } 1159 1176 .status .media:only-child { 1160 1177 grid-area: span 2 / span 2; ··· 1207 1224 .alt-badge { 1208 1225 position: absolute; 1209 1226 bottom: 8px; 1210 - left: 8px; 1227 + inset-inline-start: 8px; 1211 1228 1212 1229 &:before { 1213 1230 content: ''; ··· 1266 1283 content: attr(data-formatted-duration); 1267 1284 position: absolute; 1268 1285 bottom: 8px; 1269 - right: 8px; 1286 + inset-inline-end: 8px; 1270 1287 color: var(--media-fg-color); 1271 1288 background-color: var(--media-bg-color); 1272 1289 border: var(--hairline-width) solid var(--media-outline-color); ··· 1283 1300 content: attr(data-label); 1284 1301 position: absolute; 1285 1302 bottom: 8px; 1286 - right: 8px; 1303 + inset-inline-end: 8px; 1287 1304 color: var(--media-fg-color); 1288 1305 background-color: var(--media-bg-color); 1289 1306 border: var(--hairline-width) solid var(--media-outline-color); ··· 1456 1473 z-index: 1; 1457 1474 position: absolute; 1458 1475 top: 8px; 1459 - right: 8px; 1476 + inset-inline-end: 8px; 1460 1477 color: var(--media-fg-color); 1461 1478 background-color: var(--media-bg-color); 1462 1479 padding: 2px 8px; ··· 1484 1501 } 1485 1502 1486 1503 + .carousel-button { 1487 - left: auto; 1488 - right: 8px; 1504 + inset-inline-start: auto; 1505 + inset-inline-end: 8px; 1489 1506 } 1490 1507 } 1491 1508 ··· 1655 1672 display: none; 1656 1673 1657 1674 + * { 1658 - margin-left: 1ex; 1675 + margin-inline-start: 1ex; 1659 1676 } 1660 1677 } 1661 1678 } ··· 1746 1763 --bottom: 16px; 1747 1764 bottom: var(--bottom); 1748 1765 bottom: calc(var(--bottom) + env(safe-area-inset-bottom)); 1749 - left: 16px; 1750 - left: calc(16px + env(safe-area-inset-left)); 1751 - text-align: left; 1766 + inset-inline-start: 16px; 1767 + inset-inline-start: calc(16px + env(safe-area-inset-left)); 1768 + text-align: start; 1752 1769 border-radius: 8px; 1753 1770 color: var(--text-color); 1754 1771 padding: 4px 8px; ··· 2023 2040 z-index: 0; 2024 2041 } 2025 2042 .poll-option:first-child:after { 2026 - border-top-left-radius: 12px; 2027 - border-top-right-radius: 12px; 2043 + border-start-start-radius: 12px; 2044 + border-start-end-radius: 12px; 2028 2045 } 2029 2046 .poll-option:hover:after { 2030 2047 opacity: 1; ··· 2057 2074 opacity: 1; 2058 2075 } 2059 2076 .poll-vote-button { 2060 - margin: 8px 8px 0 12px; 2077 + margin: 8px 0 0; 2078 + margin-inline-start: 12px; 2079 + margin-inline-end: 8px; 2061 2080 /* padding-inline: 24px; */ 2062 2081 min-width: 160px; 2063 2082 } ··· 2066 2085 margin: 8px 16px; 2067 2086 font-size: 90%; 2068 2087 user-select: none; 2088 + 2089 + > button:first-child { 2090 + margin-inline-start: -8px; 2091 + } 2069 2092 } 2070 2093 .poll-option-title { 2071 2094 text-shadow: 0 1px var(--bg-color); ··· 2101 2124 } 2102 2125 .status.large .extra-meta { 2103 2126 padding-top: 0; 2104 - margin-left: calc(-50px - 16px); 2127 + margin-inline-start: calc(-50px - 16px); 2105 2128 } 2106 2129 2107 2130 /* EMOJI REACTIONS */ 2108 2131 2109 2132 .status.large .emoji-reactions { 2110 2133 cursor: default; 2111 - margin-left: calc(-50px - 16px); 2134 + margin-inline-start: calc(-50px - 16px); 2112 2135 } 2113 2136 2114 2137 /* ACTIONS */ ··· 2120 2143 .status.large .actions { 2121 2144 padding-top: 4px; 2122 2145 padding-bottom: 16px; 2123 - margin-left: calc(-50px - 16px); 2146 + margin-inline-start: calc(-50px - 16px); 2124 2147 color: var(--text-insignificant-color); 2125 2148 border-top: var(--hairline-width) solid var(--outline-color); 2126 2149 margin-top: 8px; ··· 2279 2302 width: 100%; 2280 2303 border: 1px solid var(--outline-color); 2281 2304 background: linear-gradient( 2282 - to bottom right, 2305 + to bottom var(--forward), 2283 2306 var(--bg-faded-color), 2284 2307 transparent 160px 2285 2308 ); ··· 2297 2320 display: flex; 2298 2321 position: absolute; 2299 2322 top: -6px; 2300 - right: 8px; 2323 + inset-inline-end: 8px; 2301 2324 background-color: var(--bg-color); 2302 2325 border-radius: 8px; 2303 2326 z-index: 1; ··· 2307 2330 opacity: 0; 2308 2331 pointer-events: none; 2309 2332 transform: translate3d(0, 6px, 0); 2310 - transform-origin: right center; 2333 + transform-origin: var(--forward) center; 2311 2334 transition: all 0.15s ease-out 0.3s, border-color 0.3s ease-out; 2312 2335 2313 2336 .timeline.contextual .replies[data-comments-level='4'] & { ··· 2386 2409 } 2387 2410 } 2388 2411 .timeline.contextual .descendant .status { 2412 + --bg-gradient-rotation: -140deg; 2413 + :dir(rtl) & { 2414 + --bg-gradient-rotation: 140deg; 2415 + } 2416 + 2389 2417 --bg-gradient: linear-gradient( 2390 - -140deg, 2418 + var(--bg-gradient-rotation), 2391 2419 var(--bg-faded-color), 2392 2420 transparent 75% 2393 2421 ); ··· 2414 2442 .status-badge { 2415 2443 position: absolute; 2416 2444 top: 4px; 2417 - right: 4px; 2445 + inset-inline-end: 4px; 2418 2446 line-height: 0; 2419 2447 pointer-events: none; 2420 2448 opacity: 0.75; ··· 2441 2469 transform: translateX(0); 2442 2470 } 2443 2471 } 2472 + @keyframes swoosh-from-left { 2473 + 0% { 2474 + opacity: 0; 2475 + transform: translateX(-300%); 2476 + } 2477 + 100% { 2478 + opacity: 1; 2479 + transform: translateX(0); 2480 + } 2481 + } 2444 2482 .status-badge > * { 2445 2483 animation: swoosh-from-right 1s cubic-bezier(0.51, 0.28, 0.16, 1.26) both; 2484 + :dir(rtl) & { 2485 + animation-name: swoosh-from-left; 2486 + } 2446 2487 } 2447 2488 .status-badge > *:nth-child(2) { 2448 2489 animation-delay: 0.1s; ··· 2457 2498 /* MISC */ 2458 2499 2459 2500 .status-aside { 2460 - padding: 0 16px 16px 80px; 2501 + padding: 0 16px 16px; 2502 + padding-inline-start: 80px; 2461 2503 color: var(--text-insignificant-color); 2462 2504 } 2463 2505 ··· 2740 2782 vertical-align: super; 2741 2783 font-weight: normal; 2742 2784 line-height: 0; 2743 - padding-left: 2px; 2785 + padding-inline-start: 2px; 2744 2786 } 2745 2787 2746 2788 &.clickable {
+14 -6
src/components/status.jsx
··· 46 46 import getHTMLText from '../utils/getHTMLText'; 47 47 import handleContentLinks from '../utils/handle-content-links'; 48 48 import htmlContentLength from '../utils/html-content-length'; 49 + import isRTL from '../utils/is-rtl'; 49 50 import isMastodonLinkMaybe from '../utils/isMastodonLinkMaybe'; 50 51 import localeMatch from '../utils/locale-match'; 51 52 import mem from '../utils/mem'; ··· 2363 2364 useEffect(() => { 2364 2365 let handleScroll = () => { 2365 2366 const { clientWidth, scrollLeft } = carouselRef.current; 2366 - const index = Math.round(scrollLeft / clientWidth); 2367 + const index = Math.round(Math.abs(scrollLeft) / clientWidth); 2367 2368 setCurrentIndex(index); 2368 2369 }; 2369 2370 if (carouselRef.current) { ··· 2407 2408 e.stopPropagation(); 2408 2409 carouselRef.current.focus(); 2409 2410 carouselRef.current.scrollTo({ 2410 - left: carouselRef.current.clientWidth * (currentIndex - 1), 2411 + left: 2412 + carouselRef.current.clientWidth * 2413 + (currentIndex - 1) * 2414 + (isRTL() ? -1 : 1), 2411 2415 behavior: 'smooth', 2412 2416 }); 2413 2417 }} ··· 2425 2429 e.stopPropagation(); 2426 2430 carouselRef.current.focus(); 2427 2431 carouselRef.current.scrollTo({ 2428 - left: carouselRef.current.clientWidth * (currentIndex + 1), 2432 + left: 2433 + carouselRef.current.clientWidth * 2434 + (currentIndex + 1) * 2435 + (isRTL() ? -1 : 1), 2429 2436 behavior: 'smooth', 2430 2437 }); 2431 2438 }} ··· 2580 2587 class={`card link ${isPost ? 'card-post' : ''} ${ 2581 2588 blurhashImage ? '' : size 2582 2589 }`} 2583 - lang={language} 2584 - dir="auto" 2585 2590 style={{ 2586 2591 '--average-color': 2587 2592 rgbAverageColor && `rgb(${rgbAverageColor.join(',')})`, ··· 2608 2613 }} 2609 2614 /> 2610 2615 </div> 2611 - <div class="meta-container"> 2616 + <div class="meta-container" lang={language}> 2612 2617 <p class="meta domain"> 2613 2618 <span class="domain">{domain}</span>{' '} 2614 2619 {!!publishedAt && <>&middot; </>} ··· 2685 2690 rel="nofollow noopener noreferrer" 2686 2691 class={`card link ${isPost ? 'card-post' : ''} no-image`} 2687 2692 lang={language} 2693 + dir="auto" 2688 2694 onClick={handleClick} 2689 2695 > 2690 2696 <div class="meta-container"> ··· 2987 2993 onClick={(e) => { 2988 2994 e.target.select(); 2989 2995 }} 2996 + dir="auto" 2990 2997 > 2991 2998 {htmlCode} 2992 2999 </textarea> ··· 3133 3140 <output 3134 3141 class="embed-preview" 3135 3142 dangerouslySetInnerHTML={{ __html: htmlCode }} 3143 + dir="auto" 3136 3144 /> 3137 3145 <p> 3138 3146 <small>Note: This preview is lightly styled.</small>
+9 -2
src/components/timeline.jsx
··· 13 13 14 14 import FilterContext from '../utils/filter-context'; 15 15 import { filteredItems, isFiltered } from '../utils/filters'; 16 + import isRTL from '../utils/is-rtl'; 16 17 import states, { statusKey } from '../utils/states'; 17 18 import statusPeek from '../utils/status-peek'; 18 19 import { isMediaFirstInstance } from '../utils/store-utils'; ··· 864 865 class="small plain2" 865 866 // disabled={reachStart} 866 867 onClick={() => { 868 + const left = 869 + Math.min(320, carouselRef.current?.offsetWidth) * 870 + (isRTL() ? 1 : -1); 867 871 carouselRef.current?.scrollBy({ 868 - left: -Math.min(320, carouselRef.current?.offsetWidth), 872 + left, 869 873 behavior: 'smooth', 870 874 }); 871 875 }} ··· 878 882 class="small plain2" 879 883 // disabled={reachEnd} 880 884 onClick={() => { 885 + const left = 886 + Math.min(320, carouselRef.current?.offsetWidth) * 887 + (isRTL() ? -1 : 1); 881 888 carouselRef.current?.scrollBy({ 882 - left: Math.min(320, carouselRef.current?.offsetWidth), 889 + left, 883 890 behavior: 'smooth', 884 891 }); 885 892 }}
+4 -3
src/components/translation-block.css
··· 35 35 border-bottom: 0; 36 36 margin-bottom: -1px; 37 37 background-image: linear-gradient( 38 - to top left, 38 + to top var(--backward), 39 39 var(--bg-color) 50%, 40 40 var(--bg-faded-blur-color) 41 41 ); ··· 44 44 .status-translation-block .translated-block { 45 45 border: 1px solid var(--outline-color); 46 46 line-height: 1.3; 47 - border-radius: 0 8px 8px 8px; 47 + border-radius: 8px; 48 + border-start-start-radius: 0; 48 49 margin: 0; 49 50 padding: 8px; 50 51 background-color: var(--bg-color); 51 52 background-image: linear-gradient( 52 - to bottom right, 53 + to bottom var(--forward), 53 54 var(--bg-color), 54 55 var(--bg-faded-blur-color) 55 56 );
+16
src/index.css
··· 110 110 --spring-timing-funtion: cubic-bezier(0.175, 0.885, 0.32, 1.275); 111 111 112 112 --min-dimension: 88px; 113 + 114 + --forward: right; 115 + --backward: left; 116 + --to-forward: to right; 117 + --to-backward: to left; 118 + &:dir(rtl) { 119 + --forward: left; 120 + --backward: right; 121 + --to-forward: to left; 122 + --to-backward: to right; 123 + } 113 124 } 114 125 115 126 @media (min-resolution: 2dppx) { ··· 440 451 } 441 452 :has(:focus-visible) .hide-until-focus-visible { 442 453 display: initial; 454 + } 455 + 456 + .bidi-isolate { 457 + direction: initial; 458 + unicode-bidi: isolate; 443 459 } 444 460 445 461 /* KEYFRAMES */
+2 -2
src/pages/accounts.css
··· 28 28 } 29 29 30 30 #accounts-container section > ul > li .current { 31 - margin-right: 8px; 31 + margin-inline-end: 8px; 32 32 color: var(--green-color); 33 33 opacity: 0.1; 34 34 } ··· 47 47 } 48 48 49 49 #accounts-container .avatar { 50 - margin-right: 8px; 50 + margin-inline-end: 8px; 51 51 } 52 52 53 53 #accounts-container .accounts-list li div {
+27 -17
src/pages/catchup.css
··· 111 111 margin-bottom: 8px; 112 112 align-items: center; 113 113 gap: 8px; 114 - text-align: left; 114 + text-align: start; 115 115 justify-content: space-between; 116 116 117 117 a { ··· 146 146 input[type='range'] { 147 147 accent-color: var(--link-color); 148 148 direction: rtl; 149 + &:dir(rtl) { 150 + direction: ltr; 151 + } 149 152 } 150 153 } 151 154 ··· 251 254 overflow-y: hidden; 252 255 max-width: 100%; 253 256 mask-image: linear-gradient( 254 - to right, 257 + var(--to-forward), 255 258 transparent, 256 259 black 16px calc(100% - 16px), 257 260 transparent ··· 315 318 316 319 .count { 317 320 font-size: 70%; 318 - margin-left: 4px; 321 + margin-inline-start: 4px; 319 322 background-color: var(--bg-color); 320 323 padding: 4px 6px; 321 324 border-radius: 12px; ··· 386 389 387 390 .count { 388 391 position: absolute; 389 - right: -4px; 392 + inset-inline-end: -4px; 390 393 top: -4px; 391 394 font-size: 10px; 392 395 background-color: var(--bg-color); ··· 406 409 overflow: hidden; 407 410 text-align: center; 408 411 mask-image: linear-gradient( 409 - to right, 412 + var(--to-forward), 410 413 black calc(100% - 0.5em), 411 414 transparent 100% 412 415 ); ··· 478 481 479 482 > li { 480 483 &:first-child > a { 481 - border-top-left-radius: var(--corner-radius); 482 - border-top-right-radius: var(--corner-radius); 484 + border-start-start-radius: var(--corner-radius); 485 + border-start-end-radius: var(--corner-radius); 483 486 } 484 487 485 488 &:last-child > a { 486 - border-bottom-left-radius: var(--corner-radius); 487 - border-bottom-right-radius: var(--corner-radius); 489 + border-end-start-radius: var(--corner-radius); 490 + border-end-end-radius: var(--corner-radius); 488 491 } 489 492 } 490 493 } ··· 502 505 503 506 @media (min-width: 40em) { 504 507 &.separator + li a { 505 - border-top-left-radius: var(--corner-radius); 506 - border-top-right-radius: var(--corner-radius); 508 + border-start-start-radius: var(--corner-radius); 509 + border-start-end-radius: var(--corner-radius); 507 510 } 508 511 509 512 &:has(+ .separator) a { 510 - border-bottom-left-radius: var(--corner-radius); 511 - border-bottom-right-radius: var(--corner-radius); 513 + border-end-start-radius: var(--corner-radius); 514 + border-end-end-radius: var(--corner-radius); 512 515 } 513 516 } 514 517 ··· 572 575 'author meta' 573 576 'content content'; 574 577 /* align-items: center; */ 578 + --bg-gradient-angle: 140deg; 579 + &:dir(rtl) { 580 + --bg-gradient-angle: -140deg; 581 + } 575 582 background-image: linear-gradient( 576 - 140deg, 583 + var(--bg-gradient-angle), 577 584 var(--post-bg-color), 578 585 transparent min(160px, 50%) 579 586 ); ··· 636 643 } 637 644 638 645 > .avatar ~ .avatar { 639 - margin-left: -8px; 646 + margin-inline-start: -8px; 640 647 } 641 648 642 649 > .icon { ··· 655 662 white-space: nowrap; 656 663 overflow: hidden; 657 664 mask-image: linear-gradient( 658 - to right, 665 + var(--to-forward), 659 666 black calc(100% - 1em), 660 667 transparent 100% 661 668 ); ··· 887 894 &:has(.post-peek-media), 888 895 .post-peek-media:first-child img { 889 896 transform-origin: left center; 897 + :dir(rtl) & { 898 + transform-origin: right center; 899 + } 890 900 } 891 901 } 892 902 893 903 @media (max-width: 480px) { 894 904 .post-peek-media:not(:last-child) { 895 - margin-right: -24px; 905 + margin-inline-end: -24px; 896 906 box-shadow: 0 0 0 2px var(--bg-blur-color); 897 907 } 898 908 /* Max 10, I'm not going to code more than this */
+1
src/pages/filters.jsx
··· 365 365 defaultValue={keyword} 366 366 disabled={uiState === 'loading'} 367 367 required 368 + dir="auto" 368 369 /> 369 370 <div class="filter-keyword-actions"> 370 371 <label>
+3 -2
src/pages/hashtag.jsx
··· 166 166 titleComponent={ 167 167 !!instance && ( 168 168 <h1 class="header-double-lines"> 169 - <b>{hashtagTitle}</b> 169 + <b dir="auto">{hashtagTitle}</b> 170 170 <div>{instance}</div> 171 171 </h1> 172 172 ) ··· 379 379 // no spaces, no hashtags 380 380 pattern="[^#][^\s#]+[^#]" 381 381 disabled={reachLimit} 382 + dir="auto" 382 383 /> 383 384 </form> 384 385 )} ··· 402 403 }} 403 404 > 404 405 <Icon icon="x" alt="Remove hashtag" class="danger-icon" /> 405 - <span> 406 + <span class="bidi-isolate"> 406 407 <span class="more-insignificant">#</span> 407 408 {t} 408 409 </span>
+3 -2
src/pages/login.css
··· 30 30 31 31 #instances-suggestions { 32 32 margin: 0.2em 0 0; 33 - padding: 0 0 0 1.2em; 33 + padding: 0; 34 + padding-inline-start: 1.2em; 34 35 list-style: none; 35 36 width: 90vw; 36 37 max-width: 40em; 37 38 overflow: auto; 38 39 white-space: nowrap; 39 40 mask-image: linear-gradient( 40 - to right, 41 + var(--to-forward), 41 42 transparent, 42 43 black 1.2em, 43 44 black calc(100% - 5em),
+1
src/pages/login.jsx
··· 158 158 onInput={(e) => { 159 159 setInstanceText(e.target.value); 160 160 }} 161 + dir="auto" 161 162 /> 162 163 {instancesSuggestions?.length > 0 ? ( 163 164 <ul id="instances-suggestions">
+9 -6
src/pages/notifications.css
··· 185 185 .notification-group-statuses > li:before { 186 186 content: counter(index); 187 187 position: absolute; 188 - left: 0; 188 + inset-inline-start: 0; 189 189 font-size: 10px; 190 190 padding: 8px; 191 191 font-weight: bold; ··· 194 194 margin-top: -1px; 195 195 } 196 196 .notification-group-statuses > li:not(:last-child) .status-link { 197 - border-bottom-left-radius: 0; 198 - border-bottom-right-radius: 0; 197 + border-end-start-radius: 0; 198 + border-end-end-radius: 0; 199 199 } 200 200 .notification-group-statuses > li:not(:first-child) .status-link { 201 - border-top-left-radius: 0; 202 - border-top-right-radius: 0; 201 + border-start-start-radius: 0; 202 + border-start-end-radius: 0; 203 203 } 204 204 205 205 #mentions-option { 206 206 float: right; 207 + &:dir(rtl) { 208 + float: left; 209 + } 207 210 margin-top: 0.5em; 208 211 } 209 212 #mentions-option label { ··· 388 391 width: calc(100% - 16px); 389 392 } 390 393 .announcements > ul > li:last-child { 391 - border-right: none; 394 + border-inline-end: none; 392 395 } 393 396 .announcements .announcement-block { 394 397 padding: 16px;
+10 -5
src/pages/search.css
··· 48 48 a { 49 49 .icon { 50 50 vertical-align: middle; 51 - transition: transform 0.2s; 51 + transition: margin 0.2s; 52 52 } 53 53 &:hover .icon { 54 - transform: translateX(4px); 54 + margin-inline-start: 4px; 55 55 } 56 56 } 57 57 } ··· 101 101 } 102 102 .search-popover { 103 103 position: absolute; 104 - left: 8px; 104 + inset-inline-start: 8px; 105 105 max-width: calc(100% - 16px); 106 - /* right: 8px; */ 107 106 background-color: var(--bg-color); 108 107 border: 1px solid var(--outline-color); 109 108 box-shadow: 0 4px 24px var(--drop-shadow-color); ··· 118 117 } 119 118 .search-popover-item { 120 119 text-decoration: none; 121 - padding: 8px 16px 8px 8px; 120 + padding: 8px; 121 + padding-inline-end: 16px; 122 122 display: flex; 123 123 gap: 8px; 124 124 align-items: center; ··· 132 132 } 133 133 .search-popover-item:is(:focus, .focus) { 134 134 box-shadow: inset 4px 0 0 0 var(--button-bg-color); 135 + :dir(rtl) & { 136 + box-shadow: inset -4px 0 0 0 var(--button-bg-color); 137 + } 135 138 } 136 139 .search-popover-item :is(mark, q) { 137 140 color: var(--text-color); 138 141 background-color: var(--link-bg-color); 142 + unicode-bidi: isolate; 143 + direction: initial; 139 144 } 140 145 .search-popover-item:is(:hover, :focus, .focus) :is(mark, q) { 141 146 background-color: var(--link-bg-color);
+5 -5
src/pages/settings.css
··· 36 36 border-bottom: var(--hairline-width) solid var(--outline-color); 37 37 } 38 38 #settings-container section > ul > li > div:last-child { 39 - text-align: right; 39 + text-align: end; 40 40 } 41 41 #settings-container section > ul > li .sub-section { 42 - text-align: left !important; 42 + text-align: start !important; 43 43 margin-top: 8px; 44 - margin-left: 24px; 44 + margin-inline-start: 24px; 45 45 } 46 46 #settings-container section > ul > li .sub-section p { 47 47 margin-block: 0.5em; ··· 121 121 grid-template-rows: 1fr 1fr; 122 122 123 123 > span:first-child { 124 - text-align: left; 124 + text-align: start; 125 125 } 126 126 127 127 > span:last-child { 128 - text-align: right; 128 + text-align: end; 129 129 } 130 130 } 131 131 }
+2 -2
src/pages/status.css
··· 11 11 align-self: stretch; 12 12 } 13 13 header h1 .deck-back { 14 - margin-left: -16px; 14 + margin-inline-start: -16px; 15 15 } 16 16 17 17 .button-refresh .icon { ··· 39 39 font-size: 70% !important; 40 40 41 41 & > .avatar ~ .avatar { 42 - margin-left: -4px; 42 + margin-inline-start: -4px; 43 43 } 44 44 } 45 45 .ancestors-indicator:not([hidden]) {
+2
src/pages/status.jsx
··· 1370 1370 const detailsRef = useRef(); 1371 1371 useLayoutEffect(() => { 1372 1372 function handleScroll(e) { 1373 + // NOTE: this scrollLeft works for RTL too 1374 + // Browsers do the magic for us 1373 1375 e.target.dataset.scrollLeft = e.target.scrollLeft; 1374 1376 } 1375 1377 detailsRef.current?.addEventListener('scroll', handleScroll, {
+1 -1
src/pages/trending.jsx
··· 209 209 const total = history.reduce((acc, cur) => acc + +cur.uses, 0); 210 210 return ( 211 211 <Link to={`/${instance}/t/${name}`} key={name}> 212 - <span> 212 + <span dir="auto"> 213 213 <span class="more-insignificant">#</span> 214 214 {name} 215 215 </span>
+4 -3
src/pages/welcome.css
··· 140 140 height: auto; 141 141 max-height: none; 142 142 position: fixed; 143 - left: 0; 143 + inset-inline-start: 0; 144 144 top: 0; 145 145 bottom: 0; 146 146 width: 50%; ··· 153 153 } 154 154 155 155 #why-container { 156 - padding: 32px 32px 32px 8px; 157 - margin-left: 50%; 156 + padding: 32px; 157 + padding-inline-start: 8px; 158 + margin-inline-start: 50%; 158 159 159 160 /* overflow: auto; 160 161 mask-image: linear-gradient(to top, transparent 16px, black 64px); */
+26
src/utils/is-rtl.js
··· 1 + let IS_RTL = false; 2 + 3 + // Use MutationObserver to detect RTL 4 + const observer = new MutationObserver((mutations) => { 5 + mutations.forEach((mutation) => { 6 + if (mutation.type === 'attributes') { 7 + const { value } = mutation.target; 8 + if (value === 'rtl') { 9 + IS_RTL = true; 10 + } else { 11 + IS_RTL = false; 12 + } 13 + // Fire custom event 'dirchange' on document 14 + // document.dispatchEvent(new Event('dirchange')); 15 + } 16 + }); 17 + }); 18 + observer.observe(document.documentElement, { 19 + attributes: true, 20 + attributeFilter: ['dir'], 21 + }); 22 + 23 + export default function isRTL() { 24 + return IS_RTL; 25 + // return document.documentElement.dir === 'rtl'; 26 + }