diff --git a/AUTHORS.rst b/AUTHORS.rst index f57795d4fa7..5a8bd3cd689 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -24,6 +24,7 @@ Contributors * Alastair Houghton -- Apple Help builder * Alex Gaynor -- linkcheck retry on errors * Alexander Todorov -- inheritance_diagram tests and improvements +* Alireza Shabani -- RTL support * Andi Albrecht -- agogo theme * Antonio Valentino -- qthelp builder, docstring inheritance * Antti Kaihola -- doctest extension (skipif option) diff --git a/CHANGES.rst b/CHANGES.rst index 9f7b8f91eba..8e4f5a513f2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -111,6 +111,9 @@ Features added * #13326: Remove hardcoding from handling :class:`~sphinx.addnodes.productionlist` nodes in all writers, to improve flexibility. Patch by Adam Turner. +* #10385: Add RTL (right-to-left) support for all Sphinx themes via the ``is_rtl`` + theme option. Includes automatic layout mirroring, and bidirectional text support. + Patch by Alireza Shabani. Bugs fixed ---------- diff --git a/doc/usage/theming.rst b/doc/usage/theming.rst index a596acfe0eb..7a6bb958936 100644 --- a/doc/usage/theming.rst +++ b/doc/usage/theming.rst @@ -192,6 +192,18 @@ These themes are: .. versionadded:: 3.2 + - **is_rtl** (true or false): Enable right-to-left (RTL) text direction. + Use this for languages that are read right-to-left (like Farsi, Arabic, + Hebrew, etc). When enabled, the theme will: + + - Change text direction to RTL + - Mirror layout components + - Keep code blocks in LTR direction + + Defaults to ``False``. + + .. versionadded:: 8.2.0 + **alabaster** `Alabaster theme`_ is a modified "Kr" Sphinx theme from @kennethreitz (especially as used in his Requests project), which was itself originally diff --git a/sphinx/themes/agogo/static/agogo.css.jinja b/sphinx/themes/agogo/static/agogo.css.jinja index d281c744d3c..476c3a1df39 100644 --- a/sphinx/themes/agogo/static/agogo.css.jinja +++ b/sphinx/themes/agogo/static/agogo.css.jinja @@ -2,6 +2,8 @@ * Sphinx stylesheet -- agogo theme. */ +@import url("rtl.css"); + * { margin: 0px; padding: 0px; diff --git a/sphinx/themes/agogo/static/rtl.css.jinja b/sphinx/themes/agogo/static/rtl.css.jinja new file mode 100644 index 00000000000..bb6e427a55d --- /dev/null +++ b/sphinx/themes/agogo/static/rtl.css.jinja @@ -0,0 +1,156 @@ +{% if theme_is_rtl|tobool %} + +/* Core RTL overrides for Agogo theme */ +body, +div.header div.rel { + direction: rtl !important; +} + +body { + text-align: right !important; +} + +/* Header adjustments */ +div.header .headertitle { + text-align: right !important; + letter-spacing: 0 !important; +} + +p.logo { + float: left !important; +} + +/* Content layout */ +div.document { + float: right !important; +} + +div.body { + {%- if theme_rightsidebar|tobool %} + padding-left: 2em !important; + padding-right: 0 !important; + {%- else %} + padding-right: 2em !important; + padding-left: 0 !important; + {% endif %} +} + +/* Sidebar positioning */ +div.sidebar, +aside.sidebar { + {%- if theme_rightsidebar|tobool %} + float: left !important; + {%- else %} + float: right !important; + {% endif %} +} + +/* Lists and margins */ +div.document ul { + margin-right: 1.5em !important; + margin-left: 0 !important; +} + +div.document dd { + margin-right: 1.2em !important; + margin-left: 0 !important; +} + +/* Admonitions */ +div.admonition { + border-right: 0.2em solid black !important; + border-left: none !important; +} + +/* Search elements */ +ul.search { + margin: 10px 20px 0 0 !important; +} + +ul.search li { + padding: 5px 20px 5px 0 !important; + background-position: right 7px !important; +} + +/* Table alignments */ +table.indextable td { + text-align: right !important; +} + +/* Footer adjustments */ +div.footer .right { + text-align: left !important; +} + +div.footer .left { + text-align: right !important; +} + +/* Code blocks - keep LTR */ +div.highlight pre { + direction: ltr !important; + text-align: left !important; +} + +/* Header links */ +a.headerlink { + padding-right: .3em !important; + padding-left: 0 !important; +} + +/* Sidebar list adjustments */ +div.sidebar li.toctree-l1 a, +aside.sidebar li.toctree-l1 a { + padding-right: 3px !important; + padding-left: 0 !important; +} + +div.sidebar li.toctree-l2 a, +aside.sidebar li.toctree-l2 a { + margin-right: 1em !important; + margin-left: 0 !important; +} + +div.sidebar li.toctree-l3 a, +aside.sidebar li.toctree-l3 a { + margin-right: 2em !important; + margin-left: 0 !important; +} + +/* Float adjustments */ +.left { + float: right !important; +} + +.right { + float: left !important; +} + +/* Image alignments */ +img.align-left, +.figure.align-left, +object.align-left { + float: right !important; + margin-left: 1em !important; + margin-right: 0 !important; +} + +img.align-right, +.figure.align-right, +object.align-right { + float: left !important; + margin-right: 1em !important; + margin-left: 0 !important; +} + +/* Viewcode extension */ +.viewcode-link, +.viewcode-back { + float: left !important; +} + +/* Equation numbers */ +span.eqno { + float: left !important; +} +{% endif %} \ No newline at end of file diff --git a/sphinx/themes/basic/static/basic.css.jinja b/sphinx/themes/basic/static/basic.css.jinja index 4a00f3629f0..6102503d502 100644 --- a/sphinx/themes/basic/static/basic.css.jinja +++ b/sphinx/themes/basic/static/basic.css.jinja @@ -4,6 +4,8 @@ /* -- main layout ----------------------------------------------------------- */ +@import url("rtl.css"); + div.clearer { clear: both; } diff --git a/sphinx/themes/basic/static/documentation_options.js.jinja b/sphinx/themes/basic/static/documentation_options.js.jinja index b66690abd19..5e2895f4f00 100644 --- a/sphinx/themes/basic/static/documentation_options.js.jinja +++ b/sphinx/themes/basic/static/documentation_options.js.jinja @@ -10,4 +10,5 @@ const DOCUMENTATION_OPTIONS = { NAVIGATION_WITH_KEYS: {{ 'true' if theme_navigation_with_keys|tobool else 'false'}}, SHOW_SEARCH_SUMMARY: {{ 'true' if show_search_summary else 'false' }}, ENABLE_SEARCH_SHORTCUTS: {{ 'true' if theme_enable_search_shortcuts|tobool else 'false'}}, + IS_RTL: {{ 'true' if is_rtl else 'false' }}, }; diff --git a/sphinx/themes/basic/static/rtl.css.jinja b/sphinx/themes/basic/static/rtl.css.jinja new file mode 100644 index 00000000000..538d0a74899 --- /dev/null +++ b/sphinx/themes/basic/static/rtl.css.jinja @@ -0,0 +1,118 @@ +{% if theme_is_rtl|tobool %} + +/* Core RTL layout */ +body, +div.document, +div.documentwrapper, +div.body p, +div.body dd, +div.body li, +div.body blockquote { + direction: rtl !important; +} + +/* Text alignments */ +body, +div.body p, +div.body dd, +div.body li, +div.body blockquote, +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6, +th, +table.docutils td { + text-align: right !important; +} + +/* Layout adjustments */ +div.sphinxsidebar { + float: right !important; + margin-right: -100% !important; +} + +div.bodywrapper { + margin: 0 {{ theme_sidebarwidth|todim }} 0 0 !important; +} + +/* Headers padding */ +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + padding: 3px 10px 3px 0 !important; +} + +/* Lists */ +ul, +ol { + padding-right: 30px !important; + padding-left: 0 !important; +} + +/* Left-aligned elements */ +div.related li.right, +#sidebarbutton { + float: left !important; +} + +div.related li.right { + margin-left: 10px !important; + margin-right: 0 !important; +} + +/* Form and search elements */ +div.sphinxsidebar input, +div.sphinxsidebar #searchbox input[type="text"], +div.sphinxsidebar #searchbox input[type="submit"] { + float: right !important; + text-align: right !important; +} + +/* Footnotes */ +.footnote-reference { + float: right !important; +} + +.footnote { + direction: rtl !important; + text-align: right !important; +} + +/* Code blocks - keep LTR */ +pre { + direction: ltr !important; + text-align: left !important; + border-right: 4px solid #ac9 !important; + border-left: none !important; +} + +/* Admonitions */ +div.admonition { + padding: 7px 10px 7px 7px !important; +} + +/* References and links */ +a.reference.internal, +a.reference.external { + unicode-bidi: bidi-override !important; +} + +/* Sidebar collapsible button */ +{% if theme_collapsiblesidebar|tobool %} +#sidebarbutton { + border-right: 1px solid {{ theme_relbarbgcolor }} !important; + border-left: none !important; +} +{% endif %} + +/* Topic boxes and sidebars */ +div.topic { + margin: 0 0 0.5em 1em !important; +} +{% endif %} \ No newline at end of file diff --git a/sphinx/themes/basic/theme.toml b/sphinx/themes/basic/theme.toml index f380be0ae64..34277396db1 100644 --- a/sphinx/themes/basic/theme.toml +++ b/sphinx/themes/basic/theme.toml @@ -21,3 +21,4 @@ enable_search_shortcuts = "True" globaltoc_collapse = "true" globaltoc_includehidden = "false" globaltoc_maxdepth = "" +is_rtl = "false" diff --git a/sphinx/themes/epub/static/epub.css.jinja b/sphinx/themes/epub/static/epub.css.jinja index 306016bd301..354fe37da42 100644 --- a/sphinx/themes/epub/static/epub.css.jinja +++ b/sphinx/themes/epub/static/epub.css.jinja @@ -2,6 +2,8 @@ * Sphinx stylesheet -- epub theme. */ +@import url("rtl.css"); + /* -- main layout ----------------------------------------------------------- */ {% if theme_writing_mode is defined %} diff --git a/sphinx/themes/epub/static/rtl.css.jinja b/sphinx/themes/epub/static/rtl.css.jinja new file mode 100644 index 00000000000..b46c2bb5617 --- /dev/null +++ b/sphinx/themes/epub/static/rtl.css.jinja @@ -0,0 +1,167 @@ +{% if theme_is_rtl|tobool %} + +/* Core RTL layout for Epub theme */ +body, +div.header div.rel, +div.body p, +div.body dd, +div.body li, +div.body blockquote, +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + direction: rtl !important; + text-align: right !important; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + border-bottom-color: {{ theme_headercolor2 }} !important; +} + +/* Header adjustments */ +div.header .headertitle { + letter-spacing: 0 !important; +} + +p.logo { + float: left !important; +} + +/* Content layout */ +div.document { + float: right !important; +} + +div.body { + {%- if theme_rightsidebar|tobool %} + padding-left: 2em !important; + padding-right: 0 !important; + {%- else %} + padding-right: 2em !important; + padding-left: 0 !important; + {% endif %} +} + +/* Sidebar positioning - specific to Agogo */ +div.sidebar, +aside.sidebar { + {%- if theme_rightsidebar|tobool %} + float: left !important; + margin: 0 1em 0 0 !important; + {%- else %} + float: right !important; + margin: 0 0 0 1em !important; + {% endif %} +} + +/* Lists and margins */ +div.document ul { + margin-right: 1.5em !important; + margin-left: 0 !important; +} + +div.document dd { + margin-right: 1.2em !important; + margin-left: 0 !important; +} + +/* Tables */ +table.docutils td, +table.docutils th { + padding: 1px 5px 1px 8px !important; +} + +/* Admonitions */ +div.admonition { + padding: 7px !important; + border-right: 0.2em solid black !important; + border-left: none !important; +} + +p.admonition-title { + margin: 0 0 5px 10px !important; +} + +/* Code blocks - keep LTR */ +div.highlight pre { + direction: ltr !important; + text-align: left !important; + font-family: {{ theme_codecodefront }} !important; +} + +/* Header links */ +a.headerlink { + padding-right: .3em !important; + padding-left: 0 !important; +} + +/* Search elements */ +ul.search { + margin: 10px 20px 0 0 !important; +} + +ul.search li { + padding: 5px 20px 5px 0 !important; + background-position: right 7px !important; +} + +/* Footer adjustments */ +div.footer-wrapper { + text-align: right !important; +} + +div.footer .right { + text-align: left !important; +} + +div.footer .left { + text-align: right !important; +} + +/* Float adjustments */ +.left { + float: right !important; +} + +.right { + float: left !important; +} + +/* Image alignments */ +img.align-left, +.figure.align-left, +object.align-left { + float: right !important; + margin-left: 1em !important; + margin-right: 0 !important; +} + +img.align-right, +.figure.align-right, +object.align-right { + float: left !important; + margin-right: 1em !important; + margin-left: 0 !important; +} + +/* Field lists */ +dl.field-list > dt { + float: right !important; + clear: right !important; + padding-left: 0.5em !important; + padding-right: 0 !important; +} + +dl.field-list > dd { + margin-right: 9em !important; + margin-left: 0 !important; +} +{% endif %} \ No newline at end of file diff --git a/sphinx/themes/nonav/static/nonav.css.jinja b/sphinx/themes/nonav/static/nonav.css.jinja index f68ff88dcd6..a905a4ac0f8 100644 --- a/sphinx/themes/nonav/static/nonav.css.jinja +++ b/sphinx/themes/nonav/static/nonav.css.jinja @@ -2,6 +2,8 @@ * Sphinx stylesheet -- nonav theme. */ +@import url("rtl.css"); + /* -- main layout ----------------------------------------------------------- */ div.clearer { diff --git a/sphinx/themes/nonav/static/rtl.css.jinja b/sphinx/themes/nonav/static/rtl.css.jinja new file mode 100644 index 00000000000..86bc85e6f6c --- /dev/null +++ b/sphinx/themes/nonav/static/rtl.css.jinja @@ -0,0 +1,147 @@ +{% if theme_is_rtl|tobool %} + +/* Core RTL overrides for Nonav theme */ +body, +div.document, +div.documentwrapper, +div.body p, +div.body dd, +div.body li, +div.body blockquote { + direction: rtl !important; +} + +body, +div.body p, +div.body dd, +div.body li, +div.body blockquote, +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + text-align: right !important; +} + +/* Lists */ +ul, +ol { + margin-right: 30px !important; + margin-left: 0 !important; + padding-right: 0 !important; + padding-left: 0 !important; +} + +dd { + margin-right: 30px !important; + margin-left: 0 !important; +} + +/* Tables */ +table.docutils td, +table.docutils th { + text-align: right !important; + padding: 1px 5px 1px 8px !important; +} + +th { + text-align: right !important; + padding-left: 5px !important; + padding-right: 0 !important; +} + +/* Search page */ +ul.search { + margin: 10px 20px 0 0 !important; +} + +ul.search li { + padding: 5px 20px 5px 0 !important; + background-position: right 7px !important; +} + +ul.search li div.context { + margin: 2px 30px 0 0 !important; + text-align: right !important; +} + +/* Admonitions */ +div.admonition { + border-right: 0.2em solid black !important; + border-left: none !important; +} + +p.admonition-title { + margin: 0 0 5px 10px !important; +} + +/* Code blocks - keep LTR */ +pre { + direction: ltr !important; + text-align: left !important; +} + +/* Field lists */ +dl.field-list > dt { + float: right !important; + clear: right !important; + padding-left: 0.5em !important; + padding-right: 0 !important; +} + +dl.field-list > dd { + margin-right: 9em !important; + margin-left: 0 !important; +} + +/* Sidebars and topics */ +div.sidebar, +aside.sidebar { + float: left !important; + margin: 0 1em 0.5em 0 !important; +} + +/* Footer */ +div.footer { + text-align: left !important; + padding: 3px 0 3px 8px !important; +} + +/* Image alignments */ +img.align-left, +.figure.align-left, +object.align-left { + float: right !important; + margin-left: 1em !important; + margin-right: 0 !important; +} + +img.align-right, +.figure.align-right, +object.align-right { + float: left !important; + margin-right: 1em !important; + margin-left: 0 !important; +} + +/* Equation numbers */ +span.eqno { + float: left !important; +} + +/* Bibliography and citations */ +table.citation { + border-right: solid 1px gray !important; + border-left: none !important; + margin-right: 1px !important; + margin-left: 0 !important; +} + +/* Line blocks */ +.line-block .line-block { + margin-right: 1.5em !important; + margin-left: 0 !important; +} +{% endif %} \ No newline at end of file diff --git a/sphinx/themes/scrolls/static/rtl.css.jinja b/sphinx/themes/scrolls/static/rtl.css.jinja new file mode 100644 index 00000000000..31668bb8b47 --- /dev/null +++ b/sphinx/themes/scrolls/static/rtl.css.jinja @@ -0,0 +1,150 @@ +{% if theme_is_rtl|tobool %} + +/* Core RTL layout for Scrolls theme */ +body { + direction: rtl !important; + text-align: right !important; +} + +/* Main layout adjustments */ +div.document { + direction: rtl !important; +} + +div.documentwrapper { + direction: rtl !important; +} + +/* Text alignments */ +div.body p, +div.body dd, +div.body li, +div.body blockquote { + direction: rtl !important; + text-align: right !important; +} + +/* Headers alignment */ +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + text-align: right !important; +} + +/* Lists */ +ul, ol { + margin-right: 30px !important; + margin-left: 0 !important; + padding-right: 0 !important; + padding-left: 0 !important; +} + +dd { + margin-right: 30px !important; + margin-left: 0 !important; +} + +/* Tables */ +table.docutils td, +table.docutils th { + text-align: right !important; + padding: 1px 5px 1px 8px !important; +} + +th { + text-align: right !important; + padding-left: 5px !important; + padding-right: 0 !important; +} + +/* Search page */ +ul.search { + margin: 10px 20px 0 0 !important; +} + +ul.search li { + padding: 5px 20px 5px 0 !important; + background-position: right 7px !important; +} + +ul.search li div.context { + margin: 2px 30px 0 0 !important; + text-align: right !important; +} + +/* Admonitions */ +div.admonition { + padding: 7px !important; + border-right: 0.2em solid black !important; + border-left: none !important; +} + +p.admonition-title { + margin: 0 0 5px 10px !important; +} + +/* Code blocks - keep LTR */ +pre { + direction: ltr !important; + text-align: left !important; +} + +/* Field lists */ +dl.field-list > dt { + float: right !important; + clear: right !important; + padding-left: 0.5em !important; + padding-right: 0 !important; +} + +dl.field-list > dd { + margin-right: 9em !important; + margin-left: 0 !important; +} + +/* Sidebars and topics */ +div.sidebar, +aside.sidebar { + float: left !important; + margin: 0 1em 0.5em 0 !important; +} + +/* Image alignments */ +img.align-left, +.figure.align-left, +object.align-left { + float: right !important; + margin-left: 1em !important; + margin-right: 0 !important; +} + +img.align-right, +.figure.align-right, +object.align-right { + float: left !important; + margin-right: 1em !important; + margin-left: 0 !important; +} + +/* Equation numbers */ +span.eqno { + float: left !important; +} + +/* Bibliography and citations */ +table.citation { + border-right: solid 1px gray !important; + border-left: none !important; + margin-right: 1px !important; + margin-left: 0 !important; +} + +/* Line blocks */ +.line-block .line-block { + margin-right: 1.5em !important; + margin-left: 0 !important; +} +{% endif %} \ No newline at end of file diff --git a/sphinx/themes/scrolls/static/scrolls.css.jinja b/sphinx/themes/scrolls/static/scrolls.css.jinja index 401449b8d9d..1578d6f752d 100644 --- a/sphinx/themes/scrolls/static/scrolls.css.jinja +++ b/sphinx/themes/scrolls/static/scrolls.css.jinja @@ -2,6 +2,8 @@ * Sphinx stylesheet -- scrolls theme. */ +@import url("rtl.css"); + body { background-color: #222; margin: 0; diff --git a/sphinx/themes/traditional/static/rtl.css.jinja b/sphinx/themes/traditional/static/rtl.css.jinja new file mode 100644 index 00000000000..d8ee7925e19 --- /dev/null +++ b/sphinx/themes/traditional/static/rtl.css.jinja @@ -0,0 +1,150 @@ +{% if theme_is_rtl|tobool %} + +/* Core RTL layout */ +body, +nav.contents, +aside.topic, +div.topic { + direction: rtl !important; +} + +/* Layout adjustments */ +div.documentwrapper { + float: right !important; +} + +div.bodywrapper { + margin: 0 0 0 {{ theme_sidebarwidth|todim }} !important; +} + +div.sphinxsidebar { + float: left !important; + margin-right: -100% !important; + margin-left: 0 !important; +} + +/* Navigation and related */ +div.related ul { + padding: 0 10px 0 0 !important; +} + +div.related li.right { + float: left !important; + margin-left: 5px !important; + margin-right: 0 !important; +} + +/* Text alignments */ +div.body p, +div.body dd, +div.body li, +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + text-align: right !important; +} + +div.body td { + text-align: right !important; + padding: 0 0 2px 8px !important; +} + +/* Lists and margins */ +ul.fakelist { + margin: 10px 20px 10px 0 !important; +} + +dd { + margin: 3px 30px 10px 0 !important; +} + +/* Sidebar adjustments */ +div.sphinxsidebarwrapper { + margin: 10px 0 10px 15px !important; +} + +div.sphinxsidebar ul { + margin-right: 15px !important; + margin-left: 0 !important; +} + +/* Search elements */ +ul.search { + margin: 10px 20px 0 0 !important; +} + +ul.search li { + padding: 5px 20px 5px 0 !important; + background-position: right 7px !important; +} + +ul.search li div.context { + margin: 2px 30px 0 0 !important; + text-align: right !important; +} + +/* Form elements */ +form dt { + float: right !important; + margin-left: 10px !important; + margin-right: 0 !important; +} + +/* Code blocks - keep LTR */ +pre { + direction: ltr !important; + text-align: left !important; +} + +/* Tables */ +th { + text-align: right !important; + padding-left: 5px !important; + padding-right: 0 !important; +} + +p.admonition-title { + margin: 0 0 5px 10px !important; +} + +/* View code links */ +.viewcode-link, +.viewcode-back { + float: left !important; +} + +/* Equation numbers */ +span.eqno { + float: left !important; +} + +/* Line blocks */ +.line-block .line-block { + margin: 0 1.5em 0 0 !important; +} + +/* Index tables */ +table.indextable td { + text-align: right !important; +} + +table.indextable > tbody > tr > td > ul { + padding-right: 0 !important; +} + +/* Field lists */ +.field-list ul { + padding-right: 1em !important; + padding-left: 0 !important; +} + +/* Print styles */ +@media print { + div.documentwrapper { + direction: rtl !important; + } +} +{% endif %} \ No newline at end of file diff --git a/sphinx/themes/traditional/static/traditional.css.jinja b/sphinx/themes/traditional/static/traditional.css.jinja index c5e829786d0..ed0b004aa79 100644 --- a/sphinx/themes/traditional/static/traditional.css.jinja +++ b/sphinx/themes/traditional/static/traditional.css.jinja @@ -2,6 +2,8 @@ * Sphinx stylesheet -- traditional docs.python.org theme. */ +@import url("rtl.css"); + body { color: #000; margin: 0;