From 2a8053d9f51ed8b08d22b19cb57d7286970d5199 Mon Sep 17 00:00:00 2001 From: Open Source Contributor Date: Sun, 3 May 2026 19:13:17 -0600 Subject: [PATCH] feat: add sidebarPosition option to position sidebar on right Adds a new config option 'sidebarPosition' that accepts 'left' (default) or 'right' to position the sidebar on either side of the content. This feature is useful for: - RTL (right-to-left) language support - Personal preference for right-side navigation Usage: window.$docsify = { sidebarPosition: 'right' } Closes #1282 --- src/core/config.js | 1 + src/core/render/index.js | 5 +++++ src/themes/shared/_app.css | 8 +++++++- src/themes/shared/_mq.css | 13 +++++++++++++ src/themes/shared/_sidebar.css | 26 ++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/core/config.js b/src/core/config.js index b2e2fdb9e2..044b277b11 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -49,6 +49,7 @@ const defaultDocsifyConfig = () => ({ false ), onlyCover: false, + sidebarPosition: /** @type {'left' | 'right'} */ ('left'), plugins: /** @type {Plugin[]} */ ([]), relativePath: false, repo: /** @type {string} */ (''), diff --git a/src/core/render/index.js b/src/core/render/index.js index f4db193754..bfaa493eb1 100644 --- a/src/core/render/index.js +++ b/src/core/render/index.js @@ -626,6 +626,11 @@ export function Render(Base) { } } + // Set sidebar position on body + if (config.sidebarPosition === 'right') { + dom.$.body.setAttribute('data-sidebar-position', 'right'); + } + this._updateRender(); dom.body.classList.add('ready'); } diff --git a/src/themes/shared/_app.css b/src/themes/shared/_app.css index b521a4c528..df771259a0 100644 --- a/src/themes/shared/_app.css +++ b/src/themes/shared/_app.css @@ -41,12 +41,18 @@ main { > .content { position: absolute; inset: 0; - transition: left var(--duration-medium) ease; + transition: left var(--duration-medium) ease, right var(--duration-medium) ease; body:has(.sidebar.show) & { left: var(--sidebar-width); } + /* Right sidebar position */ + body[data-sidebar-position='right']:has(.sidebar.show) & { + left: 0; + right: var(--sidebar-width); + } + /* hideSidebar: true */ body:not:has(.sidebar) & { position: static; diff --git a/src/themes/shared/_mq.css b/src/themes/shared/_mq.css index 61a67b5674..6b2ba0c4b8 100644 --- a/src/themes/shared/_mq.css +++ b/src/themes/shared/_mq.css @@ -58,6 +58,19 @@ main > .content { left: 0; } + + /* Right sidebar on mobile */ + body[data-sidebar-position='right']:has(.sidebar.show) { + .app-nav { + left: auto; + right: 0; + } + + main > .content { + left: auto; + right: 0; + } + } } body:has(.app-nav-merged) { diff --git a/src/themes/shared/_sidebar.css b/src/themes/shared/_sidebar.css index 13e29f2543..1cc80d3285 100644 --- a/src/themes/shared/_sidebar.css +++ b/src/themes/shared/_sidebar.css @@ -114,6 +114,15 @@ translate var(--duration-medium) ease, visibility var(--duration-medium); + /* Right sidebar position */ + [data-sidebar-position='right'] & { + left: auto; + right: 0; + translate: calc(0px + var(--sidebar-width)); + border-right: none; + border-left: 1px solid var(--sidebar-border-color); + } + /* Non-webkit browsers: style scrollbar for consistency */ @supports (scrollbar-width: auto) { &::-webkit-scrollbar { @@ -236,6 +245,23 @@ translate var(--duration-medium) ease; } + /* Right sidebar position */ + body[data-sidebar-position='right'] & { + left: auto; + right: 0; + + &.show { + translate: calc(0px - var(--sidebar-width)); + } + } + + body[data-sidebar-position='right']:has(.sidebar.show) & { + translate: calc(0px - var(--sidebar-width)); + transition: + background 0s, + translate var(--duration-medium) ease; + } + body.sticky & { position: fixed; }