diff --git a/src/components/AdjustableSidebarWidth.vue b/src/components/AdjustableSidebarWidth.vue index 61a0a4ef5..02dd90209 100644 --- a/src/components/AdjustableSidebarWidth.vue +++ b/src/components/AdjustableSidebarWidth.vue @@ -461,11 +461,10 @@ export default { position: fixed; top: var(--top-offset-mobile); bottom: 0; - left: 0; + inset-inline-start: 0; z-index: $nav-z-index + 1; transform: translateX(-100%); transition: transform var(--nav-transition-duration) ease-in; - left: 0; :deep(.aside-animated-child) { opacity: 0; @@ -502,7 +501,7 @@ export default { cursor: col-resize; top: 0; bottom: 0; - right: 0; + inset-inline-end: 0; width: 5px; height: 100%; user-select: none; diff --git a/src/components/Badge.vue b/src/components/Badge.vue index 623504e0d..4635b3606 100644 --- a/src/components/Badge.vue +++ b/src/components/Badge.vue @@ -59,7 +59,7 @@ export default { border-style: var(--badge-border-style, none); border-width: var(--badge-border-width, 1px); margin: auto; - margin-left: 5px; + margin-inline-start: 5px; color: var(--colors-badge-text, var(--color-badge-text)); background-color: var(--badge-color); @include prefers-dark { diff --git a/src/components/CodeBlock.vue b/src/components/CodeBlock.vue index 2b2dd23e7..159633ff8 100644 --- a/src/components/CodeBlock.vue +++ b/src/components/CodeBlock.vue @@ -29,6 +29,10 @@ export default { @import 'docc-render/styles/_core.scss'; code { + /* enforce "ltr" direction for text in code blocks right now since code is + likely to remain as English and not translated */ + direction: ltr; + &::before { content: attr(data-before-code); } diff --git a/src/components/ContentNode/Aside.vue b/src/components/ContentNode/Aside.vue index 46f968071..f9b9ffec6 100644 --- a/src/components/ContentNode/Aside.vue +++ b/src/components/ContentNode/Aside.vue @@ -53,11 +53,13 @@ aside { break-inside: avoid; border-radius: var(--aside-border-radius, $border-radius); border-style: var(--aside-border-style, solid); - border-width: var(--aside-border-width, + /*border-width: var(--aside-border-width, $aside-width-border $aside-width-border $aside-width-border - $aside-width-left-border); + $aside-width-left-border);*/ + border-block-width: $aside-width-border; + border-inline-width: $aside-width-left-border 0; padding: rem(16px); text-align: start; diff --git a/src/components/ContentNode/LinkableHeading.vue b/src/components/ContentNode/LinkableHeading.vue index 1a1352419..25945ff9c 100644 --- a/src/components/ContentNode/LinkableHeading.vue +++ b/src/components/ContentNode/LinkableHeading.vue @@ -78,7 +78,7 @@ $icon-margin: 7px; color: inherit; text-decoration: none; position: relative; - padding-right: $icon-size-default + $icon-margin; + padding-inline-end: $icon-size-default + $icon-margin; display: inline-block; &::after { @@ -88,11 +88,11 @@ $icon-margin: 7px; .icon { position: absolute; - right: 0; + inset-inline-end: 0; bottom: .2em; display: none; height: $icon-size-default; - margin-left: $icon-margin; + margin-inline-start: $icon-margin; } &:hover, &:focus { diff --git a/src/components/ContentNode/TabNavigator.vue b/src/components/ContentNode/TabNavigator.vue index 06b285580..f703f4415 100644 --- a/src/components/ContentNode/TabNavigator.vue +++ b/src/components/ContentNode/TabNavigator.vue @@ -130,9 +130,9 @@ export default { .tabs-content { flex: 1 1 auto; min-width: 0; - padding-right: var(--spacing-stacked-margin-xlarge); + padding-inline-end: var(--spacing-stacked-margin-xlarge); @include breakpoint(small) { - padding-right: 0; + padding-inline-end: 0; padding-bottom: var(--spacing-stacked-margin-large); } } diff --git a/src/components/DocumentationLayout.vue b/src/components/DocumentationLayout.vue index 6f2fdf9a9..b3eb520e2 100644 --- a/src/components/DocumentationLayout.vue +++ b/src/components/DocumentationLayout.vue @@ -248,7 +248,7 @@ export default { } .navigator-filter .quick-navigation-open { - margin-left: var(--nav-filter-horizontal-padding); + margin-inline-start: var(--nav-filter-horizontal-padding); width: calc(var(--nav-filter-horizontal-padding) * 2); } } @@ -268,14 +268,14 @@ export default { .documentation-layout-aside { height: 100%; box-sizing: border-box; - border-right: $generic-border-style; + border-inline-end: $generic-border-style; @include breakpoint(medium, nav) { background: var(--color-fill); - border-right: none; + border-inline-end: none; .sidebar-transitioning & { - border-right: $generic-border-style; + border-inline-end: $generic-border-style; } } } @@ -293,8 +293,7 @@ export default { @include inTargetWeb { @include breakpoint-full-width-container(); @include breakpoints-from(xlarge) { - border-left: $generic-border-style; - border-right: $generic-border-style; + border-inline: $generic-border-style; box-sizing: border-box; } } diff --git a/src/components/DocumentationTopic.vue b/src/components/DocumentationTopic.vue index 888d8018e..23c110f9d 100644 --- a/src/components/DocumentationTopic.vue +++ b/src/components/DocumentationTopic.vue @@ -816,7 +816,7 @@ $space-size: 15px; small { font-size: 1rem; - padding-left: 0.416rem; + padding-inline-start: 0.416rem; } } diff --git a/src/components/DocumentationTopic/DecoratedTopicTitle.vue b/src/components/DocumentationTopic/DecoratedTopicTitle.vue index f2a3be6e1..faa853a51 100644 --- a/src/components/DocumentationTopic/DecoratedTopicTitle.vue +++ b/src/components/DocumentationTopic/DecoratedTopicTitle.vue @@ -75,6 +75,12 @@ export default { diff --git a/src/components/DocumentationTopic/PrimaryContent/Parameters.vue b/src/components/DocumentationTopic/PrimaryContent/Parameters.vue index 6528e93b9..b3e542757 100644 --- a/src/components/DocumentationTopic/PrimaryContent/Parameters.vue +++ b/src/components/DocumentationTopic/PrimaryContent/Parameters.vue @@ -55,7 +55,7 @@ export default { .param-name { font-weight: $font-weight-semibold; - padding-left: 1rem; + padding-inline-start: 1rem; padding-top: var(--spacing-param); &:first-child { @@ -63,15 +63,15 @@ export default { } @include breakpoint(small) { - padding-left: 0; + padding-inline-start: 0; } } .param-content { - padding-left: 2rem; + padding-inline-start: 2rem; @include breakpoint(small) { - padding-left: 0; + padding-inline-start: 0; } :deep(dt) { @@ -79,7 +79,7 @@ export default { } :deep(dd) { - margin-left: 1em; + margin-inline-start: 1em; } } diff --git a/src/components/DocumentationTopic/RelationshipsList.vue b/src/components/DocumentationTopic/RelationshipsList.vue index c42c2bab1..a0f006a44 100644 --- a/src/components/DocumentationTopic/RelationshipsList.vue +++ b/src/components/DocumentationTopic/RelationshipsList.vue @@ -131,7 +131,7 @@ export default { list-style: none; &.column { - margin-left: 0; + margin-inline-start: 0; margin-top: 15px; } @@ -144,7 +144,7 @@ export default { flex-direction: row; flex-wrap: wrap; margin-top: 15px; - margin-left: 0; + margin-inline-start: 0; li:not(:last-child)::after { content: ",\00a0" diff --git a/src/components/DocumentationTopic/Summary/Availability.vue b/src/components/DocumentationTopic/Summary/Availability.vue index 8fe18ec59..1f320bbe2 100644 --- a/src/components/DocumentationTopic/Summary/Availability.vue +++ b/src/components/DocumentationTopic/Summary/Availability.vue @@ -113,7 +113,7 @@ $availability-info-spacing: 10px; .changed { $-coin-spacer: 5px; - padding-left: $icon-size-default - $-coin-spacer + 2; + padding-inline-start: $icon-size-default - $-coin-spacer + 2; border: none; &::after { @@ -124,7 +124,7 @@ $availability-info-spacing: 10px; &::before { @include coin($modified-svg, $icon-size-default); margin: 0; - left: -$-coin-spacer; + inset-inline-start: -$-coin-spacer; @include prefers-dark { background-image: $modified-dark-svg; @@ -162,7 +162,7 @@ $availability-info-spacing: 10px; width: 1px; height: 1em; background: currentColor; - margin-left: $availability-info-spacing; + margin-inline-start: $availability-info-spacing; } &:last-child::after { diff --git a/src/components/DocumentationTopic/TopicLinkBlockIcon.vue b/src/components/DocumentationTopic/TopicLinkBlockIcon.vue index 5cfa7df53..eef0b3a9f 100644 --- a/src/components/DocumentationTopic/TopicLinkBlockIcon.vue +++ b/src/components/DocumentationTopic/TopicLinkBlockIcon.vue @@ -64,7 +64,7 @@ export default { height: rem(25px); flex: 0 0 $topic-link-icon-width; width: $topic-link-icon-width; - margin-right: $topic-link-icon-spacing; + margin-inline-end: $topic-link-icon-spacing; } .topic-icon { diff --git a/src/components/DocumentationTopic/TopicsLinkBlock.vue b/src/components/DocumentationTopic/TopicsLinkBlock.vue index f790a6e91..d9e4b4b84 100644 --- a/src/components/DocumentationTopic/TopicsLinkBlock.vue +++ b/src/components/DocumentationTopic/TopicsLinkBlock.vue @@ -216,11 +216,11 @@ export default { .abstract, .link-block :deep(.badge) { - margin-left: calc(#{$topic-link-icon-spacing} + #{$topic-link-icon-width}); + margin-inline-start: calc(#{$topic-link-icon-spacing} + #{$topic-link-icon-width}); } .link-block .badge + .badge { - margin-left: 1rem; + margin-inline-start: 1rem; } .link { @@ -238,7 +238,7 @@ export default { flex-flow: row wrap; .badge { - margin-left: 1rem; + margin-inline-start: 1rem; margin-top: 0; } } diff --git a/src/components/Filter/FilterInput.vue b/src/components/Filter/FilterInput.vue index dcdf72334..9c0614500 100644 --- a/src/components/Filter/FilterInput.vue +++ b/src/components/Filter/FilterInput.vue @@ -456,11 +456,10 @@ $input-height: rem(28px); position: relative; z-index: 1; cursor: text; - margin-left: var(--input-horizontal-spacing); - margin-right: rem(3px); + margin-inline: var(--input-horizontal-spacing) rem(3px); @include breakpoint(small) { - margin-right: rem(7px); + margin-inline-end: rem(7px); } .svg-icon { @@ -522,11 +521,11 @@ $input-height: rem(28px); &__selected-tags { z-index: 1; - padding-left: $tag-outline-padding; + padding-inline-start: $tag-outline-padding; margin: -$tag-outline-padding 0; @include breakpoint(small) { - padding-left: 0; + padding-inline-start: 0; } :deep() { @@ -534,11 +533,11 @@ $input-height: rem(28px); padding: $tag-outline-padding; @include breakpoint(small) { - padding-right: rem(7px); + padding-inline-end: rem(7px); } .tag:last-child { - padding-right: 0; + padding-inline-end: 0; } } } @@ -562,8 +561,7 @@ $input-height: rem(28px); &__delete-button-wrapper { display: flex; align-items: center; - padding-right: var(--input-horizontal-spacing); - padding-left: rem(3px); + padding-inline: rem(3px) var(--input-horizontal-spacing); border-top-right-radius: $small-border-radius; border-bottom-right-radius: $small-border-radius; } diff --git a/src/components/Icons/InlineChevronRightIcon.vue b/src/components/Icons/InlineChevronRightIcon.vue index 8e0d9846c..9f4d09198 100644 --- a/src/components/Icons/InlineChevronRightIcon.vue +++ b/src/components/Icons/InlineChevronRightIcon.vue @@ -22,3 +22,9 @@ export default { components: { SVGIcon }, }; + + diff --git a/src/components/Icons/SidenavIcon.vue b/src/components/Icons/SidenavIcon.vue index cc0121c56..79ef28c5e 100644 --- a/src/components/Icons/SidenavIcon.vue +++ b/src/components/Icons/SidenavIcon.vue @@ -24,3 +24,9 @@ export default { components: { SVGIcon }, }; + + diff --git a/src/components/Icons/TwoLetterSymbolIcon.vue b/src/components/Icons/TwoLetterSymbolIcon.vue index bdf63028c..7a5a137de 100644 --- a/src/components/Icons/TwoLetterSymbolIcon.vue +++ b/src/components/Icons/TwoLetterSymbolIcon.vue @@ -46,3 +46,12 @@ export default { }, }; + + diff --git a/src/components/NavMenuItemBase.vue b/src/components/NavMenuItemBase.vue index 5a8d94ddb..c0ea398f1 100644 --- a/src/components/NavMenuItemBase.vue +++ b/src/components/NavMenuItemBase.vue @@ -36,12 +36,12 @@ export default { @import "docc-render/styles/_core.scss"; .nav-menu-item { - margin-left: $nav-menu-item-left-margin; + margin-inline-start: $nav-menu-item-left-margin; list-style: none; min-width: 0; @include nav-in-breakpoint { - margin-left: 0; + margin-inline-start: 0; width: 100%; min-height: rem(42px); // remove the first border of the first element diff --git a/src/components/Navigator/BaseNavigatorCard.vue b/src/components/Navigator/BaseNavigatorCard.vue index 9ef7bdd0c..cf573acce 100644 --- a/src/components/Navigator/BaseNavigatorCard.vue +++ b/src/components/Navigator/BaseNavigatorCard.vue @@ -134,7 +134,7 @@ $close-icon-padding: 5px; display: flex; flex-direction: column; // right padding is added by the items, so visually the scroller is stuck to the side - padding-right: 0; + padding-inline-end: 0; flex: 1 1 auto; min-height: 0; height: 100%; diff --git a/src/components/Navigator/BaseNavigatorCardItem.vue b/src/components/Navigator/BaseNavigatorCardItem.vue index efe29b47f..a45102571 100644 --- a/src/components/Navigator/BaseNavigatorCardItem.vue +++ b/src/components/Navigator/BaseNavigatorCardItem.vue @@ -67,7 +67,9 @@ $nesting-spacing: $nav-card-horizontal-spacing + $nav-card-horizontal-spacing-sm align-items: stretch; min-height: $item-height; box-sizing: border-box; - padding: 0 var(--nav-head-wrapper-right-space) 0 var(--nav-head-wrapper-left-space); + padding-block: 0; + padding-inline-start: var(--nav-head-wrapper-left-space); + padding-inline-end: var(--nav-head-wrapper-right-space); &.active { .head-wrapper { @@ -99,7 +101,7 @@ $nesting-spacing: $nav-card-horizontal-spacing + $nav-card-horizontal-spacing-sm } .navigator-icon-wrapper { - margin-right: 7px; + margin-inline-end: 7px; } .head-wrapper { @@ -119,3 +121,13 @@ $nesting-spacing: $nav-card-horizontal-spacing + $nav-card-horizontal-spacing-sm @include safe-area-right-set(padding-right, var(--nav-head-wrapper-right-space)); } + + diff --git a/src/components/Navigator/NavigatorCard.vue b/src/components/Navigator/NavigatorCard.vue index 5b4199382..04df6767d 100644 --- a/src/components/Navigator/NavigatorCard.vue +++ b/src/components/Navigator/NavigatorCard.vue @@ -1020,7 +1020,7 @@ $navigator-card-vertical-spacing: 8px !default; @include safe-area-left-set(margin-left, var(--card-horizontal-spacing)); @include safe-area-right-set(margin-right, var(--card-horizontal-spacing)); padding: $navigator-card-vertical-spacing $nav-card-horizontal-spacing; - padding-left: $nav-card-horizontal-spacing * 2; + padding-inline-start: $nav-card-horizontal-spacing * 2; background: $technology-title-background; border-radius: $nano-border-radius; display: flex; diff --git a/src/components/Navigator/NavigatorCardItem.vue b/src/components/Navigator/NavigatorCardItem.vue index c7c950692..73a6b0ba1 100644 --- a/src/components/Navigator/NavigatorCardItem.vue +++ b/src/components/Navigator/NavigatorCardItem.vue @@ -366,7 +366,7 @@ $chevron-width: $nav-card-horizontal-spacing; position: absolute; width: 100%; height: 100%; - padding-right: $tree-toggle-padding; + padding-inline-end: $tree-toggle-padding; box-sizing: border-box; z-index: 1; display: flex; diff --git a/src/components/Navigator/QuickNavigationModal.vue b/src/components/Navigator/QuickNavigationModal.vue index 0fd0c90c4..7b9a8be81 100644 --- a/src/components/Navigator/QuickNavigationModal.vue +++ b/src/components/Navigator/QuickNavigationModal.vue @@ -531,7 +531,7 @@ $input-horizontal-spacing: rem(15px); overflow: auto; } &__preview { - border-left: $base-border-width solid var(--color-grid); + border-inline-start: $base-border-width solid var(--color-grid); flex: 0 0 61.8%; overflow: auto; position: sticky; @@ -560,7 +560,7 @@ $input-horizontal-spacing: rem(15px); margin: auto; width: 100%; .navigator-icon { - margin-right: rem(10px); + margin-inline-end: rem(10px); } .symbol-name { display: flex; @@ -572,11 +572,11 @@ $input-horizontal-spacing: rem(15px); @include font-styles(body-reduced-tight); color: var(--color-figure-gray-secondary); display: flex; - margin-left: rem(27px); + margin-inline-start: rem(27px); overflow: hidden; white-space: nowrap; .parent-path { - padding-right: rem(5px); + padding-inline-end: rem(5px); } } } diff --git a/src/components/TabnavItem.vue b/src/components/TabnavItem.vue index 394e1e3bb..35666fa1f 100644 --- a/src/components/TabnavItem.vue +++ b/src/components/TabnavItem.vue @@ -58,12 +58,12 @@ $tabnav-item-gutter: rem(30px); display: flex; list-style: none; - padding-left: $tabnav-item-gutter; + padding-inline-start: $tabnav-item-gutter; margin: 0; outline: none; &:first-child { - padding-left: 0; + padding-inline-start: 0; } // hack to make sure item margin is not overwritten by external css @@ -97,7 +97,7 @@ $tabnav-item-gutter: rem(30px); content: ''; position: absolute; bottom: -1 * ($tabnav-margin + 1); - left: 0; + inset-inline-start: 0; width: 100%; border: 1px solid transparent; } diff --git a/src/lang/index.js b/src/lang/index.js index 0eb5a1122..fa1429769 100644 --- a/src/lang/index.js +++ b/src/lang/index.js @@ -9,15 +9,17 @@ */ /* eslint-disable camelcase */ +import ar from './locales/ar.json'; import en_US from './locales/en-US.json'; import zh_CN from './locales/zh-CN.json'; import ja_JP from './locales/ja-JP.json'; import ko_KR from './locales/ko-KR.json'; // default locale -export const defaultLocale = 'en-US'; +export const defaultLocale = process.env.VUE_APP_DEFAULT_LOCALE ?? 'en-US'; // translated locales export const messages = { + ar, 'en-US': en_US, 'zh-CN': zh_CN, 'ja-JP': ja_JP, diff --git a/src/lang/locales.json b/src/lang/locales.json index 4c340feb7..079525d84 100644 --- a/src/lang/locales.json +++ b/src/lang/locales.json @@ -1,4 +1,9 @@ [ + { + "code": "ar", + "name": "Arabic", + "slug": "ar" + }, { "code": "en-US", "name": "English", diff --git a/src/lang/locales/ar.json b/src/lang/locales/ar.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/src/lang/locales/ar.json @@ -0,0 +1 @@ +{} diff --git a/src/styles/_base.scss b/src/styles/_base.scss index ec8e3b97e..f07b584d0 100644 --- a/src/styles/_base.scss +++ b/src/styles/_base.scss @@ -28,3 +28,11 @@ :root { --app-height: 100vh; } + +html { + --scale-inline: 1; +} + +html[dir="rtl"] { + --scale-inline: -1; +} diff --git a/src/styles/base/_reset.scss b/src/styles/base/_reset.scss index e5589cccb..4e6c5d44b 100644 --- a/src/styles/base/_reset.scss +++ b/src/styles/base/_reset.scss @@ -80,7 +80,7 @@ img { //============================================================ caption, th { - text-align: left; + text-align: start; } //============================================================ diff --git a/src/styles/base/_typography.scss b/src/styles/base/_typography.scss index b33d872e5..53a9e8d01 100644 --- a/src/styles/base/_typography.scss +++ b/src/styles/base/_typography.scss @@ -63,8 +63,7 @@ button { -moz-font-feature-settings: 'kern'; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - direction: ltr; - text-align: left; + text-align: start; } // @@ -105,7 +104,7 @@ ol { // ul, ol { - margin-left: em(20px); + margin-inline-start: em(20px); // remove the top margin when nesting lists ul, diff --git a/src/utils/metadata.js b/src/utils/metadata.js index 2b8aeb3b7..b318ab563 100644 --- a/src/utils/metadata.js +++ b/src/utils/metadata.js @@ -122,10 +122,39 @@ export function addOrUpdateMetadata({ ); } +// these are hardcoded constants needed for manually determining the +// directionality of locales in Firefox +// +// this is very incomplete and will need to be manually updated to support other +// rtl languages until the `getTextInfo().direction` API is supported in FF — +// for now, this is just a basic set of example rtl languages +const RtlLocales = new Set([ + 'ar', // Arabic + 'he', // Hebrew + 'ur', // Urdu +]); + +const Direction = { + ltr: 'ltf', + rtl: 'rtl', +}; + +function getDirection(localeName) { + const locale = new Intl.Locale(localeName); + if ((typeof locale.getTextInfo) === 'function') { + return locale.getTextInfo()?.direction ?? Direction.ltr; + } + + // only needed for Firefox, which doesn't support `Intl.Locale.getTextInfo` + return RtlLocales.has(localeName) ? Direction.rtl : Direction.ltr; +} + /** * It updates the document setting a new lang attribute with the iso code or fallback on the locale * @param {String} locale */ export function updateLangTag(locale) { - document.querySelector('html').setAttribute('lang', locale); + const htmlElement = document.querySelector('html'); + htmlElement.setAttribute('lang', locale); + htmlElement.setAttribute('dir', getDirection(locale)); }