Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix heading padding FOUC (option 3 with modifications: sticky header) #1939

Merged
merged 24 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/_markbind/layouts/default.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<include src="headers/header.md" />
{% include "_markbind/layouts/headers/header.md" %}

<div id="flex-body">
<div id="content-wrapper" class="fixed-header-padding">
Expand Down
2 changes: 1 addition & 1 deletion docs/_markbind/layouts/devGuide.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<include src="headers/header.md" />
{% include "_markbind/layouts/headers/header.md" %}

<div id="flex-body">
<nav id="site-nav" class="fixed-header-padding">
Expand Down
2 changes: 1 addition & 1 deletion docs/_markbind/layouts/headers/header.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<link rel="stylesheet" href="{{baseUrl}}/css/main.css">
</head-bottom>

<header fixed>
<header {{ 'sticky' if isSticky else 'fixed' }}>
<navbar type="dark">
<a slot="brand" href="{{baseUrl}}/index.html" title="Home" class="navbar-brand"><img src="{{baseUrl}}/images/logo-darkbackground.svg" height="20"></a>
<li><a highlight-on="exact" href="{{baseUrl}}/index.html" class="nav-link">HOME</a></li>
Expand Down
6 changes: 6 additions & 0 deletions docs/_markbind/layouts/stickyUserGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!--
This layout is only for demoing header[sticky]
-->

{% set isSticky = true %}
{% include "_markbind/layouts/userGuide.md" %}
9 changes: 8 additions & 1 deletion docs/_markbind/layouts/userGuide.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
<include src="headers/header.md" />
{% if isSticky %}
<div class="w-100 p-1 bg-warning text-center">

**This is a "non-sticky" announcement that does not follow the header as you scroll down!.**
</div>
{% endif %}

{% include "_markbind/layouts/headers/header.md" %}

