diff --git a/packages/eui/changelogs/upcoming/9211.md b/packages/eui/changelogs/upcoming/9211.md
new file mode 100644
index 00000000000..0f1c822414f
--- /dev/null
+++ b/packages/eui/changelogs/upcoming/9211.md
@@ -0,0 +1 @@
+- Added `--euiBottomBarOffset` CSS variable to `EuiBottomBar` for positioning other fixed elements relative to the bottom bar's height
diff --git a/packages/eui/src/components/bottom_bar/bottom_bar.test.tsx b/packages/eui/src/components/bottom_bar/bottom_bar.test.tsx
index 4b2264e4e0f..1d69c1eec58 100644
--- a/packages/eui/src/components/bottom_bar/bottom_bar.test.tsx
+++ b/packages/eui/src/components/bottom_bar/bottom_bar.test.tsx
@@ -89,4 +89,77 @@ describe('EuiBottomBar', () => {
expect(baseElement).toMatchSnapshot();
});
});
+
+ describe('CSS variables', () => {
+ test('sets the correct css variable for the bottom bar', () => {
+ const { unmount } = render(
+
+ Content
+
+ );
+
+ // The CSS variable should be set on the document root
+ const cssVarValue = getComputedStyle(
+ document.documentElement
+ ).getPropertyValue('--euiBottomBarOffset');
+ expect(cssVarValue).toBeTruthy();
+ expect(cssVarValue).toMatch(/\d+px/);
+
+ unmount();
+
+ // After unmounting, the CSS variable should be cleared
+ const clearedValue = getComputedStyle(
+ document.documentElement
+ ).getPropertyValue('--euiBottomBarOffset');
+ expect(clearedValue.trim()).toBe('');
+ });
+
+ test('does not set css variable when affordForDisplacement is false', () => {
+ render(
+ Content
+ );
+
+ const cssVarValue = getComputedStyle(
+ document.documentElement
+ ).getPropertyValue('--euiBottomBarOffset');
+ expect(cssVarValue.trim()).toBe('');
+ });
+
+ test('does not set css variable when usePortal is false', () => {
+ render(
+
+ Content
+
+ );
+
+ const cssVarValue = getComputedStyle(
+ document.documentElement
+ ).getPropertyValue('--euiBottomBarOffset');
+ expect(cssVarValue.trim()).toBe('');
+ });
+
+ test('does not set css variable for non-fixed positions', () => {
+ const { unmount: unmountSticky } = render(
+ Content
+ );
+
+ let cssVarValue = getComputedStyle(
+ document.documentElement
+ ).getPropertyValue('--euiBottomBarOffset');
+ expect(cssVarValue.trim()).toBe('');
+
+ unmountSticky();
+
+ const { unmount: unmountStatic } = render(
+ Content
+ );
+
+ cssVarValue = getComputedStyle(document.documentElement).getPropertyValue(
+ '--euiBottomBarOffset'
+ );
+ expect(cssVarValue.trim()).toBe('');
+
+ unmountStatic();
+ });
+ });
});
diff --git a/packages/eui/src/components/bottom_bar/bottom_bar.tsx b/packages/eui/src/components/bottom_bar/bottom_bar.tsx
index 806d3742950..e0c95d954ca 100644
--- a/packages/eui/src/components/bottom_bar/bottom_bar.tsx
+++ b/packages/eui/src/components/bottom_bar/bottom_bar.tsx
@@ -18,6 +18,7 @@ import React, {
import {
useCombinedRefs,
useEuiMemoizedStyles,
+ useEuiThemeCSSVariables,
EuiThemeProvider,
} from '../../services';
import { EuiScreenReaderOnly } from '../accessibility';
@@ -120,6 +121,7 @@ const _EuiBottomBar = forwardRef<
ref
) => {
const styles = useEuiMemoizedStyles(euiBottomBarStyles);
+ const { setGlobalCSSVariables } = useEuiThemeCSSVariables();
// Force some props if `fixed` position, but not if the user has supplied these
affordForDisplacement =
@@ -134,6 +136,11 @@ const _EuiBottomBar = forwardRef<
useEffect(() => {
if (affordForDisplacement && usePortal) {
document.body.style.paddingBottom = `${dimensions.height}px`;
+
+ // EUI doesn't use this css variable, but it is useful for consumers
+ setGlobalCSSVariables({
+ '--euiBottomBarOffset': `${dimensions.height}px`,
+ });
}
if (bodyClassName) {
@@ -143,13 +150,22 @@ const _EuiBottomBar = forwardRef<
return () => {
if (affordForDisplacement && usePortal) {
document.body.style.paddingBottom = '';
+ setGlobalCSSVariables({
+ '--euiBottomBarOffset': null,
+ });
}
if (bodyClassName) {
document.body.classList.remove(bodyClassName);
}
};
- }, [affordForDisplacement, usePortal, dimensions, bodyClassName]);
+ }, [
+ affordForDisplacement,
+ usePortal,
+ dimensions,
+ bodyClassName,
+ setGlobalCSSVariables,
+ ]);
const classes = classNames(
'euiBottomBar',
diff --git a/packages/website/docs/components/containers/bottom-bar.mdx b/packages/website/docs/components/containers/bottom-bar.mdx
index aa398ad02aa..88d4cc838c0 100644
--- a/packages/website/docs/components/containers/bottom-bar.mdx
+++ b/packages/website/docs/components/containers/bottom-bar.mdx
@@ -209,6 +209,21 @@ export default () => {
};
```
+### Relative positioning
+
+When `affordForDisplacement` is enabled and the bottom bar is positioned as `fixed`, **EuiBottomBar** automatically sets a global CSS variable that you can use to position other fixed elements in your application relative to the bottom bar's height:
+
+- `--euiBottomBarOffset` (Set when `affordForDisplacement={true}` and `position="fixed"`)
+
+This variable is automatically updated when the bottom bar resizes and is removed when the bottom bar is closed.
+
+```css
+.custom-fixed-footer {
+ /* Adjust footer position when bottom bar is visible */
+ padding-bottom: var(--euiBottomBarOffset, 0);
+}
+```
+
## Props
import docgen from '@elastic/eui-docgen/dist/components/bottom_bar';