diff --git a/public/index.css b/public/index.css
index c16d2e5c89200..7cfb9a488983f 100644
--- a/public/index.css
+++ b/public/index.css
@@ -267,7 +267,7 @@ article > section li > :is(p, pre, .code-snippet, blockquote):not(:first-child)
 }
 
 article > section nav :is(ul, ol) {
-	padding-left: inherit;
+	padding-inline-start: inherit;
 }
 
 article > section nav {
@@ -334,7 +334,7 @@ code {
 }
 
 /*RTL Fix Arrows*/
-[dir="rtl"] .toc-mobile-container svg, [dir="rtl"] [rel="next"] svg {
+[dir="rtl"] .toc-mobile-container svg {
     transform: rotateY(180deg);
 }
 
@@ -344,7 +344,7 @@ pre {
 	--padding-block: 1rem;
 	--padding-inline: 2rem;
 	padding: var(--padding-block) var(--padding-inline);
-	padding-right: calc(var(--padding-inline) * 2);
+	padding-inline-end: calc(var(--padding-inline) * 2);
 	margin-left: calc(var(--padding-inline) * -1);
 	margin-right: calc(var(--padding-inline) * -1);
 	font-family: var(--font-mono);
@@ -392,7 +392,7 @@ th {
 td,
 th {
 	padding: 6px;
-	text-align: left;
+	text-align: start;
 }
 
 blockquote code {
@@ -402,7 +402,7 @@ blockquote code {
 blockquote {
 	margin: 2rem 0 2rem 0;
 	padding: 1.25em 1.5rem;
-	border-left: 8px solid var(--theme-divider);
+	border-inline-start: 8px solid var(--theme-divider);
 	background-color: var(--theme-bg-offset);
 	border-radius: 0 0.25rem 0.25rem 0;
 	line-height: 1.7;
@@ -503,6 +503,7 @@ h2.heading {
 	font: inherit;
 	color: var(--theme-text-lighter);
 	text-decoration: none;
+	unicode-bidi: plaintext;
 }
 
 .header-link:hover,
@@ -545,7 +546,7 @@ h2.heading {
 /* Apply different TOC styling for wide viewports showing the right sidebar */
 @media (min-width: 72em) {
 	h2.heading {
-		padding-left: calc(1rem + 4px);
+		padding-inline-start: calc(1rem + 4px);
 	}
 
 	.header-link {
diff --git a/scripts/add-language.mjs b/scripts/add-language.mjs
index 9162837a8809f..122fa91140164 100644
--- a/scripts/add-language.mjs
+++ b/scripts/add-language.mjs
@@ -23,6 +23,8 @@ class LanguageScaffolder {
 	#tag = '';
 	/** Language name (e.g. English, Português do Brasil, etc.) */
 	#name = '';
+	/** Language writing direction (`'ltr' | 'rtl'`) */
+	#dir = '';
 	/** Track whether this instance has made any changes. */
 	#dirty = false;
 
@@ -69,6 +71,15 @@ class LanguageScaffolder {
 				validate: (name) => (name ? true : kleur.reset('[Press Enter to resubmit] ') + kleur.red().italic('Please enter a language name.')),
 				format: (value) => value.trim(),
 			},
+			{
+				type: 'select',
+				name: 'dir',
+				message: 'Writing direction',
+				choices: [
+					{ title: 'Left-to-right', description: '(e.g. English, Russian, etc.)', value: 'ltr' },
+					{ title: 'Right-to-left', description: '(e.g. Arabic, Hebrew, etc.)', value: 'rtl' },
+				],
+			},
 			{
 				type: 'confirm',
 				name: 'confirm',
@@ -77,11 +88,12 @@ class LanguageScaffolder {
 			},
 		];
 
-		const { tag, name, confirm } = await prompts(questions);
+		const { tag, name, dir, confirm } = await prompts(questions);
 		console.log(); // Add newline after questions summary.
 
 		this.#tag = tag;
 		this.#name = name;
+		this.#dir = dir;
 
 		if (!confirm) process.exit(0);
 	}
@@ -136,6 +148,22 @@ class LanguageScaffolder {
 					defaultExport.properties.push(newProperty);
 				}
 			},
+			// Handle the set of RTL languages.
+			ExportNamedDeclaration: (path) => {
+				if (this.#dir !== 'rtl') return;
+
+				const namedExport = path.node.declaration;
+				if (!t.isVariableDeclaration(namedExport)) return;
+
+				const declarator = namedExport.declarations[0];
+				if (declarator.id.name !== 'rtlLanguages') return;
+
+				const langArray = declarator.init.arguments[0];
+				if (!t.isArrayExpression(langArray)) return;
+
+				const langAlreadyInList = langArray.elements.some(({ value }) => value === this.#tag);
+				if (!langAlreadyInList) langArray.elements.push(t.stringLiteral(this.#tag));
+			},
 		});
 
 		if (!langAlreadyInList) {
diff --git a/src/components/Button.astro b/src/components/Button.astro
index 7644056309786..6addcf638efdd 100644
--- a/src/components/Button.astro
+++ b/src/components/Button.astro
@@ -3,16 +3,13 @@ const { class: className = '', style, href } = Astro.props;
 // Wrap in <span> because Houdini is disabled for a[href] for security
 
 const { variant = 'primary' } = Astro.props;
-const { before, after } = Astro.slots;
 ---
 
 
 
-<span class={`link pixel variant-${variant} ${before ? 'has-before' : ''} ${after ? 'has-after' : ''} ${className}`.trim()} {style}>
+<span class:list={[`link pixel variant-${variant}`, className]} {style}>
     <a {href}>
-        <slot name="before" />
         <span><slot /></span>
-        <slot name="after" />
     </a>
 </span>
 
@@ -108,14 +105,6 @@ const { before, after } = Astro.slots;
     .link:active {
         transform: translateY(0);
     }
-    .has-before a :first-child {
-        margin-left: -1rem;
-        margin-right: 0.25rem;
-    }
-    .has-before a :last-child {
-        margin-left: 0.25rem;
-        margin-right: -1rem;
-    }
     a {
         display: flex;
         align-items: center;
@@ -136,7 +125,7 @@ const { before, after } = Astro.slots;
 		}
 
     a > :global(* + *) {
-        margin-left: 0.25rem;
+        margin-inline-start: 0.25rem;
     }
 
     .variant-primary {
diff --git a/src/components/DeployGuidesNav.astro b/src/components/DeployGuidesNav.astro
index 66aa4220b07ff..903b24b390bb5 100644
--- a/src/components/DeployGuidesNav.astro
+++ b/src/components/DeployGuidesNav.astro
@@ -79,6 +79,7 @@ const services: Service[] = [
 		user-select: none;
 		font-weight: bold;
 		cursor: pointer;
+		unicode-bidi: plaintext;
 	}
 
 	.filter-text {
diff --git a/src/components/LeftSidebar/LeftSidebar.astro b/src/components/LeftSidebar/LeftSidebar.astro
index cac5d1c7805ca..00954e0df98d8 100644
--- a/src/components/LeftSidebar/LeftSidebar.astro
+++ b/src/components/LeftSidebar/LeftSidebar.astro
@@ -82,7 +82,6 @@ for (const section of sidebarSections) {
 			position: fixed;
 			top: calc(var(--theme-navbar-height) + 3rem);
 			bottom: 0;
-			right: unset;
 			width: calc(var(--theme-left-sidebar-width) - var(--min-spacing-inline) * 1.6);
 		}
 	}
diff --git a/src/components/LeftSidebar/SidebarContent.astro b/src/components/LeftSidebar/SidebarContent.astro
index 3aa69586ca23d..4be0e9d87d2f4 100644
--- a/src/components/LeftSidebar/SidebarContent.astro
+++ b/src/components/LeftSidebar/SidebarContent.astro
@@ -165,6 +165,10 @@ const lang = getLanguageFromURL(Astro.url.pathname);
 		vertical-align: middle;
 	}
 
+	:global([dir="rtl"]) svg {
+		transform: rotate(180deg);
+	}
+
 	svg path {
 		fill: currentColor;
 	}
diff --git a/src/components/LeftSidebar/Sponsors.astro b/src/components/LeftSidebar/Sponsors.astro
index 3943b385f86e3..b578cbec6be16 100644
--- a/src/components/LeftSidebar/Sponsors.astro
+++ b/src/components/LeftSidebar/Sponsors.astro
@@ -36,11 +36,6 @@
 	}
 	.sponsor {
 		margin-bottom: -0.375rem;
-		/* display: grid;
-		padding-left: 1rem;
-		padding: 0.25rem;
-		grid-gap: 0.5rem;
-		grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); */
 	}
 	svg {
 		color: var(--theme-text);
diff --git a/src/components/PageContent/ArticleNavigationButton.astro b/src/components/PageContent/ArticleNavigationButton.astro
index 83cab95e2027d..635ab32843a83 100644
--- a/src/components/PageContent/ArticleNavigationButton.astro
+++ b/src/components/PageContent/ArticleNavigationButton.astro
@@ -59,4 +59,7 @@ const { item, rel } = Astro.props as Props;
 		font-weight: bold;
 		color: var(--theme-text);
 	}
+	:global([dir="rtl"]) svg {
+		transform: rotateY(180deg);
+	}
 </style>
diff --git a/src/components/PageContent/PageContent.astro b/src/components/PageContent/PageContent.astro
index 79bb4ef0d9b97..14d32d99d04de 100644
--- a/src/components/PageContent/PageContent.astro
+++ b/src/components/PageContent/PageContent.astro
@@ -4,7 +4,13 @@ import TableOfContents from '../RightSidebar/TableOfContents';
 import { getNav, useTranslations } from "../../i18n/util";
 import { getLanguageFromURL } from '../../util';
 
-const { content, currentPage } = Astro.props;
+export interface Props {
+	content: Record<string, any>;
+	currentPage: string;
+	layoutDir: 'ltr' | 'rtl';
+}
+
+const { content, currentPage, layoutDir } = Astro.props as Props;
 // We wrap `@astrojs/` in a span to style it separately on integration pages.
 const title = content.title.replace('@astrojs/', '<span class="scope">@astrojs/</span>');
 const headings = content.astro?.headings;
@@ -23,7 +29,7 @@ const t = useTranslations(Astro);
 	// inside elements that hide any overflow axis. The article content hides `overflow-x`,
 	// so we must place the mobile TOC here.
 	headings && (
-		<nav class="mobile-toc">
+		<nav class="mobile-toc" dir={layoutDir}>
 			<TableOfContents
 				client:media="(max-width: 72em)"
 				headings={headings}
@@ -78,14 +84,13 @@ const t = useTranslations(Astro);
 	.mobile-toc {
 		display: block;
 		position: fixed;
-		left: 0;
+		inset-inline: 0;
 		top: calc(var(--theme-navbar-height));
-		right: 0;
 		z-index: 2;
 	}
 	@media (min-width: 50em) {
 		.mobile-toc {
-			left: var(--theme-left-sidebar-width);
+			inset-inline-start: var(--theme-left-sidebar-width);
 			margin-top: 0;
 		}
 	}
diff --git a/src/components/RightSidebar/CommunityMenu.astro b/src/components/RightSidebar/CommunityMenu.astro
index 8d6fd7c368938..9d16b515cbadc 100644
--- a/src/components/RightSidebar/CommunityMenu.astro
+++ b/src/components/RightSidebar/CommunityMenu.astro
@@ -20,7 +20,7 @@ const HeadingWrapper = hideOnLargerScreens
 	<HeadingWrapper>
 		<h2 class="heading"><UIString key="rightSidebar.community" /></h2>
 		{ hideOnLargerScreens &&
-		<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 1 16 16" width="16" height="16" aria-hidden="true">
+		<svg class="chevron" xmlns="http://www.w3.org/2000/svg" viewBox="0 1 16 16" width="16" height="16" aria-hidden="true">
 			<path fill-rule="evenodd"
 				d="M6.22 3.22a.75.75 0 011.06 0l4.25 4.25a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06-1.06L9.94 8 6.22 4.28a.75.75 0 010-1.06z">
 			</path>
@@ -78,16 +78,16 @@ const HeadingWrapper = hideOnLargerScreens
 
 <style>
 	h2, ul {
-		padding-left: 2rem;
+		padding-inline-start: 2rem;
 	}
 
 	@media (min-width: 50em) {
 		h2 {
-			padding-left: 1rem;
+			padding-inline-start: 1rem;
 		}
 
 		ul {
-			padding-left: 0;
+			padding-inline-start: 0;
 		}
 	}
 
@@ -97,7 +97,7 @@ const HeadingWrapper = hideOnLargerScreens
 		}
 
 		h2 {
-			padding-left: calc(1rem + 4px);
+			padding-inline-start: calc(1rem + 4px);
 		}
 	}
 
@@ -114,7 +114,7 @@ const HeadingWrapper = hideOnLargerScreens
 		display: none;
 	}
 
-	details[open] > summary svg {
+	details[open] > summary .chevron {
 		transform: rotate(90deg);
 	}
 
@@ -128,12 +128,16 @@ const HeadingWrapper = hideOnLargerScreens
 		display: inline;
 	}
 
-	svg {
+	.chevron {
 		transform: rotate(0);
 		transition: .15s transform ease;
 		vertical-align: middle;
 	}
 
+	:global([dir="rtl"]) .chevron {
+		transform: rotate(180deg);
+	}
+
 	svg path {
 		fill: currentColor;
 	}
diff --git a/src/components/RightSidebar/TableOfContents.css b/src/components/RightSidebar/TableOfContents.css
index 50c54134cc3ae..4df99d48e92f3 100644
--- a/src/components/RightSidebar/TableOfContents.css
+++ b/src/components/RightSidebar/TableOfContents.css
@@ -49,13 +49,19 @@
 	margin-inline-end: 0.5rem;
 	border-radius: 0.5rem;
 	border: 1px solid var(--theme-shade-subtle);
-	padding: 0.25rem 0.5rem 0.25rem 0.75rem;
+	padding: 0.25rem 0.75rem;
+	padding-inline-end: 0.5rem;
+}
+
+.toc-toggle svg {
+	margin-inline-start: .25rem;
 }
 
 .toc-current-heading {
 	text-overflow: ellipsis;
 	overflow: hidden;
 	color: var(--theme-text-light);
+	unicode-bidi: plaintext;
 }
 
 .toc-mobile-container[open] .toc-toggle {
@@ -68,7 +74,7 @@
 }
 
 .toc-mobile-header span {
-	margin-left: 0.2rem;
+	margin-inline-start: 0.2rem;
 }
 
 .toc-mobile-header svg {
diff --git a/src/i18n/languages.ts b/src/i18n/languages.ts
index 8fceecee14d23..37280ba3b12d1 100644
--- a/src/i18n/languages.ts
+++ b/src/i18n/languages.ts
@@ -14,3 +14,5 @@ export default {
 	ja: '日本語',
 	ru: 'Русский',
 };
+
+export const rtlLanguages = new Set(['ar']);
diff --git a/src/layouts/MainLayout.astro b/src/layouts/MainLayout.astro
index 960a7acba61b2..ea8db1414aa14 100644
--- a/src/layouts/MainLayout.astro
+++ b/src/layouts/MainLayout.astro
@@ -9,11 +9,13 @@ import RightSidebar from '../components/RightSidebar/RightSidebar.astro';
 import { getLanguageFromURL } from '../util';
 import { normalizeLangTag } from '../i18n/bcp-normalize';
 import { useTranslations } from '../i18n/util';
+import { rtlLanguages } from '../i18n/languages';
 
 const { content = {}, hideRightSidebar = false } = Astro.props;
 const isFallback = !!Astro.params.fallback;
 const url = new URL(Astro.request.url);
 const lang = getLanguageFromURL(url.pathname);
+const direction = rtlLanguages.has(lang) ? 'rtl' : 'ltr';
 const bcpLang = normalizeLangTag(lang);
 const currentPage = url.pathname;
 const filePath = `src/pages${currentPage.replace(/\/$/, '')}.md`;
@@ -28,7 +30,7 @@ const canonicalURL = new URL(Astro.url.pathname.replace(/([^/])$/, '$1/'), Astro
 if (isFallback) canonicalURL.pathname = canonicalURL.pathname.replace(`/${lang}/`, '/en/');
 ---
 
-<html dir={content.dir ?? 'ltr'} lang={bcpLang} class="initial">
+<html dir={direction} lang={bcpLang} class="initial">
 	<head>
 		<HeadCommon />
 		<HeadSEO {content} {canonicalURL} />
@@ -44,12 +46,12 @@ if (isFallback) canonicalURL.pathname = canonicalURL.pathname.replace(`/${lang}/
 				display: none;
 				background: var(--theme-bg-gradient);
 				z-index: 10;
-				left: 0;
+				inset-inline-start: 0;
 			}
 			#right-sidebar {
 				display: none;
 				top: var(--theme-navbar-height);
-				right: 0;
+				inset-inline-end: 0;
 				width: var(--theme-right-sidebar-width);
 			}
 			#main-content {
@@ -65,7 +67,7 @@ if (isFallback) canonicalURL.pathname = canonicalURL.pathname.replace(`/${lang}/
 				:global(.mobile-sidebar-toggle #left-sidebar) {
 					display: block;
 					top: var(--theme-navbar-height);
-					right: 0;
+					inset-inline-end: 0;
 				}
 				/*
 					Try to prevent the rest of the page from scrolling,
@@ -89,7 +91,7 @@ if (isFallback) canonicalURL.pathname = canonicalURL.pathname.replace(`/${lang}/
 
 			@media (min-width: 50em) {
 				#main-content {
-					margin-left: var(--theme-left-sidebar-width);
+					margin-inline-start: var(--theme-left-sidebar-width);
 				}
 				#left-sidebar {
 					display: flex;
@@ -103,7 +105,7 @@ if (isFallback) canonicalURL.pathname = canonicalURL.pathname.replace(`/${lang}/
 
 			@media (min-width: 72em) {
 				#main-content {
-					margin-right: var(--theme-right-sidebar-width);
+					margin-inline-end: var(--theme-right-sidebar-width);
 				}
 				#right-sidebar {
 					display: flex;
@@ -140,11 +142,13 @@ if (isFallback) canonicalURL.pathname = canonicalURL.pathname.replace(`/${lang}/
 				{!hideRightSidebar && <RightSidebar content={content} githubEditUrl={githubEditUrl} />}
 			</aside>
 			<div id="main-content" lang={isFallback && 'en'}>
-				<PageContent {content} {githubEditUrl} {currentPage}>
-					<slot name="header" />
-					{isFallback && <FallbackNotice />}
-					<slot />
-				</PageContent>
+				<div dir={isFallback ? 'ltr' : direction}>
+					<PageContent {content} {githubEditUrl} {currentPage} layoutDir={direction}>
+						<slot name="header" />
+						{isFallback && <div lang={bcpLang} dir={direction}><FallbackNotice /></div>}
+						<slot />
+					</PageContent>
+				</div>
 			</div>
 		</main>
 	</body>
diff --git a/src/pages/ar/editor-setup.md b/src/pages/ar/editor-setup.md
index 020c7c683a33c..751bd9c21497f 100644
--- a/src/pages/ar/editor-setup.md
+++ b/src/pages/ar/editor-setup.md
@@ -4,7 +4,6 @@ setup: |
   import Badge from '~/components/Badge.astro';
 title: إعداد البيئة البرمجية
 description: أعِد محرر الشفرة لبناء المشاريع مع Astro.
-dir: rtl
 ---
 
 خصص محرر الكود لتحسين تجربة التطوير مع أسترو وفتح ميزات جديدة
diff --git a/src/pages/ar/getting-started.md b/src/pages/ar/getting-started.md
index c16c0989b74ad..e80c218368fbe 100644
--- a/src/pages/ar/getting-started.md
+++ b/src/pages/ar/getting-started.md
@@ -5,7 +5,6 @@ setup: |
     import ContributorList from '../../components/ContributorList.astro'
     import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
 title: باشر بالبدأ
-dir: rtl
 ---
 #### ما هو أسترو؟