This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git
The following commit(s) were added to refs/heads/master by this push:
new 11ddc6fddc3 [refactor](next) fix some issues (#3666)
11ddc6fddc3 is described below
commit 11ddc6fddc32c1fde57c8905bdcb00049093f434
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Sun May 17 23:09:02 2026 -0700
[refactor](next) fix some issues (#3666)
---
.github/workflows/manual-deploy-website.yml | 1 +
src/components/home-next/NavbarNext.scss | 22 +--
src/components/home-next/NavbarNext.tsx | 19 +-
.../home-next/sections/EcosystemSection.scss | 2 +-
.../home-next/sections/EcosystemSection.tsx | 6 +-
.../home-next/sections/FeaturesSection.tsx | 213 +++++++++++++--------
src/components/home-next/sections/HeroSection.tsx | 29 +--
src/scss/custom.scss | 32 +---
src/theme/DocItem/Layout/MobileSidebarDrawer.tsx | 105 +++++-----
src/theme/DocItem/Layout/index.tsx | 6 +-
src/theme/DocItem/Layout/styles.module.css | 28 +++
static/js/custom-script.js | 31 +++
12 files changed, 301 insertions(+), 193 deletions(-)
diff --git a/.github/workflows/manual-deploy-website.yml
b/.github/workflows/manual-deploy-website.yml
index abcb2502f34..d69ed030b0f 100644
--- a/.github/workflows/manual-deploy-website.yml
+++ b/.github/workflows/manual-deploy-website.yml
@@ -42,6 +42,7 @@ jobs:
yarn cache clean
export NODE_OPTIONS=--max-old-space-size=8192
yarn
+ node ./scripts/update_github_info.js
yarn key-features:generate
PWA_SERVICE_WORKER_URL=https://doris.apache.org/sw.js yarn
docusaurus build --locale en --locale zh-CN
if [ ! -d "./ja-build" ]; then
diff --git a/src/components/home-next/NavbarNext.scss
b/src/components/home-next/NavbarNext.scss
index 5481134d3c6..abf1c391f35 100644
--- a/src/components/home-next/NavbarNext.scss
+++ b/src/components/home-next/NavbarNext.scss
@@ -462,15 +462,6 @@
&--mobile-open &__mobile-panel {
display: block;
}
-
- // With nav hidden, push Ask AI + actions to the right together.
- &__ask-ai {
- margin-left: auto;
- }
-
- &__actions {
- margin-left: 0;
- }
}
}
@@ -542,11 +533,18 @@
// Trim star-link horizontal padding (only change needed to make
everything fit).
&__star-link {
padding: 0 8px 0 6px;
+ // Right-anchor the Star+Download+☰ group. Since `__actions`
collapses
+ // to `display: contents` below, Star is the first member of the
+ // right-side group and inherits the auto-margin job from
__actions.
+ margin-left: auto;
}
- // Push Star + Download + ☰ together to the right as one group.
+ // Collapse `__actions` so its children (Ask Me, Star, Download) become
+ // flex children of `__inner` directly. This lets Ask Me wrap to its
own
+ // full-width second row via `flex: 0 0 100%; order: 10` below — which
+ // is impossible while it's nested inside a non-wrapping flex
container.
&__actions {
- margin-left: auto;
+ display: contents;
}
// Hamburger follows actions naturally — no extra margin needed.
@@ -554,7 +552,7 @@
margin-left: 0;
}
- // Ask AI: full-width second row, icon + text centered both axes.
+ // Ask Me: full-width second row, icon + text centered both axes.
&__ask-ai {
order: 10;
flex: 0 0 100%;
diff --git a/src/components/home-next/NavbarNext.tsx
b/src/components/home-next/NavbarNext.tsx
index 772a4a4eac0..41043e26498 100644
--- a/src/components/home-next/NavbarNext.tsx
+++ b/src/components/home-next/NavbarNext.tsx
@@ -168,17 +168,16 @@ export function NavbarNext(): JSX.Element {
))}
</div>
- <button
- type="button"
- id="navbar-ask-ai-btn"
- className="navbar-next__ask-ai"
- aria-label="Ask AI"
- >
- <StarGreenIcon />
- <span>Ask AI</span>
- </button>
-
<div className="navbar-next__actions">
+ <button
+ type="button"
+ id="navbar-ask-ai-btn"
+ className="navbar-next__ask-ai"
+ aria-label="Ask Me"
+ >
+ <StarGreenIcon />
+ <span>Ask Me</span>
+ </button>
<a
href={`https://github.com/${GITHUB_REPO}`}
target="_blank"
diff --git a/src/components/home-next/sections/EcosystemSection.scss
b/src/components/home-next/sections/EcosystemSection.scss
index 394497f57ec..f77abbdbf17 100644
--- a/src/components/home-next/sections/EcosystemSection.scss
+++ b/src/components/home-next/sections/EcosystemSection.scss
@@ -329,7 +329,7 @@
&__logo--metabase .ecosystem-next__logo-image,
&__logo--grafana .ecosystem-next__logo-image,
- &__logo--langfuse .ecosystem-next__logo-image {
+ &__logo--litefuse .ecosystem-next__logo-image {
padding: 0;
}
diff --git a/src/components/home-next/sections/EcosystemSection.tsx
b/src/components/home-next/sections/EcosystemSection.tsx
index 02549513516..243eaa4d142 100644
--- a/src/components/home-next/sections/EcosystemSection.tsx
+++ b/src/components/home-next/sections/EcosystemSection.tsx
@@ -144,9 +144,9 @@ const CONSUMERS: EcosystemGroup[] = [
className: 'ecosystem-next__logo--grafana',
},
{
- name: 'Langfuse',
- logoSrc: '/images/ecomsystem-log/longfuse.png',
- className: 'ecosystem-next__logo--langfuse',
+ name: 'Litefuse',
+ logoSrc: '/images/ecomsystem-log/litefuse.png',
+ className: 'ecosystem-next__logo--litefuse',
},
],
},
diff --git a/src/components/home-next/sections/FeaturesSection.tsx
b/src/components/home-next/sections/FeaturesSection.tsx
index 22ebc0d80e8..72d393169f3 100644
--- a/src/components/home-next/sections/FeaturesSection.tsx
+++ b/src/components/home-next/sections/FeaturesSection.tsx
@@ -1,4 +1,4 @@
-import React, { CSSProperties, JSX, useEffect, useRef, useState } from 'react';
+import React, { CSSProperties, JSX, useEffect, useMemo, useRef, useState }
from 'react';
import './FeaturesSection.scss';
type CapabilityTone = 'green' | 'cream' | 'ink';
@@ -81,10 +81,6 @@ const CAPABILITIES: Capability[] = [
},
];
-interface CapabilityCardStyle extends CSSProperties {
- zIndex: number;
-}
-
function clamp(value: number, min: number, max: number): number {
return Math.max(min, Math.min(max, value));
}
@@ -93,61 +89,12 @@ function easeOut(value: number): number {
return 1 - Math.pow(1 - value, 2.5);
}
-function useContainerProgress(ref: React.RefObject<HTMLElement>): number {
- const [progress, setProgress] = useState(0);
-
- useEffect(() => {
- const el = ref.current;
- if (!el) return undefined;
-
- let frame = 0;
-
- function update() {
- window.cancelAnimationFrame(frame);
- frame = window.requestAnimationFrame(() => {
- const rect = el.getBoundingClientRect();
- const viewportHeight = window.innerHeight;
- const scrollable = rect.height - viewportHeight;
-
- if (scrollable <= 0) {
- setProgress(rect.top <= 0 ? 1 : 0);
- return;
- }
-
- setProgress(clamp(-rect.top / scrollable, 0, 1));
- });
- }
-
- update();
- window.addEventListener('scroll', update, { passive: true });
- window.addEventListener('resize', update);
-
- return () => {
- window.cancelAnimationFrame(frame);
- window.removeEventListener('scroll', update);
- window.removeEventListener('resize', update);
- };
- }, [ref]);
-
- return progress;
-}
-
-function useIsNarrowViewport(): boolean {
- const [isNarrowViewport, setIsNarrowViewport] = useState(false);
-
- useEffect(() => {
- const query = window.matchMedia('(max-width: 820px)');
- const update = () => setIsNarrowViewport(query.matches);
-
- update();
- query.addEventListener('change', update);
- return () => query.removeEventListener('change', update);
- }, []);
-
- return isNarrowViewport;
+interface CardTransform {
+ transform: string;
+ opacity: number;
}
-function getCardStyle(capability: Capability, idx: number, total: number,
progress: number): CapabilityCardStyle {
+function computeCardTransform(capability: Capability, idx: number, total:
number, progress: number): CardTransform {
const transitions = Math.max(1, total - 1);
const slice = 1 / transitions;
const activeAt = idx * slice;
@@ -184,12 +131,35 @@ function getCardStyle(capability: Capability, idx:
number, total: number, progre
}
return {
- transform: `translate(-50%, -50%) translateY(${translateY}px)
scale(${scale}) rotate(${rotation}deg)`,
+ transform: `translate3d(-50%, -50%, 0) translateY(${translateY}px)
scale(${scale}) rotate(${rotation}deg)`,
opacity,
- zIndex: 10 + idx,
};
}
+const INITIAL_CARD_TRANSFORMS = CAPABILITIES.map((cap, i) =>
+ computeCardTransform(cap, i, CAPABILITIES.length, 0),
+);
+
+const INITIAL_DOT_ON = CAPABILITIES.map((_, i) => {
+ const transitions = Math.max(1, CAPABILITIES.length - 1);
+ return 0 >= i / transitions - 0.001;
+});
+
+function useIsNarrowViewport(): boolean {
+ const [isNarrowViewport, setIsNarrowViewport] = useState(false);
+
+ useEffect(() => {
+ const query = window.matchMedia('(max-width: 820px)');
+ const update = () => setIsNarrowViewport(query.matches);
+
+ update();
+ query.addEventListener('change', update);
+ return () => query.removeEventListener('change', update);
+ }, []);
+
+ return isNarrowViewport;
+}
+
function ArrowIcon(): JSX.Element {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
@@ -280,15 +250,24 @@ interface CapabilityCardProps {
capability: Capability;
idx: number;
total: number;
- progress: number;
+ cardRef: (el: HTMLElement | null) => void;
isNarrowViewport: boolean;
}
-function CapabilityCard({ capability, idx, total, progress, isNarrowViewport
}: CapabilityCardProps): JSX.Element {
+function CapabilityCard({ capability, idx, total, cardRef, isNarrowViewport }:
CapabilityCardProps): JSX.Element {
+ const initialStyle: CSSProperties | undefined = isNarrowViewport
+ ? undefined
+ : {
+ zIndex: 10 + idx,
+ transform: INITIAL_CARD_TRANSFORMS[idx].transform,
+ opacity: INITIAL_CARD_TRANSFORMS[idx].opacity,
+ };
+
return (
<article
+ ref={cardRef}
className={`features-next__card
features-next__card--${capability.tone}`}
- style={isNarrowViewport ? undefined : getCardStyle(capability,
idx, total, progress)}
+ style={initialStyle}
>
<div className="features-next__copy">
<div className="features-next__card-num">
@@ -321,8 +300,90 @@ function CapabilityCard({ capability, idx, total,
progress, isNarrowViewport }:
export function FeaturesSection(): JSX.Element {
const containerRef = useRef<HTMLDivElement>(null);
+ const cardRefs = useRef<(HTMLElement | null)[]>([]);
+ const dotRefs = useRef<(HTMLSpanElement | null)[]>([]);
const isNarrowViewport = useIsNarrowViewport();
- const progress = useContainerProgress(containerRef);
+
+ const cardRefSetters = useMemo(
+ () =>
+ CAPABILITIES.map((_, idx) => (el: HTMLElement | null) => {
+ cardRefs.current[idx] = el;
+ }),
+ [],
+ );
+ const dotRefSetters = useMemo(
+ () =>
+ CAPABILITIES.map((_, idx) => (el: HTMLSpanElement | null) => {
+ dotRefs.current[idx] = el;
+ }),
+ [],
+ );
+
+ useEffect(() => {
+ if (isNarrowViewport) {
+ cardRefs.current.forEach(el => {
+ if (!el) return;
+ el.style.transform = '';
+ el.style.opacity = '';
+ });
+ dotRefs.current.forEach(el => {
+ el?.classList.remove('features-next__stage-dot--on');
+ });
+ return undefined;
+ }
+
+ const containerEl = containerRef.current;
+ if (!containerEl) return undefined;
+
+ let frame = 0;
+ let lastProgress = -1;
+
+ function update() {
+ window.cancelAnimationFrame(frame);
+ frame = window.requestAnimationFrame(() => {
+ const rect = containerEl.getBoundingClientRect();
+ const viewportHeight = window.innerHeight;
+ const scrollable = rect.height - viewportHeight;
+
+ let progress: number;
+ if (scrollable <= 0) {
+ progress = rect.top <= 0 ? 1 : 0;
+ } else {
+ progress = clamp(-rect.top / scrollable, 0, 1);
+ }
+
+ if (Math.abs(progress - lastProgress) < 0.0005) return;
+ lastProgress = progress;
+
+ const total = CAPABILITIES.length;
+ for (let i = 0; i < cardRefs.current.length; i++) {
+ const cardEl = cardRefs.current[i];
+ if (!cardEl) continue;
+ const { transform, opacity } =
computeCardTransform(CAPABILITIES[i], i, total, progress);
+ cardEl.style.transform = transform;
+ cardEl.style.opacity = String(opacity);
+ }
+
+ const transitions = Math.max(1, total - 1);
+ for (let i = 0; i < dotRefs.current.length; i++) {
+ const dotEl = dotRefs.current[i];
+ if (!dotEl) continue;
+ const isOn = progress >= i / transitions - 0.001;
+ dotEl.classList.toggle('features-next__stage-dot--on',
isOn);
+ }
+ });
+ }
+
+ update();
+ window.addEventListener('scroll', update, { passive: true });
+ window.addEventListener('resize', update);
+
+ return () => {
+ window.cancelAnimationFrame(frame);
+ window.removeEventListener('scroll', update);
+ window.removeEventListener('resize', update);
+ };
+ }, [isNarrowViewport]);
return (
<section className="features-next">
@@ -345,22 +406,22 @@ export function FeaturesSection(): JSX.Element {
capability={capability}
idx={i}
total={CAPABILITIES.length}
- progress={progress}
+ cardRef={cardRefSetters[i]}
isNarrowViewport={isNarrowViewport}
/>
))}
<div className="features-next__stage-progress"
aria-hidden="true">
- {CAPABILITIES.map((capability, i) => {
- const transitions = Math.max(1,
CAPABILITIES.length - 1);
- const isOn = progress >= i / transitions -
0.001;
-
- return (
- <span
- key={capability.num}
- className={isOn ?
'features-next__stage-dot features-next__stage-dot--on' :
'features-next__stage-dot'}
- />
- );
- })}
+ {CAPABILITIES.map((capability, i) => (
+ <span
+ key={capability.num}
+ ref={dotRefSetters[i]}
+ className={
+ INITIAL_DOT_ON[i]
+ ? 'features-next__stage-dot
features-next__stage-dot--on'
+ : 'features-next__stage-dot'
+ }
+ />
+ ))}
</div>
</div>
</div>
diff --git a/src/components/home-next/sections/HeroSection.tsx
b/src/components/home-next/sections/HeroSection.tsx
index ef60640e677..4d728808b77 100644
--- a/src/components/home-next/sections/HeroSection.tsx
+++ b/src/components/home-next/sections/HeroSection.tsx
@@ -1,4 +1,5 @@
import React, { JSX, useState, useEffect, useMemo, useRef } from 'react';
+import { StarGreenIcon } from '@site/src/components/Icons/star-green-icon';
import './HeroSection.scss';
// ─── SVG atoms
───────────────────────────────────────────────────────────────
@@ -24,14 +25,6 @@ function LightningSvg({ size = 24, color = '#FFD23F' }: {
size?: number; color?:
);
}
-function DownloadIcon(): JSX.Element {
- return (
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
- <path d="M12 4v12m0 0l-5-5m5 5l5-5M4 20h16" />
- </svg>
- );
-}
-
function SlackIcon(): JSX.Element {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"
aria-hidden="true">
@@ -897,13 +890,23 @@ export function HeroSection(): JSX.Element {
</p>
<div className="hero-next__ctas">
- <a className="hero-next__btn
hero-next__btn--yellow" href="/download">
- <DownloadIcon />
- Download
- </a>
- <a className="hero-next__btn
hero-next__btn--primary" href={GET_STARTED_HREF}>
+ <a className="hero-next__btn
hero-next__btn--yellow" href={GET_STARTED_HREF}>
Get Started
</a>
+ <button
+ type="button"
+ className="hero-next__btn
hero-next__btn--primary"
+ onClick={() => {
+ // Reuse the Kapa modal trigger bound to
the
+ // navbar Ask Me button via
docusaurus.config.js
+ // (data-modal-override-open-selector).
+
document.getElementById('navbar-ask-ai-btn')?.click();
+ }}
+ aria-label="Ask Me"
+ >
+ <StarGreenIcon />
+ Ask Me
+ </button>
<a
className="hero-next__btn
hero-next__btn--ghost"
href="https://doris.apache.org/slack"
diff --git a/src/scss/custom.scss b/src/scss/custom.scss
index 74c66c2854d..5106caa62b0 100644
--- a/src/scss/custom.scss
+++ b/src/scss/custom.scss
@@ -145,33 +145,13 @@ body {
}
/**
- * Kapa Ask AI modal must overlay the sticky NavbarNext (which sits at
- * z-index 300). Kapa's Mantine modal defaults to a lower stacking value
- * so without this override the dialog gets clipped behind the navbar.
+ * Lift Kapa's shadow-host above the sticky NavbarNext (z-index 300). The
+ * Mantine modal/overlay live inside the shadow root and inherit stacking
+ * from this host, so raising the host is enough.
*/
-[data-kapa-widget-container],
-.mantine-Modal-root,
-.mantine-Modal-inner,
-.mantine-Overlay-root,
-.mantine-Modal-overlay {
+#kapa-widget-container {
z-index: 9999 !important;
}
-/**
- * Push the Kapa Ask AI modal down from the top of the viewport so it
- * doesn't sit flush against the 64px sticky NavbarNext. Kapa renders its
- * modal flush against whichever container it sits below, so we both
- * override Mantine's yOffset variable on the inner wrapper and add a
- * margin-top on the modal panel itself — whichever Kapa is using, the
- * gap shows up.
- */
-.mantine-Modal-inner,
-[class*="Modal-inner"] {
- --mantine-modal-y-offset: 24px !important;
- padding-top: 24px !important;
-}
-
-.mantine-Modal-content,
-[class*="Modal-content"] {
- margin-top: 24px !important;
-}
+// Centering the Kapa modal itself is done by injecting CSS into its shadow
+// root from static/js/custom-script.js, since light-DOM rules can't pierce it.
diff --git a/src/theme/DocItem/Layout/MobileSidebarDrawer.tsx
b/src/theme/DocItem/Layout/MobileSidebarDrawer.tsx
index a55cca9a745..a366248a27b 100644
--- a/src/theme/DocItem/Layout/MobileSidebarDrawer.tsx
+++ b/src/theme/DocItem/Layout/MobileSidebarDrawer.tsx
@@ -1,4 +1,5 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';
+import { createPortal } from 'react-dom';
import clsx from 'clsx';
import { useLocation } from '@docusaurus/router';
import { useDocsSidebar } from '@docusaurus/plugin-content-docs/client';
@@ -58,6 +59,59 @@ export default function MobileSidebarDrawer(): JSX.Element |
null {
if (!sidebar) return null;
+ const drawer = (
+ <div className={clsx(open && styles.open)} aria-hidden={!open}>
+ <div
+ className={styles.backdrop}
+ onClick={close}
+ role="presentation"
+ />
+ <aside
+ className={styles.drawer}
+ aria-label={isZH ? '文档目录' : 'Docs sidebar'}
+ >
+ <div className={styles.header}>
+ <span>{isZH ? '目录' : 'Menu'}</span>
+ <button
+ type="button"
+ className={styles.close}
+ onClick={close}
+ aria-label={isZH ? '关闭目录' : 'Close docs sidebar'}
+ >
+ <svg width="20" height="20" viewBox="0 0 20 20"
fill="none" aria-hidden="true">
+ <path
+ d="M5 5l10 10M15 5L5 15"
+ stroke="currentColor"
+ strokeWidth="1.6"
+ strokeLinecap="round"
+ />
+ </svg>
+ </button>
+ </div>
+ <nav
+ className={styles.body}
+ aria-label={isZH ? '文档目录导航' : 'Docs sidebar navigation'}
+ >
+ <ul className={clsx(ThemeClassNames.docs.docSidebarMenu,
'menu__list', styles.menu)}>
+ <DocSidebarItems
+ items={sidebar.items}
+ activePath={pathname}
+ onItemClick={(item) => {
+ if (item.type === 'link') {
+ close();
+ }
+ if (item.type === 'category' && item.href) {
+ close();
+ }
+ }}
+ level={1}
+ />
+ </ul>
+ </nav>
+ </aside>
+ </div>
+ );
+
return (
<>
<div className={styles.toolbar}>
@@ -129,56 +183,7 @@ export default function MobileSidebarDrawer(): JSX.Element
| null {
</button>
</div>
- <div className={clsx(open && styles.open)} aria-hidden={!open}>
- <div
- className={styles.backdrop}
- onClick={close}
- role="presentation"
- />
- <aside
- className={styles.drawer}
- aria-label={isZH ? '文档目录' : 'Docs sidebar'}
- >
- <div className={styles.header}>
- <span>{isZH ? '目录' : 'Menu'}</span>
- <button
- type="button"
- className={styles.close}
- onClick={close}
- aria-label={isZH ? '关闭目录' : 'Close docs sidebar'}
- >
- <svg width="20" height="20" viewBox="0 0 20 20"
fill="none" aria-hidden="true">
- <path
- d="M5 5l10 10M15 5L5 15"
- stroke="currentColor"
- strokeWidth="1.6"
- strokeLinecap="round"
- />
- </svg>
- </button>
- </div>
- <nav
- className={styles.body}
- aria-label={isZH ? '文档目录导航' : 'Docs sidebar
navigation'}
- >
- <ul
className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list',
styles.menu)}>
- <DocSidebarItems
- items={sidebar.items}
- activePath={pathname}
- onItemClick={(item) => {
- if (item.type === 'link') {
- close();
- }
- if (item.type === 'category' && item.href)
{
- close();
- }
- }}
- level={1}
- />
- </ul>
- </nav>
- </aside>
- </div>
+ {typeof document !== 'undefined' && createPortal(drawer,
document.body)}
</>
);
}
diff --git a/src/theme/DocItem/Layout/index.tsx
b/src/theme/DocItem/Layout/index.tsx
index 546ec86e702..3a5d033c10d 100644
--- a/src/theme/DocItem/Layout/index.tsx
+++ b/src/theme/DocItem/Layout/index.tsx
@@ -69,8 +69,10 @@ export default function DocItemLayout({ children }: Props):
JSX.Element {
<DocVersionBanner />
<div className={styles.docItemContainer}>
<article>
- <MobileSidebarDrawer />
- <DocBreadcrumbs />
+ <div className={styles.mobileStickyHeader}>
+ <MobileSidebarDrawer />
+ <DocBreadcrumbs />
+ </div>
{/* <DocVersionBadge /> */}
{docTOC.mobile}
<DocItemContent>{children}</DocItemContent>
diff --git a/src/theme/DocItem/Layout/styles.module.css
b/src/theme/DocItem/Layout/styles.module.css
index e1664cd34b2..a31feeaf8ef 100644
--- a/src/theme/DocItem/Layout/styles.module.css
+++ b/src/theme/DocItem/Layout/styles.module.css
@@ -22,3 +22,31 @@
.footerBtn:hover {
border: 1px solid var(--ifm-color-primary);
}
+
+/* Freeze the search/locale/menu toolbar + breadcrumb at the top on mobile
+ so they remain reachable while scrolling. Sits just below the sticky
+ green NavbarNext (64px tall; ~112px when Ask AI wraps to a second row
+ at ≤480px). z-index stays under the navbar (300) but above content. */
+@media (max-width: 996px) {
+ .mobileStickyHeader {
+ position: sticky;
+ top: 64px;
+ z-index: 200;
+ background-color: #fff;
+ margin-bottom: 0.5rem;
+ padding: 0.5rem 0 0.25rem;
+ box-shadow: 0 4px 8px -4px rgba(0, 0, 0, 0.08);
+ }
+
+ /* Breadcrumb has its own margin-bottom that we want inside the sticky
+ strip's padding rather than escaping it. */
+ .mobileStickyHeader :global(.theme-doc-breadcrumbs) {
+ margin-bottom: 0;
+ }
+}
+
+@media (max-width: 480px) {
+ .mobileStickyHeader {
+ top: 112px;
+ }
+}
diff --git a/static/js/custom-script.js b/static/js/custom-script.js
index e746900d0bf..af8159231e7 100644
--- a/static/js/custom-script.js
+++ b/static/js/custom-script.js
@@ -10,3 +10,34 @@
y = l.getElementsByTagName(r)[0];
y.parentNode.insertBefore(t, y);
})(window, document, 'clarity', 'script', 'kfyqejiz0g');
+
+// Center the Kapa Ask Me modal vertically. Kapa renders inside a Shadow DOM
+// on `#kapa-widget-container`, so light-DOM CSS can't reach it. We inject a
+// <style> into the shadow root and re-inject if Kapa rebuilds it.
+(function centerKapaModal() {
+ var STYLE_ID = 'doris-kapa-center-modal';
+ var CSS_TEXT =
+ '.mantine-Modal-inner{' +
+ 'align-items:center !important;' +
+ 'padding-top:0 !important;' +
+ 'padding-bottom:0 !important;' +
+ '--modal-y-offset:0 !important;' +
+ '}';
+
+ function inject() {
+ var host = document.getElementById('kapa-widget-container');
+ if (!host || !host.shadowRoot) return false;
+ if (host.shadowRoot.getElementById(STYLE_ID)) return true;
+ var style = document.createElement('style');
+ style.id = STYLE_ID;
+ style.textContent = CSS_TEXT;
+ host.shadowRoot.appendChild(style);
+ return true;
+ }
+
+ if (inject()) return;
+ var observer = new MutationObserver(function () {
+ inject();
+ });
+ observer.observe(document.documentElement, { childList: true, subtree:
true });
+})();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]