@@ -184,15 +188,26 @@ export const HeaderExample = {
text: (
<>
- Most consumer need a header that does not scroll way with the page
- contents. You can apply this display by changing{' '}
- position to fixed. It will
- also add the appropriate padding to the window body by applying a
- class.
+ Most consumers need a header that does not scroll away with the page
+ contents. You can apply this display by applying the property{' '}
+ {'position="fixed"'}. This will
+ also add a class of .euiBody--headerIsFixed to
+ the window body.
+
+
+ You will then need to apply your own padding to this body class to
+ afford for the header height. EUI supplies a helper mixin that also
+ accounts for this height in flyouts and the collapsible nav. Simply
+ add{' '}
+ @mixin euiHeaderAffordForFixed;{' '}
+ anywhere in your SASS.
>
),
- snippet: '',
+ snippet: [
+ '',
+ '@mixin euiHeaderAffordForFixed;',
+ ],
demo: ,
},
{
@@ -282,5 +297,34 @@ export const HeaderExample = {
snippet: headerLinksSnippet,
demo: ,
},
+ {
+ title: 'Stacked headers',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: headerStackedSource,
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: headerStackedHtml,
+ },
+ ],
+ text: (
+
+ Stacking multiple headers provide a great way to separate global
+ navigation concerns. However, the{' '}
+ {'position="fixed"'} option will not
+ be aware of the number of headers. Therefore, if you do need fixed and
+ stacked headers, you will need to apply the helper mixin and pass in
+ the correct height to afford for.
+
+ ),
+ snippet: [
+ `
+`,
+ '@include euiHeaderAffordForFixed($euiHeaderHeightCompensation * 2);',
+ ],
+ demo: ,
+ },
],
};
diff --git a/src-docs/src/views/header/header_position.js b/src-docs/src/views/header/header_position.js
index 591658d7058..12ae9828b6d 100644
--- a/src-docs/src/views/header/header_position.js
+++ b/src-docs/src/views/header/header_position.js
@@ -4,6 +4,7 @@ import {
EuiHeader,
EuiHeaderLogo,
EuiSwitch,
+ EuiSpacer,
} from '../../../../src/components';
export default () => {
@@ -11,28 +12,20 @@ export default () => {
const sections = [
{
- items: [
- ,
- ],
- borders: 'none',
- },
- {
- items: [
-
- setPosition(e.target.checked ? 'fixed' : 'static')}
- />
-
,
- ],
- borders: 'none',
+ items: [Kibana],
+ borders: 'right',
},
];
- return ;
+ return (
+ <>
+ setPosition(e.target.checked ? 'fixed' : 'static')}
+ />
+
+
+ >
+ );
};
diff --git a/src-docs/src/views/header/header_sections.js b/src-docs/src/views/header/header_sections.js
index 63a048d1dd3..caba40b6fb2 100644
--- a/src-docs/src/views/header/header_sections.js
+++ b/src-docs/src/views/header/header_sections.js
@@ -72,6 +72,9 @@ export default () => {
items: [renderLogo, ],
borders: 'right',
breadcrumbs: breadcrumbs,
+ breadcrumbProps: {
+ 'aria-label': 'Header sections breadcrumbs',
+ },
},
{
items: [renderSearch, ],
diff --git a/src-docs/src/views/header/header_stacked.tsx b/src-docs/src/views/header/header_stacked.tsx
new file mode 100644
index 00000000000..468352e8c1c
--- /dev/null
+++ b/src-docs/src/views/header/header_stacked.tsx
@@ -0,0 +1,90 @@
+import React, { useState, useEffect } from 'react';
+
+import {
+ EuiHeader,
+ EuiHeaderLogo,
+ EuiHeaderSectionItemButton,
+} from '../../../../src/components/header';
+import { EuiSwitch } from '../../../../src/components/form';
+import { EuiSpacer } from '../../../../src/components/spacer';
+import { EuiAvatar } from '../../../../src/components/avatar';
+// @ts-ignore
+import HeaderUpdates from './header_updates';
+
+export default () => {
+ const [isFixed, setIsFixed] = useState(false);
+
+ const breadcrumbs = [
+ {
+ text: 'Management',
+ href: '#',
+ },
+ {
+ text: 'Users',
+ },
+ ];
+
+ /**
+ * Docs Note: This additional class is needed only for docs to override the usually single header
+ */
+ useEffect(() => {
+ if (isFixed) document.body.classList.add('euiBody--headerIsFixed--double');
+
+ return () => {
+ document.body.classList.remove('euiBody--headerIsFixed--double');
+ };
+ }, [isFixed]);
+
+ const headers = (
+ <>
+ Elastic,
+ ],
+ borders: 'right',
+ },
+ {
+ items: [
+
+
+ ,
+ ],
+ },
+ ]}
+ />
+
+
+ ,
+ ],
+ breadcrumbs: breadcrumbs,
+ borders: 'none',
+ },
+ {
+ items: isFixed ? [] : undefined,
+ },
+ ]}
+ />
+ >
+ );
+
+ return (
+ <>
+ setIsFixed(e.target.checked)}
+ />
+
+ {headers}
+ >
+ );
+};
diff --git a/src-docs/src/views/header/header_updates.js b/src-docs/src/views/header/header_updates.js
index 2becb81e0cc..474a1efa69d 100644
--- a/src-docs/src/views/header/header_updates.js
+++ b/src-docs/src/views/header/header_updates.js
@@ -121,18 +121,13 @@ export default () => {
);
let flyout;
- const flyoutStyle = {
- top: '49px',
- height: 'calc(100vh - 49px)',
- };
if (isFlyoutVisible) {
flyout = (
closeFlyout()}
size="s"
id="headerNewsFeed"
- aria-labelledby="flyoutSmallTitle"
- style={flyoutStyle}>
+ aria-labelledby="flyoutSmallTitle">
What's new
diff --git a/src/components/flyout/_mixins.scss b/src/components/flyout/_mixins.scss
index bd6133ea5c0..cc482a47ae4 100644
--- a/src/components/flyout/_mixins.scss
+++ b/src/components/flyout/_mixins.scss
@@ -15,10 +15,4 @@
display: flex;
flex-direction: column;
align-items: stretch;
-
- // When the EuiHeader is fixed, we need to account for it in the position of the flyout
- .euiBody--headerIsFixed & {
- top: $euiHeaderHeightCompensation;
- height: calc(100% - #{$euiHeaderHeightCompensation});
- }
}
diff --git a/src/components/header/_header.scss b/src/components/header/_header.scss
index c10f16bd14f..948e76fe187 100644
--- a/src/components/header/_header.scss
+++ b/src/components/header/_header.scss
@@ -9,7 +9,7 @@
display: flex;
justify-content: space-between;
background: $euiHeaderBackgroundColor;
- border-bottom: $euiBorderThin;
+ border-bottom: 1px solid $euiHeaderBorderColor;
&--fixed {
position: fixed;
@@ -19,10 +19,6 @@
}
}
-.euiBody--headerIsFixed {
- padding-top: $euiHeaderHeightCompensation;
-}
-
.euiBody--collapsibleNavIsOpen,
.euiBody--hasFlyout {
.euiHeader--fixed {
@@ -30,9 +26,15 @@
}
}
+.euiHeader--fixed + .euiHeader--fixed {
+ top: $euiHeaderHeightCompensation;
+}
+
.euiHeader--dark {
- // Only force reverse theme if the theme is light
@if (lightness($euiTextColor) < 50) {
- @include euiHeaderDarkTheme;
+ @include euiHeaderDarkTheme($backgroundColor: shade($euiColorDarkestShade, 28%));
+ } @else {
+ // Makes forced "dark" theme darker than the typical dark them to separate them visually
+ @include euiHeaderDarkTheme($backgroundColor: shade($euiColorLightestShade, 50%));
}
}
diff --git a/src/components/header/_mixins.scss b/src/components/header/_mixins.scss
index f22c6ffad73..54ee4c66a09 100644
--- a/src/components/header/_mixins.scss
+++ b/src/components/header/_mixins.scss
@@ -1,6 +1,6 @@
-@mixin euiHeaderDarkTheme {
- $headerBackgroundColor: shade($euiColorDarkestShade, 28%);
- background-color: $headerBackgroundColor;
+@mixin euiHeaderDarkTheme($backgroundColor) {
+ background-color: $backgroundColor;
+ border-bottom-color: lightOrDarkTheme($backgroundColor, $euiHeaderBorderColor);
.euiHeaderLogo__text,
.euiHeaderLink,
@@ -9,12 +9,12 @@
}
.euiHeaderLink-isActive {
- color: makeHighContrastColor($euiColorPrimary, $headerBackgroundColor);
+ color: makeHighContrastColor($euiColorPrimary, $backgroundColor);
}
.euiHeaderSectionItem {
&:after {
- background: $euiColorDarkShade;
+ background: lightOrDarkTheme($euiColorDarkShade, $euiColorLightestShade);
}
}
@@ -22,7 +22,7 @@
.euiHeaderLink,
.euiHeaderSectionItem__button {
&:hover {
- background: transparentize($euiColorDarkShade, .5);
+ background: transparentize(lightOrDarkTheme($euiColorDarkShade, $euiColorLightestShade), .5);
}
&:focus {
@@ -32,6 +32,6 @@
.euiHeaderNotification,
.euiHeaderSectionItemButton__notification {
- box-shadow: 0 0 0 1px $headerBackgroundColor;
+ box-shadow: 0 0 0 1px $backgroundColor;
}
}
diff --git a/src/components/header/_variables.scss b/src/components/header/_variables.scss
index 9d86cc31981..1e9c763d3ad 100644
--- a/src/components/header/_variables.scss
+++ b/src/components/header/_variables.scss
@@ -1,6 +1,7 @@
// Note - these are also used by the EuiNavDrawer (/nav_drawer) component
// Themable colors
$euiHeaderBackgroundColor: $euiColorEmptyShade !default;
+$euiHeaderBorderColor: $euiBorderColor !default;
$euiHeaderBreadcrumbColor: $euiColorDarkestShade !default;
// Layout vars
diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx
index 5ea43095bbc..9835250ef25 100644
--- a/src/components/header/header.tsx
+++ b/src/components/header/header.tsx
@@ -86,6 +86,9 @@ export type EuiHeaderProps = CommonProps &
theme?: 'default' | 'dark';
};
+// Start a counter to manage the total number of fixed headers that need the body class
+let euiHeaderFixedCounter = 0;
+
export const EuiHeader: FunctionComponent = ({
children,
className,
@@ -103,11 +106,18 @@ export const EuiHeader: FunctionComponent = ({
useEffect(() => {
if (position === 'fixed') {
+ // Increment fixed header counter for each fixed header
+ euiHeaderFixedCounter++;
document.body.classList.add('euiBody--headerIsFixed');
+
+ return () => {
+ // Both decrement the fixed counter AND then check if there are none
+ if (--euiHeaderFixedCounter === 0) {
+ // If there are none, THEN remove class
+ document.body.classList.remove('euiBody--headerIsFixed');
+ }
+ };
}
- return () => {
- document.body.classList.remove('euiBody--headerIsFixed');
- };
}, [position]);
let contents;
diff --git a/src/components/header/header_breadcrumbs/_header_breadcrumbs.scss b/src/components/header/header_breadcrumbs/_header_breadcrumbs.scss
index ba74f7475ed..bc953be7168 100644
--- a/src/components/header/header_breadcrumbs/_header_breadcrumbs.scss
+++ b/src/components/header/header_breadcrumbs/_header_breadcrumbs.scss
@@ -1,18 +1,9 @@
// Breadcrumb navigation included in the header.
-@import '../../link/mixins';
-
.euiHeaderBreadcrumbs {
- margin-left: $euiSize;
- margin-right: $euiSize;
+ margin-left: $euiSizeS;
+ margin-right: $euiSizeS;
display: flex;
align-items: center;
flex-grow: 1;
}
-
-@include euiBreakpoint('xs') {
- .euiHeaderBreadcrumbs {
- margin-left: $euiSizeS;
- margin-right: $euiSizeS;
- }
-}
diff --git a/src/global_styling/mixins/_header.scss b/src/global_styling/mixins/_header.scss
new file mode 100644
index 00000000000..d0cf770ad3a
--- /dev/null
+++ b/src/global_styling/mixins/_header.scss
@@ -0,0 +1,14 @@
+@mixin euiHeaderAffordForFixed($headerHeight: $euiHeaderHeightCompensation) {
+ // The `&` allows for grouping inside another specific body class.
+ // When not applied inside of another selector, it simply renders with the single class
+ &.euiBody--headerIsFixed {
+ padding-top: $headerHeight;
+
+ // When the EuiHeader is fixed, we need to account for it in the position of the flyout
+ .euiFlyout,
+ .euiCollapsibleNav {
+ top: $headerHeight;
+ height: calc(100% - #{$headerHeight});
+ }
+ }
+}
diff --git a/src/global_styling/mixins/_index.scss b/src/global_styling/mixins/_index.scss
index 9e464623b06..bec84abc99f 100644
--- a/src/global_styling/mixins/_index.scss
+++ b/src/global_styling/mixins/_index.scss
@@ -9,6 +9,7 @@
@import 'beta_badge';
@import 'button';
@import 'form';
+@import 'header';
@import 'loading';
@import 'panel';
@import 'popover';
diff --git a/src/global_styling/variables/_index.scss b/src/global_styling/variables/_index.scss
index 2e3a6a64fdc..46c7f8407af 100644
--- a/src/global_styling/variables/_index.scss
+++ b/src/global_styling/variables/_index.scss
@@ -6,7 +6,7 @@
// coming from when looking inside the individual component files. Any component local
// variables should be declared at the top of those documents prefixed with $componentName.
-// Import order is important. Size and color..etc are used in other variables.
+// Import order is important. Size, color, ...etc are used in other variables.
@import 'size';
@import 'colors';
@import 'animations';
diff --git a/src/themes/eui-amsterdam/global_styling/variables/_header.scss b/src/themes/eui-amsterdam/global_styling/variables/_header.scss
index 98cbd7f19ac..c823edcea56 100644
--- a/src/themes/eui-amsterdam/global_styling/variables/_header.scss
+++ b/src/themes/eui-amsterdam/global_styling/variables/_header.scss
@@ -1 +1,7 @@
+// Curated border color to fade into the shadow without looking too much like a border
+// It adds separation between the header and flyout
+$euiHeaderBorderColor: lightOrDarkTheme(shade($euiBorderColor, 3%), shade($euiColorEmptyShade, 35%));
+
+$euiHeaderHeight: $euiSizeXXL + $euiSizeS;
$euiHeaderChildSize: $euiSizeXXL;
+$euiHeaderHeightCompensation: $euiHeaderHeight;
diff --git a/src/themes/eui-amsterdam/overrides/_flyout.scss b/src/themes/eui-amsterdam/overrides/_flyout.scss
new file mode 100644
index 00000000000..6e78d2fe0ce
--- /dev/null
+++ b/src/themes/eui-amsterdam/overrides/_flyout.scss
@@ -0,0 +1,5 @@
+// Amsterdam shadows extend upwards as well, but this means flyouts shadows can overlap fixed headers.
+// The clip path ensures only the left side of the shadow is exposed.
+.euiFlyout {
+ clip-path: polygon(-10% 0, 100% 0, 100% 100%, -10% 100%);
+}
diff --git a/src/themes/eui-amsterdam/overrides/_header.scss b/src/themes/eui-amsterdam/overrides/_header.scss
index e42ef6b9a44..de2bc6c2b4a 100644
--- a/src/themes/eui-amsterdam/overrides/_header.scss
+++ b/src/themes/eui-amsterdam/overrides/_header.scss
@@ -1,6 +1,5 @@
.euiHeader {
height: $euiHeaderHeight;
- border-bottom: none;
padding-left: $euiSizeS;
padding-right: $euiSizeS;
}
@@ -11,6 +10,10 @@
}
.euiHeaderLogo {
+ @include euiBreakpoint('xs') {
+ padding: 0 $euiSizeXS;
+ }
+
padding-left: $euiSizeS;
padding-right: $euiSizeS;
min-width: $euiHeaderChildSize;
@@ -23,3 +26,7 @@
.euiHeaderBreadcrumbs {
margin-left: $euiSizeS;
}
+
+.euiHeader--default + .euiHeader--default {
+ border-top: $euiBorderThin;
+}
diff --git a/src/themes/eui-amsterdam/overrides/_index.scss b/src/themes/eui-amsterdam/overrides/_index.scss
index fed4c2dba43..3b7b0823c90 100644
--- a/src/themes/eui-amsterdam/overrides/_index.scss
+++ b/src/themes/eui-amsterdam/overrides/_index.scss
@@ -1,6 +1,7 @@
@import 'button';
@import 'button_empty';
@import 'button_group';
+@import 'flyout';
@import 'header';
@import 'image';
@import 'modal';