From 11954d833f8e2ada6d18771b2e3ad758704a500c Mon Sep 17 00:00:00 2001 From: Cell <1024@lruihao.cn> Date: Fri, 27 Mar 2026 15:37:32 +0800 Subject: [PATCH] feat(header): refactored the blur effect to backdrop-filter and added a configuration switch (#721) - Removed the old blur mixin based on filter: blur, and replaced it with backdrop-filter for a more modern frosted glass effect - Added the `params.header.blur` configuration option, allowing users to enable/disable the header blur effect - Refactored the masking interaction of the mobile menu and search, applying the blur class to the masking element instead of the body - Updated the header animation class name to distinguish it from custom animations and avoid conflicts with Animate.css - Adjusted responsive breakpoints, changing `xs` to `sm` to improve mobile display - Optimized the header background color and the background color of the fixed buttons --- .github/copilot-instructions.md | 1 - assets/css/_core/_common.scss | 2 +- assets/css/_core/_layout/_breadcrumb.scss | 4 +- assets/css/_core/_layout/_footer.scss | 2 - assets/css/_core/_layout/_header.scss | 50 +++++++++++++++++--- assets/css/_core/_mixin/_blur.scss | 6 --- assets/css/_core/_mixin/_index.scss | 1 - assets/css/_core/_mixin/_sticky-top.scss | 4 +- assets/css/_core/_mixin/_theme-vars.scss | 13 +++++ assets/css/_core/_widgets/_fixed-button.scss | 5 +- assets/css/_core/_widgets/_mask.scss | 6 +-- assets/css/_page/_index.scss | 2 - assets/css/_page/_single/_collections.scss | 1 - assets/css/_page/_single/_toc.scss | 1 - assets/css/_variables.scss | 2 +- assets/js/theme.js | 35 +++++++------- hugo.toml | 2 + layouts/_partials/layout/header.html | 4 +- 18 files changed, 88 insertions(+), 53 deletions(-) delete mode 100644 assets/css/_core/_mixin/_blur.scss diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 838665a0..22ca4d46 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -92,7 +92,6 @@ FixIt/ ```scss @include border-radius; @include transition(all 0.2s ease); - @include blur; ``` ### 代码质量要求 diff --git a/assets/css/_core/_common.scss b/assets/css/_core/_common.scss index c92c0d65..099abbaa 100644 --- a/assets/css/_core/_common.scss +++ b/assets/css/_core/_common.scss @@ -75,7 +75,7 @@ break-after: page; } -@include media('xs', 'up') { +@include media('sm', 'up') { .d-none-desktop { display: none; } diff --git a/assets/css/_core/_layout/_breadcrumb.scss b/assets/css/_core/_layout/_breadcrumb.scss index 3ab140c8..cc7dc75d 100644 --- a/assets/css/_core/_layout/_breadcrumb.scss +++ b/assets/css/_core/_layout/_breadcrumb.scss @@ -30,7 +30,7 @@ [data-header-desktop='auto'] & { top: fixit-var(header-height); } - [data-header-desktop='auto']:has(#header-desktop.animate__fadeOutUp) & { + [data-header-desktop='auto']:has(#header-desktop.header__fadeOutUp) & { top: 0px; } } @@ -40,7 +40,7 @@ [data-header-mobile='auto'] & { top: fixit-var(header-height); } - [data-header-mobile='auto']:has(#header-mobile.animate__fadeOutUp) & { + [data-header-mobile='auto']:has(#header-mobile.header__fadeOutUp) & { top: 0px; } } diff --git a/assets/css/_core/_layout/_footer.scss b/assets/css/_core/_layout/_footer.scss index e61aaefa..53951bd7 100644 --- a/assets/css/_core/_layout/_footer.scss +++ b/assets/css/_core/_layout/_footer.scss @@ -30,8 +30,6 @@ footer { } } - @include blur; - @include media('print') { display: none !important; } diff --git a/assets/css/_core/_layout/_header.scss b/assets/css/_core/_layout/_header.scss index fc0a9032..77bee035 100644 --- a/assets/css/_core/_layout/_header.scss +++ b/assets/css/_core/_layout/_header.scss @@ -1,22 +1,61 @@ header { width: 100%; - background-color: fixit-var(header-background-color); transition: box-shadow 0.3s ease; @include z-index(fixed); + + &.blur { + backdrop-filter: saturate(1.2) blur(10px); + background-size: 4px 4px; + background-image: radial-gradient(transparent 1px, fixit-var(header-background-color) 1px); + } + + &:not(.blur) { + background-color: fixit-var(header-background-color); + } &:hover { - box-shadow: 0 0 1.5rem 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 0 1.5rem 0 rgba(0, 0, 0, 0.125); [data-theme=dark] & { - box-shadow: 0 0 1.5rem 0 rgba(255, 255, 255, 0.1); + box-shadow: 0 0 1.5rem 0 rgba(255, 255, 255, 0.125); } } + &.animate__animated.header__fadeInDown { + animation-name: #{$prefix}header-fade-in-down; + } + + &.animate__animated.header__fadeOutUp { + animation-name: #{$prefix}header-fade-out-up; + } + @include media('print') { display: none !important; } } +@keyframes #{$prefix}header-fade-in-down { + from { + transform: translate3d(0, -100%, 0); + visibility: visible; + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@keyframes #{$prefix}header-fade-out-up { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(0, -100%, 0); + } +} + .header-wrapper { display: flex; justify-content: space-between; @@ -287,7 +326,6 @@ header { } #header-mobile { - display: none; position: sticky; top: 0; min-height: fixit-var(header-height); @@ -496,8 +534,8 @@ header { } } - @include media('xs') { - display: block; + @include media('sm', 'up') { + display: none; } } diff --git a/assets/css/_core/_mixin/_blur.scss b/assets/css/_core/_mixin/_blur.scss deleted file mode 100644 index 1272fc9f..00000000 --- a/assets/css/_core/_mixin/_blur.scss +++ /dev/null @@ -1,6 +0,0 @@ -@mixin blur { - // TODO refactor blur with backdrop-filter - .blur & { - filter: blur(1.5px); - } -} diff --git a/assets/css/_core/_mixin/_index.scss b/assets/css/_core/_mixin/_index.scss index dc9fcf97..f777bd7b 100644 --- a/assets/css/_core/_mixin/_index.scss +++ b/assets/css/_core/_mixin/_index.scss @@ -1,4 +1,3 @@ -@import 'blur'; @import 'border-radius'; @import 'box-shadow'; @import 'compatibility'; diff --git a/assets/css/_core/_mixin/_sticky-top.scss b/assets/css/_core/_mixin/_sticky-top.scss index d2835fff..41d928e5 100644 --- a/assets/css/_core/_mixin/_sticky-top.scss +++ b/assets/css/_core/_mixin/_sticky-top.scss @@ -21,7 +21,7 @@ top: $sticky-top-bar-offset; } - [data-header-desktop='auto']:has(#header-desktop.animate__fadeOutUp) & { + [data-header-desktop='auto']:has(#header-desktop.header__fadeOutUp) & { // Once the auto header is hidden, remove the extra top offset. top: $base-top-offset; } @@ -34,7 +34,7 @@ top: $sticky-top-bar-offset; } - [data-header-mobile='auto']:has(#header-mobile.animate__fadeOutUp) & { + [data-header-mobile='auto']:has(#header-mobile.header__fadeOutUp) & { // Once the auto header is hidden, remove the extra top offset. top: $base-top-offset; } diff --git a/assets/css/_core/_mixin/_theme-vars.scss b/assets/css/_core/_mixin/_theme-vars.scss index dd8c5431..4197635f 100644 --- a/assets/css/_core/_mixin/_theme-vars.scss +++ b/assets/css/_core/_mixin/_theme-vars.scss @@ -57,6 +57,19 @@ } } +/// Define a single theme variable (generate definitions for both light and dark) +/// @param {String} $name - CSS variable name (without prefix) +/// @param {Map} $vars -- Variable map, key is CSS variable name, value is Map(light: $light-val, dark: $dark-val) +/// @example +/// @include define-theme-var(header-background-color, (light: rgba($header-background-color, 0.7), dark: rgba($header-background-color-dark, 0.5))); +@mixin define-theme-var($name, $vars) { + #{$rootPrefix}#{$name}: #{map-get($vars, light)}; + + [data-theme=dark] & { + #{$rootPrefix}#{$name}: #{map-get($vars, dark)}; + } +} + /// Define dual-theme variables (generate definitions for both light and dark simultaneously) /// @param {Map} $vars - Variable map, key is CSS variable name, value is Map(light: $light-val, dark: $dark-val) /// @example diff --git a/assets/css/_core/_widgets/_fixed-button.scss b/assets/css/_core/_widgets/_fixed-button.scss index b9064614..cbd27b11 100644 --- a/assets/css/_core/_widgets/_fixed-button.scss +++ b/assets/css/_core/_widgets/_fixed-button.scss @@ -13,13 +13,12 @@ line-height: 1.5rem; padding: 0.5rem 0.625rem; color: fixit-var(secondary); - background-color: darken($header-background-color, 3%); + background-color: #f1f1f1; transition: color 0.4s ease; @include border-radius(full); - @include blur; [data-theme=dark] & { - background-color: darken($header-background-color-dark, 3%); + background-color: #1e1e1f; } &:hover, diff --git a/assets/css/_core/_widgets/_mask.scss b/assets/css/_core/_widgets/_mask.scss index a37d63a6..b51784b3 100644 --- a/assets/css/_core/_widgets/_mask.scss +++ b/assets/css/_core/_widgets/_mask.scss @@ -6,11 +6,11 @@ left: 0; width: 100%; height: 100%; - background-color: rgba(0, 0, 0, 0); @include z-index(hide); - .blur & { - @include z-index(fixed, -1); + &.blur { background-color: rgba(0, 0, 0, 0.25); + backdrop-filter: blur(2px); + @include z-index(fixed, -1); } } diff --git a/assets/css/_page/_index.scss b/assets/css/_page/_index.scss index 8a207ed7..8d0c3a2d 100644 --- a/assets/css/_page/_index.scss +++ b/assets/css/_page/_index.scss @@ -33,8 +33,6 @@ } } - @include blur; - @include media('print') { width: 100% !important; padding-top: 0 !important; diff --git a/assets/css/_page/_single/_collections.scss b/assets/css/_page/_single/_collections.scss index 9ef37871..b26f3cfd 100644 --- a/assets/css/_page/_single/_collections.scss +++ b/assets/css/_page/_single/_collections.scss @@ -92,7 +92,6 @@ box-sizing: border-box; margin-bottom: 8rem; @include overflow-wrap(break-word); - @include blur; @include sticky-top-offset; @include media('print') { diff --git a/assets/css/_page/_single/_toc.scss b/assets/css/_page/_single/_toc.scss index 7baefd25..ad9d57b8 100644 --- a/assets/css/_page/_single/_toc.scss +++ b/assets/css/_page/_single/_toc.scss @@ -93,7 +93,6 @@ visibility: hidden; margin-bottom: 8rem; @include overflow-wrap(break-word); - @include blur; @include sticky-top-offset; .toc-title { diff --git a/assets/css/_variables.scss b/assets/css/_variables.scss index ab6eeae1..78cf4ec0 100644 --- a/assets/css/_variables.scss +++ b/assets/css/_variables.scss @@ -65,7 +65,7 @@ $selection-color-dark: rgba(50, 112, 194, 0.4) !default; $header-height: 3.5rem !default; // Color of the header background -$header-background-color: #f8f8f8 !default; +$header-background-color: #f7f7f8 !default; $header-background-color-dark: #252627 !default; // Font style of the header title diff --git a/assets/js/theme.js b/assets/js/theme.js index 77206e9c..d7ac6b84 100644 --- a/assets/js/theme.js +++ b/assets/js/theme.js @@ -80,11 +80,12 @@ class FixIt { initMenuMobile() { const $menuToggleMobile = document.getElementById('menu-toggle-mobile'); const $menuMobile = document.getElementById('menu-mobile'); + const $mask = document.getElementById('mask'); $menuToggleMobile.addEventListener('click', (event) => { - document.body.classList.toggle('blur'); + $mask.classList.toggle('blur'); $menuToggleMobile.classList.toggle('active'); $menuMobile.classList.toggle('active'); - this.disableScrollEvent = document.body.classList.contains('blur'); + this.disableScrollEvent = $mask.classList.contains('blur'); }, false); this._menuMobileOnClickMask = this._menuMobileOnClickMask || (() => { $menuToggleMobile.classList.remove('active'); @@ -175,6 +176,7 @@ class FixIt { const $searchLoading = document.getElementById(`search-loading-${suffix}`); const $searchClear = document.getElementById(`search-clear-${suffix}`); const $searchCancel = document.getElementById('search-cancel-mobile'); + const $mask = document.getElementById('mask'); // goto the PostChat panel rather than search results if (searchConfig.type === 'post-chat' && window.postChatUser) { @@ -194,12 +196,12 @@ class FixIt { this._searchMobileOnce = true; $searchInput.addEventListener('focus', () => { this.disableScrollEvent = true; - document.body.classList.add('blur'); + $mask.classList.add('blur'); $header.classList.add('open'); }, false); $searchCancel.addEventListener('click', () => { this.disableScrollEvent = false; - document.body.classList.remove('blur'); + $mask.classList.remove('blur'); document.getElementById('menu-toggle-mobile').classList.remove('active'); document.getElementById('menu-mobile').classList.remove('active'); this._resetSearchUI($header, $searchLoading, $searchClear, this._searchMobile); @@ -215,7 +217,7 @@ class FixIt { } else { this._searchDesktopOnce = true; $searchToggle.addEventListener('click', () => { - document.body.classList.add('blur'); + $mask.classList.add('blur'); $header.classList.add('open'); $searchInput.focus(); this.disableScrollEvent = true; @@ -387,7 +389,7 @@ class FixIt { } ); autosearch.on('autocomplete:selected', (_event, suggestion, _dataset, _context) => { - document.getElementById('mask')?.click(); + $mask?.click(); window.location.assign(suggestion.uri); }); if (_isMobile) { @@ -852,11 +854,7 @@ class FixIt { const $toc = document.getElementById('toc-auto'); $toc.style.visibility = 'visible'; animateCSS($toc, ['animate__fadeIn', 'animate__faster'], true); - // document.querySelector('.fi-container').addEventListener('resize', () => { - // $toc.style.marginBottom = `${document.querySelector('.fi-container').clientHeight - document.querySelector('.post-footer').offsetTop}px`; - // }); this._tocOnScroll = this._tocOnScroll || (() => { - // $toc.style.marginBottom = `${document.querySelector('.fi-container').clientHeight - document.querySelector('.post-footer').offsetTop}px`; this._updateTocActiveState($tocCore, $headingElements, INDEX_OFFSET); }); this._tocOnScroll(); @@ -1540,14 +1538,13 @@ class FixIt { // header animation forEach($headers, ($header) => { if (scroll > ACCURACY) { - $header.classList.remove('animate__fadeInDown'); - animateCSS($header, ['animate__fadeOutUp'], true); - $mask.click(); + $header.classList.remove('header__fadeInDown'); + animateCSS($header, ['header__fadeOutUp'], true); } else if (scroll < -ACCURACY) { - $header.classList.remove('animate__fadeOutUp'); - animateCSS($header, ['animate__fadeInDown'], true); - $mask.click(); + $header.classList.remove('header__fadeOutUp'); + animateCSS($header, ['header__fadeInDown'], true); } + $mask.click(); }); const contentHeight = document.body.scrollHeight - window.innerHeight; const scrollPercent = Math.max(Math.min(100 * Math.max(this.newScrollTop, 0) / contentHeight, 100), 0); @@ -1603,13 +1600,13 @@ class FixIt { } onClickMask() { - document.getElementById('mask').addEventListener('click', () => { - if (!document.body.classList.contains('blur')) return; + document.getElementById('mask').addEventListener('click', (e) => { + if (!e.target.classList.contains('blur')) return; for (let event of this.clickMaskEventSet) { event(); } this.disableScrollEvent = false; - document.body.classList.remove('blur'); + e.target.classList.remove('blur'); }, false); } diff --git a/hugo.toml b/hugo.toml index a22277fb..33087fd5 100644 --- a/hugo.toml +++ b/hugo.toml @@ -553,6 +553,8 @@ cx = "" desktopMode = "sticky" # FixIt 0.2.13 | CHANGED mobile header mode ["sticky", "normal", "auto"] mobileMode = "auto" +# FixIt 1.0.0 | NEW whether to enable header blur effect +blur = true # Header title config [params.header.title] diff --git a/layouts/_partials/layout/header.html b/layouts/_partials/layout/header.html index a7596d50..84f344a7 100644 --- a/layouts/_partials/layout/header.html +++ b/layouts/_partials/layout/header.html @@ -2,7 +2,7 @@ {{- $relLangURL := relLangURL "" -}} {{- /* Desktop header */ -}} -
+
{{- /* Mobile header */ -}} -
+