<div id="flex-body">
<nav id="site-nav" class="fixed-header-padding">
Expand Down
4 changes: 2 additions & 2 deletions docs/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mark {
margin: 0 auto;
min-width: 0;
max-width: 1000px;
padding: 0.8rem 20px 0 20px;
padding: 0 20px;
margin-top: 0.8rem;
transition: 0.4s;
-webkit-transition: 0.4s;
}
Expand All @@ -66,7 +67,6 @@ mark {
position: sticky;
top: 0;
flex: 0 0 auto;
padding-top: 1rem;
max-width: 300px;
width: 300px;
}
Expand Down
1 change: 1 addition & 0 deletions docs/site.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"src": [
"index.md",
"userGuide/index.md",
"userGuide/stickyDemo.md",
"userGuide/fullSyntaxReference.md",
"userGuide/syntaxCheatSheet.md",
"userGuide/readerFacingFeatures.md",
Expand Down
14 changes: 14 additions & 0 deletions docs/userGuide/stickyDemo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<frontmatter>
title: "User Guide: Sticky Demo"
layout: stickyUserGuide.md
pageNav: "default"
</frontmatter>

<box>

This page is the same as [*Tweaking the Page Structure*](./tweakingThePageStructure.md), except, it uses a sticky header for demoing the tradeoffs mentioned [below](#option-2-sticky-header).
</box>

<include src="tweakingThePageStructure.md" omitFrontmatter>
<variable name="isSticky">true</variable>
</include>
41 changes: 33 additions & 8 deletions docs/userGuide/tweakingThePageStructure.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ Next, edit the layout file to your liking, and add the `{% raw %}{{ content }}{%
<div id="layout-code-snippet">


```html {highlight-lines="{{ highlightLines or "1-4,7,31,36-44[:],49,54,67-71" }}"}
```html {highlight-lines="{{ highlightLines or "1-4,7[8:13],30[19:47],35-43[:],46[26:54],48,50[19:47],53,66-70" }}"}
{% raw %}<head-bottom>
<!-- Use head-top and head-bottom tags to insert content into the HTML <head> tag -->
<link rel="stylesheet" href="{{baseUrl}}/css/main.css">
</head-bottom>

<!-- Fix the header to the top while scrolling using the fixed attribute in a <header> tag -->
<!-- Create a fixed header using the fixed attribute in a <header> tag -->
<header fixed>
<navbar type="dark">
<a slot="brand" href="{{baseUrl}}/index.html" title="Home" class="navbar-brand">
Expand All @@ -70,7 +70,6 @@ Next, edit the layout file to your liking, and add the `{% raw %}{{ content }}{%
</header>

<div id="flex-body">
<!-- Push content downward when using a fixed header with the fixed-header-padding class -->
<nav id="site-nav" class="fixed-header-padding">
<div class="site-nav-top">
<div class="fw-bold mb-2" style="font-size: 1.25rem;">User Guide</div>
Expand Down Expand Up @@ -148,20 +147,46 @@ If you wish insert scripts at the bottom, before MarkBind's scripts, simply inse

---

### Fixing the header to the top
### Sticking the header to the top

Headers are commonly included inside the HTML `<header>` tag. In encouraging this, a convenient interface to implement <tooltip content="Headers that stick to the top of the page while scrolling the content">fixed headers</tooltip> surrounding the `<header>` tag is provided that ensures page anchors work correctly.
Headers are commonly included inside the html `<header>` tag. In encouraging this, 2 convenient options to implement fixed/sticky headers using the `<header>` tag are provided, which primarily ensures that the page **scrolls to anchors** correctly.

****To fix the `<header>`****
#### Option 1: Fixed header

****Steps****
1. Add the `fixed` attribute to your `<header>` element in the layout per the above example.

2. Then, to add the necessary top padding for the main content, add the `fixed-header-padding` class to **elements that should be shifted down** in accordance with the fixed header.
2. Then, to add the necessary top padding for the main content, add the `fixed-header-padding` class to **elements that should be shifted down** in accordance with the height of the fixed header.

This class should generally be added to the elements that immediately below the header. You may also refer to the default template generated by `markbind init`, or the above code example (which is used for this documentation).

<box type="warning" seamless>

As this feature does not make any assumptions of your header height, runtime height detection is performed to populate the `fixed-header-padding` class. This results in improperly formatted content on page load for a brief period.

To avoid this, the implementation here switches the `<header>` element to use `fixed` positioning, and populates the `fixed-header-padding` class's padding *only once* the user has scrolled past the top of the header. Thus, ensure your `<header>` element is the first visible element.
</box>

<box type="tip" seamless>

If you are not sure where to put the `fixed-header-padding` attribute, you may also refer to the default template for `markbind init`, which already has this setup.
To preserve screen real estate, the header is hidden on devices with a width of `< 767px` when the user scrolls down, and automatically re-shown when the page is scrolled up.
</box>

#### Option 2: Sticky header (dynamically hidden)

****Steps****
1. Simply add the `sticky` attribute to your `<header>` element.

A common issue with sticky headers is positioning the "left and right" layout page elements (e.g. your site nav and page nav) correctly when the user scrolls past the header. (i.e. they appear behind the header) To avoid this, the implementation hides your header on all devices when you scroll down, and automatically re-shows it when the page is scrolled up.

Refer [here](./stickyDemo.md) for a demo.

#### When to use which?

In general, if you don't mind the header being hidden as you scroll down (and reshown when you scroll up), you may want to use the sticky option as it does not require setting up the `fixed-header-padding` class.

The sticky option also allows you to place static elements above the header (e.g. an announcement bar in the demo above).

---

### Constructing a site navigation menu easily
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mark {
margin: 0 auto;
min-width: 0;
max-width: 1000px;
padding: 0.8rem 20px 0 20px;
padding: 0 20px;
margin-top: 0.8rem;
transition: 0.4s;
-webkit-transition: 0.4s;
}
Expand All @@ -66,7 +67,6 @@ mark {
position: sticky;
top: 0;
flex: 0 0 auto;
padding-top: 1rem;
max-width: 300px;
width: 300px;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/test/functional/test_site/stylesheets/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mark {
margin: 0 auto;
min-width: 0;
max-width: 1000px;
padding: 0.8rem 20px 0 20px;
padding: 0 20px;
margin-top: 0.8rem;
transition: 0.4s;
-webkit-transition: 0.4s;
}
Expand All @@ -66,7 +67,6 @@ mark {
position: sticky;
top: 0;
flex: 0 0 auto;
padding-top: 1rem;
max-width: 300px;
width: 300px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mark {
margin: 0 auto;
min-width: 0;
max-width: 1000px;
padding: 0.8rem 20px 0 20px;
padding: 0 20px;
margin-top: 0.8rem;
transition: 0.4s;
-webkit-transition: 0.4s;
}
Expand All @@ -66,7 +67,6 @@ mark {
position: sticky;
top: 0;
flex: 0 0 auto;
padding-top: 1rem;
max-width: 300px;
width: 300px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mark {
margin: 0 auto;
min-width: 0;
max-width: 1000px;
padding: 0.8rem 20px 0 20px;
padding: 0 20px;
margin-top: 0.8rem;
transition: 0.4s;
-webkit-transition: 0.4s;
}
Expand All @@ -66,7 +67,6 @@ mark {
position: sticky;
top: 0;
flex: 0 0 auto;
padding-top: 1rem;
max-width: 300px;
width: 300px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mark {
margin: 0 auto;
min-width: 0;
max-width: 1000px;
padding: 0.8rem 20px 0 20px;
padding: 0 20px;
margin-top: 0.8rem;
transition: 0.4s;
-webkit-transition: 0.4s;
}
Expand All @@ -66,7 +67,6 @@ mark {
position: sticky;
top: 0;
flex: 0 0 auto;
padding-top: 1rem;
max-width: 300px;
width: 300px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mark {
margin: 0 auto;
min-width: 0;
max-width: 1000px;
padding: 0.8rem 20px 0 20px;
padding: 0 20px;
margin-top: 0.8rem;
transition: 0.4s;
-webkit-transition: 0.4s;
}
Expand All @@ -66,7 +67,6 @@ mark {
position: sticky;
top: 0;
flex: 0 0 auto;
padding-top: 1rem;
max-width: 300px;
width: 300px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ mark {
margin: 0 auto;
min-width: 0;
max-width: 1000px;
padding: 0.8rem 20px 0 20px;
padding: 0 20px;
margin-top: 0.8rem;
transition: 0.4s;
-webkit-transition: 0.4s;
}
Expand All @@ -66,7 +67,6 @@ mark {
position: sticky;
top: 0;
flex: 0 0 auto;
padding-top: 1rem;
max-width: 300px;
width: 300px;
}
Expand Down
44 changes: 32 additions & 12 deletions packages/core-web/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,25 @@ function scrollToUrlAnchorHeading() {
}
}

function detectAndApplyFixedHeaderStyles() {
function detectAndApplyHeaderStyles() {
jQuery(':header').each((index, heading) => {
if (heading.id) {
jQuery(heading).removeAttr('id'); // to avoid duplicated id problem
}
});

const headerSelector = jQuery('header[fixed]');
const isFixed = headerSelector.length !== 0;
if (!isFixed) {
const fixedSelector = jQuery('header[fixed]');
const isFixed = fixedSelector.length !== 0;
const stickySelector = jQuery('header[sticky]');
const isSticky = stickySelector.length !== 0;
const headerSelector = isSticky ? stickySelector : fixedSelector;
if (!(isSticky || isFixed)) {
return;
}

let isHidden = false;
window.addEventListener('resize', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a heads up, I think will have to update the fixed-header-padding height one more time during resize.

I've the "merged" this fix from the other PR into the resizeObserver changes below

if (window.innerWidth > 767 && headerSelector.hasClass('hide-header')) {
if (window.innerWidth > 767 && isHidden) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a heads up, I think will have to update the fixed-header-padding height one more time during resize.

Currently there will be a large padding if the header reappears during resize:

MarkBind.-.User.Guide_.Presentational.Components.-.Google.Chrome.2022-05-29.00-48-02.mp4

headerSelector.removeClass('hide-header');
headerSelector.css('overflow', '');
}
Expand All @@ -47,23 +51,38 @@ function detectAndApplyFixedHeaderStyles() {
};

const toggleHeaderOverflow = () => {
const headerMaxHeight = headerSelector.css('max-height');
// reset overflow when header shows again to allow content
// in the header such as search dropdown etc. to overflow
if (headerMaxHeight === '100%') {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most of the changes here (resizeObserver, this line, isHidden) are necessary due to switching to transform translate for hiding the header (height change dosen't work with position: sticky), which should be simpler (I think) to deal with too.

if (!isHidden) {
headerSelector.css('overflow', '');
updateHeaderHeight();
}
};

if (isFixed) {
/*
Dynamically fixed to avoid FOUC from the --fixed-header-padding style detection.
See https://www.w3schools.com/howto/howto_js_sticky_header.asp.
*/
const dynamicFixedHeaderListener = () => {
if (window.scrollY > headerSelector[0].offsetTop) {
document.documentElement.style.setProperty('--fixed-header-padding', 'var(--header-height)');
document.documentElement.style.setProperty('--fixed-header-position', 'fixed');
window.removeEventListener('scroll', dynamicFixedHeaderListener);
}
};
window.addEventListener('scroll', dynamicFixedHeaderListener);
}

let lastOffset = 0;
let lastHash = window.location.hash;
const toggleHeaderOnScroll = () => {
// prevent toggling of header on desktop site
if (window.innerWidth > 767) { return; }
// prevent toggling of header on desktop site with a fixed header
if (isFixed && window.innerWidth > 767) { return; }

if (lastHash !== window.location.hash) {
lastHash = window.location.hash;
isHidden = true;
headerSelector.removeClass('hide-header');
return;
}
Expand All @@ -81,17 +100,18 @@ function detectAndApplyFixedHeaderStyles() {
return;
}

isHidden = true;
headerSelector.addClass('hide-header');
} else {
isHidden = false;
headerSelector.removeClass('hide-header');
}
lastOffset = currentOffset;
};

const resizeObserver = new ResizeObserver(() => {
const headerMaxHeight = headerSelector.css('max-height');
// hide header overflow when user scrolls to support transition effect
if (headerMaxHeight !== '100%') {
if (isHidden) {
headerSelector.css('overflow', 'hidden');
return;
}
Expand Down Expand Up @@ -132,7 +152,7 @@ function restoreStyleTags() {
function executeAfterMountedRoutines() {
restoreStyleTags();
scrollToUrlAnchorHeading();
detectAndApplyFixedHeaderStyles();
detectAndApplyHeaderStyles();
}

window.handleSiteNavClick = function (elem, useAnchor = true) {
Expand Down
Loading