diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 22ca4d46..df814902 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -66,7 +66,7 @@ FixIt/ ```scss $global-font-family: system-ui, sans-serif; - $code-background-color: #f6f8fa; + $code-background-color: #f4f6f8; $header-height: 3.5rem; ``` diff --git a/apps/test/content/posts/mathjax-test.md b/apps/test/content/posts/mathjax-test.md index b4fc8b12..40c7ced0 100644 --- a/apps/test/content/posts/mathjax-test.md +++ b/apps/test/content/posts/mathjax-test.md @@ -128,7 +128,7 @@ $$ ### Dark Mode Adaptation -Use `.auto-dark-mode` class to automatically adapt to dark mode by inverting the color and hue: +Use `.auto-dark-mode` class or `auto-dark` shortcode to automatically adapt to dark mode by inverting the color and hue: For inline formula: @@ -137,9 +137,9 @@ $\bbox[border: solid .4pt magenta, pink]{x^2=4}$ For formula blocks: -{{< style "[data-theme=dark] & { filter: invert(1) hue-rotate(0.5turn); }" >}} +{{< auto-dark >}} $$ \bbox[border: solid .4pt magenta, pink]{x^2=4} $$ {.auto-dark-mode} -{{< /style >}} +{{< /auto-dark >}} diff --git a/assets/css/_core/_base.scss b/assets/css/_core/_base.scss index 7d3d4839..39e97e8f 100644 --- a/assets/css/_core/_base.scss +++ b/assets/css/_core/_base.scss @@ -9,6 +9,8 @@ html { width: 100%; scroll-behavior: smooth; overflow: auto; + container-type: scroll-state; + container-name: scroller; @include media('reduce-motion') { scroll-behavior: auto; diff --git a/assets/css/_core/_common.scss b/assets/css/_core/_common.scss index 099abbaa..656289c4 100644 --- a/assets/css/_core/_common.scss +++ b/assets/css/_core/_common.scss @@ -61,7 +61,7 @@ // auto adapt to dark mode by inverting the color and hue .auto-dark-mode { - [data-theme=dark] & { + @include dark-mode { filter: invert(1) hue-rotate(0.5turn); } } diff --git a/assets/css/_core/_layout/_breadcrumb.scss b/assets/css/_core/_layout/_breadcrumb.scss index cc7dc75d..f0330a05 100644 --- a/assets/css/_core/_layout/_breadcrumb.scss +++ b/assets/css/_core/_layout/_breadcrumb.scss @@ -69,11 +69,11 @@ content: attr(data-separator); display: inline-block; padding-inline: 0.5rem; - color: fixit-var(global-placeholder-color); + color: fixit-var(global-border-color); } &.active { - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } } } diff --git a/assets/css/_core/_layout/_footer.scss b/assets/css/_core/_layout/_footer.scss index 53951bd7..b27af482 100644 --- a/assets/css/_core/_layout/_footer.scss +++ b/assets/css/_core/_layout/_footer.scss @@ -19,8 +19,16 @@ footer { margin-right: 0.25rem; } + .hugo-icon, + .fixit-icon, .animate-icon { - color: #dc143c; + @include dark-mode { + filter: invert(1) hue-rotate(0.5turn) opacity(0.6); + } + } + + .animate-icon { + color: light-dark(#e01846, #ff5f7a); animation: icon-animate 1.33s ease-in-out infinite; } } diff --git a/assets/css/_core/_layout/_header.scss b/assets/css/_core/_layout/_header.scss index 77bee035..be468e2c 100644 --- a/assets/css/_core/_layout/_header.scss +++ b/assets/css/_core/_layout/_header.scss @@ -1,23 +1,25 @@ +$header-box-shadow: 0 0 1.5rem 0 light-dark(rgba(0, 0, 0, 0.125), rgba(255, 255, 255, 0.125)); + header { width: 100%; transition: box-shadow 0.3s ease; + background-color: fixit-var(header-background-color); @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.125); + box-shadow: $header-box-shadow; + } - [data-theme=dark] & { - box-shadow: 0 0 1.5rem 0 rgba(255, 255, 255, 0.125); + @include scroll-state-query('scroller scroll-state(scrollable: top)') { + box-shadow: $header-box-shadow; + } + + &.blur { + @include scroll-state-query('scroller scroll-state(scrollable: top)', '&') { + background-color: transparent; + backdrop-filter: saturate(1.2) blur(10px); + background-size: 4px 4px; + background-image: radial-gradient(transparent 1px, fixit-var(header-background-color) 1px); } } @@ -191,10 +193,8 @@ header { padding: 0 2rem 0 2rem; } - @include placeholder; - .search-button { - color: fixit-var(global-placeholder-color); + color: fixit-var(global-font-tertiary-color); } .search-clear:hover { @@ -204,14 +204,28 @@ header { .search-toggle:hover { cursor: default; } + + @include placeholder; } } .theme-switch { i { - transform: rotate(225deg); - transition: all 0.4s ease; + display: inline-block; + transform: rotate(225deg) scale(1); + transform-origin: center; + @include theme-switch-mode('auto'); + @include theme-switch-mode('light', '\f185'); + @include theme-switch-mode('dark', '\f186'); + + @include media('reduce-motion') { + animation: none !important; + } } + + @include theme-switch-keyframes('auto', 180deg, 0.9, 0.7, 60%, 245deg, 1.08); + @include theme-switch-keyframes('light', 140deg, 0.82, 0.55, 55%, 250deg, 1.12); + @include theme-switch-keyframes('dark', 310deg, 0.86, 0.55, 55%, 205deg, 1.08); } #header-desktop { @@ -248,18 +262,12 @@ header { display: flex; flex-direction: row; flex-wrap: nowrap; - float: right; > .menu-item { &:hover .sub-menu { display: block; transform: translateX(-0.5rem); } - &.theme-switch { - &:hover i { - transform: rotate(45deg); - } - } &.language-switch:hover .sub-menu { transform: translateX(calc(1.75rem - 100%)); } @@ -270,9 +278,9 @@ header { &.delimiter { width: 0; - margin: 0.125rem 0.25rem; + margin: 18.5px 0.5rem; padding: 0; - border-left: 1px dashed fixit-var(global-border-color-weight); + border-left: 1.5px solid fixit-var(global-border-color); } } @@ -286,13 +294,10 @@ header { background-color: fixit-var(header-background-color); max-height: 60vh; overflow-y: auto; - @include border-radius; + overscroll-behavior: contain; transition: box-shadow 0.3s ease; - box-shadow: 0 0 1.5rem 0 rgba(0, 0, 0, 0.1); - - [data-theme=dark] & { - box-shadow: 0 0 1.5rem 0 rgba(255, 255, 255, 0.1); - } + @include border-radius; + box-shadow: 0 0 1.5rem 0 light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); .menu-item { height: fixit-var(submenu-height); @@ -387,13 +392,10 @@ header { border-top: 2px solid fixit-var(global-border-color-light); display: none; overflow-y: auto; + overscroll-behavior: contain; max-height: 70vh; - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); @include scrollbar-width(none, 0); - - [data-theme=dark] & { - box-shadow: 0 0.125rem 0.25rem rgba(255, 255, 255, 0.1); - } + box-shadow: 0 0.125rem 0.25rem light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); .search-wrapper { display: flex; @@ -438,11 +440,7 @@ header { padding: 0 1rem; &:not(.menu-system):hover { - background-color: #f2f2f3; - - [data-theme=dark] & { - background-color: #535355; - } + background-color: light-dark(#f2f2f3, #535355); } .menu-link { @@ -501,11 +499,7 @@ header { } .menu-item:hover { - background-color: #e7e7ea; - - [data-theme=dark] & { - background-color: #5d5d5f; - } + background-color: light-dark(#e7e7ea, #5d5d5f); } } @@ -544,10 +538,7 @@ header { top: fixit-var(header-height); box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); @include z-index(fixed); - - [data-theme=dark] & { - box-shadow: 0 0.125rem 0.25rem rgba(255, 255, 255, 0.1); - } + box-shadow: 0 0.125rem 0.25rem light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); &.desktop { right: 2rem; @@ -569,27 +560,34 @@ header { .suggestions { overflow-y: auto; - // 41px is the height of the search-footer - max-height: calc(100dvh - #{fixit-var(header-height)} - 41px); + overscroll-behavior: contain; + // 40px is the height of the search-footer + max-height: calc(100dvh - #{fixit-var(header-height)} - 40px); .suggestion { padding: 0.75rem 1rem; - .suggestion-title { - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 1rem; - font-weight: bold; - max-width: 75%; - } + > :first-child { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.25rem; + margin-bottom: 0.5rem; - .suggestion-date { - font-size: 0.875rem; - float: right; - text-align: right; - color: fixit-var(secondary); + > a { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 1rem; + font-weight: bold; + } + + .suggestion-date { + font-size: 0.875rem; + flex-shrink: 0; + color: fixit-var(global-font-tertiary-color); + } } .suggestion-context { @@ -600,7 +598,7 @@ header { line-clamp: 2; overflow: hidden; text-overflow: ellipsis; - color: fixit-var(global-placeholder-color); + color: fixit-var(global-font-secondary-color); @include overflow-wrap(break-word); } @@ -610,11 +608,7 @@ header { } &.cursor { - background: darken($code-background-color, 5%); - - [data-theme=dark] & { - background: lighten($code-background-color-dark, 5%); - } + background-color: fixit-var(code-block-background-color); } &:hover { @@ -628,24 +622,16 @@ header { text-align: center; .search-query { - font-weight: bold; - - [data-theme=dark] & { - color: #ddd; - } + @include bold-dark; } } .search-footer { padding: 0.5rem 1rem; - float: right; - font-size: 0.8rem; - color: fixit-var(secondary); + font-size: 0.875rem; + text-align: right; + color: fixit-var(global-font-secondary-color); @include link(false, false); - - a { - font-size: 1rem; - } } } } diff --git a/assets/css/_core/_layout/_index.scss b/assets/css/_core/_layout/_index.scss index 29c4e29e..2ad60680 100644 --- a/assets/css/_core/_layout/_index.scss +++ b/assets/css/_core/_layout/_index.scss @@ -22,11 +22,11 @@ } } - &.fi-container { + &.#{$prefix}container { display: flex; align-items: flex-start; justify-content: center; - padding-inline: 1rem; + padding-inline: 0.5rem; &[data-reverse] { flex-direction: row-reverse; diff --git a/assets/css/_core/_mixin/_bold-dark.scss b/assets/css/_core/_mixin/_bold-dark.scss new file mode 100644 index 00000000..9e67c490 --- /dev/null +++ b/assets/css/_core/_mixin/_bold-dark.scss @@ -0,0 +1,10 @@ +/// Make text bold and apply an extra text color in dark mode. +/// The light theme keeps its original inherited color. +/// @param {Color} $color - Bold text color used in dark mode +@mixin bold-dark($color: #ddd) { + font-weight: bold; + + @include dark-mode { + color: $color; + } +} diff --git a/assets/css/_core/_mixin/_compatibility.scss b/assets/css/_core/_mixin/_compatibility.scss index a535643b..28725aa5 100644 --- a/assets/css/_core/_mixin/_compatibility.scss +++ b/assets/css/_core/_mixin/_compatibility.scss @@ -1,4 +1,4 @@ -@mixin placeholder($color: fixit-var(global-placeholder-color)) { +@mixin placeholder($color: fixit-var(global-font-tertiary-color)) { input::-webkit-input-placeholder { color: $color; } @@ -31,3 +31,33 @@ -moz-user-select: $value !important; user-select: $value !important; } + +/// Apply styles only when scroll-state container queries are supported. +/// Unsupported browsers skip the container branch, and can optionally reuse +/// the same styles through a fallback selector. +/// @param {String} $container-query - Full container query string +/// @param {String|null} $fallback-selector - Optional selector used as a fallback +/// @example +/// @include scroll-state-query('scroller scroll-state(scrollable: top)') { +/// box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1); +/// } +/// @include scroll-state-query('scroller scroll-state(scrollable: top)', '&') { +/// backdrop-filter: saturate(1.2) blur(10px); +/// } +@mixin scroll-state-query($container-query, $fallback-selector: null) { + @supports (container-type: scroll-state) { + @container #{$container-query} { + @content; + } + } + + // Fallback for browsers that do not support scroll-state container queries, such as Safari and Firefox. + // https://caniuse.com/mdn-css_properties_container-type_scroll-state + @if $fallback-selector != null { + @supports not (container-type: scroll-state) { + #{$fallback-selector} { + @content; + } + } + } +} diff --git a/assets/css/_core/_mixin/_index.scss b/assets/css/_core/_mixin/_index.scss index f777bd7b..4a5ad7d3 100644 --- a/assets/css/_core/_mixin/_index.scss +++ b/assets/css/_core/_mixin/_index.scss @@ -1,3 +1,4 @@ +@import 'bold-dark'; @import 'border-radius'; @import 'box-shadow'; @import 'compatibility'; @@ -6,5 +7,6 @@ @import 'media'; @import 'scrollbar-width'; @import 'sticky-top'; +@import 'theme-switch'; @import 'theme-vars'; @import 'z-index'; diff --git a/assets/css/_core/_mixin/_link.scss b/assets/css/_core/_mixin/_link.scss index 9bf5020f..4eb76cfe 100644 --- a/assets/css/_core/_mixin/_link.scss +++ b/assets/css/_core/_mixin/_link.scss @@ -3,48 +3,20 @@ /// @param {Boolean} $dark use global link color dark /// @param {String} $decoration text-decoration, default none @mixin link($light, $dark, $decoration: none) { + $link-color-light: if($light, $global-link-color, $single-link-color); + $link-color-dark: if($dark, $global-link-color-dark, $single-link-color-dark); + $link-hover-color-light: if($light, $global-link-hover-color, $single-link-hover-color); + $link-hover-color-dark: if($dark, $global-link-hover-color-dark, $single-link-hover-color-dark); + a, a::before, a::after { text-decoration: $decoration; - - @if $light { - color: $global-link-color; - } - - @else { - color: $single-link-color; - } - - [data-theme=dark] & { - @if $dark { - color: $global-link-color-dark; - } - - @else { - color: $single-link-color-dark; - } - } + color: light-dark($link-color-light, $link-color-dark); } a:active, a:hover { - @if $light { - color: $global-link-hover-color; - } - - @else { - color: $single-link-hover-color; - } - - [data-theme=dark] & { - @if $dark { - color: $global-link-hover-color-dark; - } - - @else { - color: $single-link-hover-color-dark; - } - } + color: light-dark($link-hover-color-light, $link-hover-color-dark); } } diff --git a/assets/css/_core/_mixin/_theme-switch.scss b/assets/css/_core/_mixin/_theme-switch.scss new file mode 100644 index 00000000..44915210 --- /dev/null +++ b/assets/css/_core/_mixin/_theme-switch.scss @@ -0,0 +1,48 @@ +// Shared defaults for theme-switch icon motion. +// Keep them overridable so other entries can reuse the same mixin with different pacing. +$theme-switch-animation-duration: 0.5s !default; +$theme-switch-animation-easing: cubic-bezier(0.22, 1, 0.36, 1) !default; + +/// Bind a theme mode selector to an icon glyph and its matching animation. +/// @param {String} $mode - Theme mode name: auto | light | dark +/// @param {String|null} $icon - Optional Font Awesome glyph code assigned to --fa +/// @example +/// @include theme-switch-mode('auto'); +/// @include theme-switch-mode('light', '\f185'); +@mixin theme-switch-mode($mode, $icon: null) { + [data-theme-mode='#{$mode}'] & { + @if $icon != null { + --fa: '#{$icon}'; + } + + animation: #{$prefix}theme-switch-#{$mode} $theme-switch-animation-duration $theme-switch-animation-easing; + } +} + +/// Generate one theme-switch keyframes block from a compact parameter list. +/// The final state is intentionally shared so all modes settle on the same resting pose. +/// @param {String} $name - Animation suffix used in the generated keyframes name +/// @param {Angle} $from-rotate - Initial rotation angle +/// @param {Number} $from-scale - Initial icon scale +/// @param {Number} $from-opacity - Initial icon opacity +/// @param {String} $mid-step - Intermediate keyframe percentage such as 55% or 60% +/// @param {Angle} $mid-rotate - Intermediate rotation angle +/// @param {Number} $mid-scale - Intermediate icon scale +@mixin theme-switch-keyframes($name, $from-rotate, $from-scale, $from-opacity, $mid-step, $mid-rotate, $mid-scale) { + @keyframes #{$prefix}theme-switch-#{$name} { + 0% { + transform: rotate($from-rotate) scale($from-scale); + opacity: $from-opacity; + } + + #{$mid-step} { + transform: rotate($mid-rotate) scale($mid-scale); + opacity: 1; + } + + 100% { + transform: rotate(225deg) scale(1); + opacity: 1; + } + } +} diff --git a/assets/css/_core/_mixin/_theme-vars.scss b/assets/css/_core/_mixin/_theme-vars.scss index 4197635f..df61eec6 100644 --- a/assets/css/_core/_mixin/_theme-vars.scss +++ b/assets/css/_core/_mixin/_theme-vars.scss @@ -39,55 +39,43 @@ } } -/// Batch set theme color variables using global $theme-colors -/// Generates light theme (in :root) and dark theme ([data-theme=dark]) variables -@mixin set-theme-colors() { - // Light theme (in :root) - @at-root :root { - @each $name, $value in $theme-colors { - #{$rootPrefix}#{$name}: #{$value}; - } +/// Dark mode content wrapper +/// - Explicit dark mode: [data-theme-mode='dark'] +/// - Auto mode with system dark preference: [data-theme-mode='auto'] + prefers-color-scheme +@mixin dark-mode { + [data-theme-mode='dark'] & { + @content; } - // Dark theme - @at-root [data-theme=dark] { - @each $name, $value in $theme-colors { - #{$rootPrefix}#{$name}: #{darken($value, 5%)}; + @media (prefers-color-scheme: dark) { + :root[data-theme-mode='auto'] & { + @content; } } } -/// 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)}; +/// Batch set theme color variables using global $theme-colors +/// Values are emitted via light-dark() and switched by CSS color-scheme. +@mixin set-theme-colors() { + // Light/Dark in one declaration (driven by color-scheme) + @at-root :root { + @each $name, $value in $theme-colors { + #{$rootPrefix}#{$name}: light-dark(#{$value}, #{darken($value, 5%)}); + } } -} +} -/// Define dual-theme variables (generate definitions for both light and dark simultaneously) +/// Define dual-theme variables with light-dark() /// @param {Map} $vars - Variable map, key is CSS variable name, value is Map(light: $light-val, dark: $dark-val) /// @example /// @include define-theme-vars(( /// global-background-color: (light: $global-background-color, dark: $global-background-color-dark) /// )); @mixin define-theme-vars($vars) { - // Light theme (in :root) + // Light/Dark in one declaration (driven by color-scheme) @at-root :root { @each $name, $values in $vars { - #{$rootPrefix}#{$name}: #{map-get($values, light)}; - } - } - - // Dark theme - @at-root [data-theme=dark] { - @each $name, $values in $vars { - #{$rootPrefix}#{$name}: #{map-get($values, dark)}; + #{$rootPrefix}#{$name}: light-dark(#{map-get($values, light)}, #{map-get($values, dark)}); } } } diff --git a/assets/css/_core/_root.scss b/assets/css/_core/_root.scss index d12aacff..0980a5a9 100644 --- a/assets/css/_core/_root.scss +++ b/assets/css/_core/_root.scss @@ -16,6 +16,15 @@ :root { interpolate-size: allow-keywords; + color-scheme: light dark; + + &[data-theme-mode='light'] { + color-scheme: light; + } + + &[data-theme-mode='dark'] { + color-scheme: dark; + } // ---------------------------------------- // Theme-Independent Variables @@ -79,7 +88,7 @@ global-background-color: (light: $global-background-color, dark: $global-background-color-dark), global-font-color: (light: $global-font-color, dark: $global-font-color-dark), global-font-secondary-color: (light: $global-font-secondary-color, dark: $global-font-secondary-color-dark), - global-placeholder-color: (light: $global-placeholder-color, dark: $global-placeholder-color-dark), + global-font-tertiary-color: (light: $global-font-tertiary-color, dark: $global-font-tertiary-color-dark), global-link-color: (light: $global-link-color, dark: $global-link-color-dark), global-link-hover-color: (light: $global-link-hover-color, dark: $global-link-hover-color-dark), global-border-color: (light: $global-border-color, dark: $global-border-color-dark), @@ -117,6 +126,7 @@ single-link-hover-color: (light: $single-link-hover-color, dark: $single-link-hover-color-dark), table-background-color: (light: $table-background-color, dark: $table-background-color-dark), table-thead-color: (light: $table-thead-color, dark: $table-thead-color-dark), + table-border-color: (light: $table-border-color, dark: $table-border-color-dark), blockquote-color: (light: $blockquote-color, dark: $blockquote-color-dark), reward-color: (light: $reward-color, dark: $reward-color-dark), @@ -128,7 +138,7 @@ code-inline-background-color: (light: rgba(175, 184, 193, 0.2), dark: rgba(99, 110, 123, 0.4)), code-block-background-color: (light: $code-background-color, dark: $code-background-color-dark), code-header-color: (light: $code-header-color, dark: $code-header-color-dark), - code-header-background-color: (light: darken($code-background-color, 5%), dark: darken($code-background-color-dark, 5%)), + code-header-background-color: (light: $code-header-background-color, dark: $code-header-background-color-dark), code-highlight-color: (light: $code-highlight-color, dark: $code-highlight-color-dark), gist-meta-background-color: (light: darken($code-background-color, 5%), dark: darken($code-background-color-dark, 5%)), )); diff --git a/assets/css/_core/_widgets/_cell-tooltip.scss b/assets/css/_core/_widgets/_cell-tooltip.scss index 9940fac8..e1bb98e1 100644 --- a/assets/css/_core/_widgets/_cell-tooltip.scss +++ b/assets/css/_core/_widgets/_cell-tooltip.scss @@ -1,11 +1,6 @@ body { .cell-tooltip { - --tooltip-bg: #ffffff; - --tooltip-color: #0f172a; - - [data-theme=dark] & { - --tooltip-bg: #39393c; - --tooltip-color: #e2e2e6; - } + --tooltip-bg: light-dark(#ffffff, #39393c); + --tooltip-color: light-dark(#0f172a, #e2e2e6); } } diff --git a/assets/css/_core/_widgets/_cookieconsent.scss b/assets/css/_core/_widgets/_cookieconsent.scss index 98db597b..6a52024e 100644 --- a/assets/css/_core/_widgets/_cookieconsent.scss +++ b/assets/css/_core/_widgets/_cookieconsent.scss @@ -5,11 +5,7 @@ &:hover, &:focus { - background-color: #ccc; - - [data-theme=dark] & { - background-color: #444; - } + background-color: light-dark(#ccc, #444); } } } diff --git a/assets/css/_core/_widgets/_details.scss b/assets/css/_core/_widgets/_details.scss index c3b0b26b..8a7d8ebb 100644 --- a/assets/css/_core/_widgets/_details.scss +++ b/assets/css/_core/_widgets/_details.scss @@ -8,7 +8,7 @@ } i.details-icon { - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); transition: all 0.2s ease; &:hover { diff --git a/assets/css/_core/_widgets/_fixed-button.scss b/assets/css/_core/_widgets/_fixed-button.scss index cbd27b11..31769ae7 100644 --- a/assets/css/_core/_widgets/_fixed-button.scss +++ b/assets/css/_core/_widgets/_fixed-button.scss @@ -12,14 +12,10 @@ font-size: 1rem; line-height: 1.5rem; padding: 0.5rem 0.625rem; - color: fixit-var(secondary); - background-color: #f1f1f1; + color: fixit-var(global-font-secondary-color); transition: color 0.4s ease; @include border-radius(full); - - [data-theme=dark] & { - background-color: #1e1e1f; - } + background-color: light-dark(#f1f1f1, #1e1e1f); &:hover, &:active { @@ -45,16 +41,12 @@ // Ring stroke width b2t-stroke: 0.5rem, // Progress color - b2t-prog: fixit-var(success), + b2t-prog: light-dark(fixit-var(success), fixit-var(global-font-color)), // dasharray = circumference = 2πr // dashoffset = circumference * (1 - scrollPercent / 100) b2t-dasharray: 314.15 )); - [data-theme=dark] & { - @include set-fixit-var(b2t-prog, fixit-var(global-font-color)); - } - // SVG circular progress ring svg { position: absolute; diff --git a/assets/css/_core/_widgets/_reading-progress.scss b/assets/css/_core/_widgets/_reading-progress.scss index 9fcb9267..fad5e45f 100644 --- a/assets/css/_core/_widgets/_reading-progress.scss +++ b/assets/css/_core/_widgets/_reading-progress.scss @@ -1,14 +1,13 @@ .reading-progress-bar { height: fixit-var(progress-h, 2px); width: fixit-var(progress, 0); - background-color: fixit-var(bg-progress, fixit-var(success)); + background-color: light-dark( + fixit-var(bg-progress, fixit-var(success)), + fixit-var(bg-progress-dark, fixit-var(global-font-color)) + ); position: fixed; @include z-index(fixed); - [data-theme=dark] & { - background-color: fixit-var(bg-progress-dark, fixit-var(global-font-color)); - } - @include media('print') { display: none !important; } diff --git a/assets/css/_page/_archive/_tags.scss b/assets/css/_page/_archive/_tags.scss index 0e5c8d9a..8402e307 100644 --- a/assets/css/_page/_archive/_tags.scss +++ b/assets/css/_page/_archive/_tags.scss @@ -20,7 +20,7 @@ } sup { - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } } } @@ -49,11 +49,13 @@ @for $grade from 0 through 10 { &[data-grade="#{$grade}"] { - @include set-fixit-var(tag-cloud-color, mix($tag-cloud-end, $tag-cloud-start, $grade * 10)); - - [data-theme=dark] & { - @include set-fixit-var(tag-cloud-color, mix($tag-cloud-end-dark, $tag-cloud-start-dark, $grade * 10)); - } + @include set-fixit-var( + tag-cloud-color, + light-dark( + mix($tag-cloud-end, $tag-cloud-start, $grade * 10), + mix($tag-cloud-end-dark, $tag-cloud-start-dark, $grade * 10) + ) + ); } } } diff --git a/assets/css/_page/_archive/_terms.scss b/assets/css/_page/_archive/_terms.scss index ef94f894..5908b937 100644 --- a/assets/css/_page/_archive/_terms.scss +++ b/assets/css/_page/_archive/_terms.scss @@ -63,7 +63,7 @@ .archive-item-date { text-align: right; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); white-space: nowrap; @extend .variant-numeric; } diff --git a/assets/css/_page/_home.scss b/assets/css/_page/_home.scss index af879f54..65f82d7e 100644 --- a/assets/css/_page/_home.scss +++ b/assets/css/_page/_home.scss @@ -10,13 +10,9 @@ width: 8rem; height: auto; margin: 0 auto; - box-shadow: 0 0 0 0.3618em rgba(0, 0, 0, 0.05); transition: all 0.4s ease; @include border-radius(full); - - [data-theme=dark] & { - box-shadow: 0 0 0 0.3618em rgba(255, 255, 255, 0.05); - } + box-shadow: 0 0 0 0.3618em light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)); &:hover { position: relative; @@ -62,7 +58,7 @@ font-weight: normal; margin: 0; padding: 0.5rem; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } } } @@ -77,6 +73,7 @@ } .summary { + position: relative; padding-block: 1rem; color: fixit-var(global-font-color); @@ -129,12 +126,12 @@ .content { margin-top: 0.3rem; - color: fixit-var(global-placeholder-color); + color: fixit-var(global-font-tertiary-color); @include link(false, true); b, strong { - color: fixit-var(global-placeholder-color); + color: fixit-var(global-font-tertiary-color); } } @@ -144,12 +141,10 @@ justify-content: space-between; align-items: center; font-size: 0.875rem; - @include link(false, false); .post-tags { padding: 0; - @include link(true, true); .post-tag:not(:last-child)::after { diff --git a/assets/css/_page/_index.scss b/assets/css/_page/_index.scss index 8d0c3a2d..20187772 100644 --- a/assets/css/_page/_index.scss +++ b/assets/css/_page/_index.scss @@ -1,5 +1,4 @@ .page { - position: relative; padding-inline: 0.5rem; @extend %page-style; diff --git a/assets/css/_page/_single/_alert.scss b/assets/css/_page/_single/_alert.scss index 314a3a76..e2d59c98 100644 --- a/assets/css/_page/_single/_alert.scss +++ b/assets/css/_page/_single/_alert.scss @@ -27,22 +27,13 @@ } @each $type, $item in $alert-color-map { + $dark-item: map-get($alert-color-map-dark, $type); + &.alert-#{$type} { @include set-fixit-vars(( - alert-border-color: map-get($item, border-color), - alert-color: map-get($item, color) + alert-border-color: light-dark(map-get($item, border-color), map-get($dark-item, border-color)), + alert-color: light-dark(map-get($item, color), map-get($dark-item, color)) )); } } - - [data-theme=dark] & { - @each $type, $item in $alert-color-map-dark { - &.alert-#{$type} { - @include set-fixit-vars(( - alert-border-color: map-get($item, border-color), - alert-color: map-get($item, color) - )); - } - } - } } diff --git a/assets/css/_page/_single/_code-syntax/_index.scss b/assets/css/_page/_single/_code-syntax/_index.scss index 00088764..7fe22626 100644 --- a/assets/css/_page/_single/_code-syntax/_index.scss +++ b/assets/css/_page/_single/_code-syntax/_index.scss @@ -23,7 +23,7 @@ @import 'light'; - [data-theme=dark] & { + @include dark-mode { @import 'dark'; } } diff --git a/assets/css/_page/_single/_code.scss b/assets/css/_page/_single/_code.scss index 116f5a70..c9004505 100644 --- a/assets/css/_page/_single/_code.scss +++ b/assets/css/_page/_single/_code.scss @@ -26,10 +26,6 @@ pre { line-height: inherit; min-width: max-content; @include border-radius(0); - - [data-theme=dark] & { - background-color: transparent; - } } img { @@ -80,6 +76,7 @@ pre { .lntable { overflow: auto; + overscroll-behavior-x: none; display: block; background-color: fixit-var(code-block-background-color); @include border-radius; @@ -120,7 +117,7 @@ pre { // Line numbers .lnt, .ln { - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); @include user-select(none); } @@ -321,22 +318,13 @@ pre { bottom: 0; background: linear-gradient( 180deg, - rgba($code-background-color, 0) 0%, - rgba($code-background-color, 0.9) 60%, - $code-background-color 100% + light-dark(rgba($code-background-color, 0), rgba($code-background-color-dark, 0)) 0%, + light-dark(rgba($code-background-color, 0.9), rgba($code-background-color-dark, 0.9)) 60%, + light-dark($code-background-color, $code-background-color-dark) 100% ); @include z-index(base); @include border-radius(bottom); - [data-theme=dark] & { - background: linear-gradient( - 180deg, - rgba($code-background-color-dark, 0) 0%, - rgba($code-background-color-dark, 0.9) 60%, - $code-background-color-dark 100% - ); - } - .fa-angles-down { opacity: 0; animation-name: fadeOutDown; @@ -355,12 +343,11 @@ pre { &.is-collapsed { .code-header { transition: border-radius 0.2s ease 0.2s; - @include set-fixit-var(code-header-background-color, darken($code-background-color, 6%)); @include border-radius; - - [data-theme=dark] & { - @include set-fixit-var(code-header-background-color, darken($code-background-color-dark, 6%)); - } + @include set-fixit-var( + code-header-background-color, + light-dark(darken($code-header-background-color, 1%), darken($code-header-background-color-dark, 1%)) + ); + .code-wrapper { height: 0; @@ -413,8 +400,18 @@ pre { &.line-nos-hidden { > .chroma .lntable { width: 100%; - display: table; + overscroll-behavior-x: contain; + + > tbody { + display: table; + width: 100%; + } + + .lntd:last-child { + width: 100%; + } } + .lntd:first-child, .ln { display: none; @@ -459,7 +456,7 @@ pre { min-width: calc(#{fixit-var(line-digit)} * 1ch); padding-inline: 0.75rem; flex-shrink: 0; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); background-color: fixit-var(code-block-background-color); } @@ -478,7 +475,7 @@ pre { // when line numbers shown &:not(.line-nos-hidden) { - .lntd:last-child pre.chroma, + .lntd:last-child pre.chroma:not([contenteditable='true']), .code-wrapper > pre.chroma:not(:has(.ln)) { padding-left: 0; } @@ -486,11 +483,7 @@ pre { // add border for non-classic mode &:not([data-mode='classic']) { - border: 1px solid darken($code-background-color, 3%); - - [data-theme=dark] & { - border-color: darken($code-background-color-dark, 3%); - } + border: 1px solid fixit-var(global-border-color-light); } &[data-mode='mac'] { @@ -508,7 +501,7 @@ pre { width: 12px; @include border-radius(full); - [data-theme=dark] & { + @include dark-mode { @include set-fixit-vars(( mac-red: #ff5f56, mac-yellow: #febc2e, @@ -571,7 +564,7 @@ pre { background-color: fixit-var(gist-meta-background-color); } - [data-theme=dark] & { + @include dark-mode { @import './_code-syntax/github-dark-dimmed'; } } @@ -580,7 +573,7 @@ pre { .code-tabs { margin: 0.5rem 0; background-color: fixit-var(code-block-background-color); - border: 1px solid fixit-var(global-border-color); + border: 1px solid fixit-var(global-border-color-light); @include border-radius; &[data-shadow='always'] { @@ -610,6 +603,7 @@ pre { color: fixit-var(code-header-color); background-color: fixit-var(code-header-background-color); position: sticky; + border-bottom: 1px solid fixit-var(global-border-color); @include z-index(base, 1); @include border-radius(top); @include sticky-top-offset; @@ -630,7 +624,7 @@ pre { padding: 0.4rem 0.8rem; cursor: pointer; font-weight: bold; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); white-space: nowrap; @include user-select(none); @include border-radius; @@ -654,7 +648,7 @@ pre { translate: 2px 2px; width: calc(anchor-size(width) - 0.25rem); height: calc(anchor-size(height) - 0.25rem); - box-shadow: inset 0 0 1rem 0.25rem color-mix(in srgb, fixit-var(secondary) 30%, transparent); + box-shadow: inset 0 0 1rem 0.25rem color-mix(in srgb, fixit-var(global-font-secondary-color) 30%, transparent); pointer-events: none; transition: 0.3s ease-in-out; @include border-radius; diff --git a/assets/css/_page/_single/_collections.scss b/assets/css/_page/_single/_collections.scss index b26f3cfd..0cfe028d 100644 --- a/assets/css/_page/_single/_collections.scss +++ b/assets/css/_page/_single/_collections.scss @@ -1,16 +1,12 @@ // Collection Navigation .collection-card { font-size: 0.875rem; - background-color: darken($global-background-color, 3%); margin-block: 1rem; padding: 1rem 1.25rem; position: relative; @include border-radius(0.5rem); @include user-select(none); - - [data-theme=dark] & { - background-color: lighten($global-background-color-dark, 3%); - } + background-color: light-dark(darken($global-background-color, 4%), lighten($global-background-color-dark, 4%)); &:has(.collection-nav-item:nth-child(2))::before { content: ''; @@ -19,16 +15,12 @@ bottom: 0.75rem; width: 1px; height: 2rem; - background: linear-gradient(to bottom, rgba(0, 0, 0, 0.03), rgba(0, 0, 0, 0.05) 50%, rgba(0, 0, 0, 0.03) 100%); - - [data-theme=dark] & { - background: linear-gradient( - to bottom, - rgba(255, 255, 255, 0.03), - rgba(255, 255, 255, 0.05) 50%, - rgba(255, 255, 255, 0.03) 100% - ); - } + background: linear-gradient( + to bottom, + light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)), + light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.08)) 50%, + light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)) 100% + ); } .collection-title { @@ -107,11 +99,7 @@ &.open { .collection-summary { - background-color: darken($global-background-color, 4%); - - [data-theme=dark] & { - background-color: lighten($global-background-color-dark, 4%); - } + background-color: light-dark(darken($global-background-color, 5%), lighten($global-background-color-dark, 5%)); } } @@ -120,12 +108,8 @@ align-items: baseline; gap: 0.25em; padding: 0.2em 0.5em; - background-color: darken($global-background-color, 3%); + background-color: light-dark(darken($global-background-color, 5%), lighten($global-background-color-dark, 5%)); - [data-theme=dark] & { - background-color: lighten($global-background-color-dark, 3%); - } - > i.fa-layer-group { flex-shrink: 0; } @@ -142,7 +126,7 @@ .collection-count { flex-shrink: 0; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } .details-icon { @@ -154,11 +138,7 @@ .collection-content { font-size: fixit-var(collection-list-font-size); - background-color: darken($global-background-color, 2%); - - [data-theme=dark] & { - background-color: lighten($global-background-color-dark, 2%); - } + background-color: light-dark(darken($global-background-color, 3%), lighten($global-background-color-dark, 3%)); .collection-list { margin: 0; @@ -166,7 +146,8 @@ padding-block: 0.4em; text-indent: -0.8em; list-style: none; - overflow-y: scroll; + overflow-y: auto; + overscroll-behavior: contain; max-height: 60vh; @include scrollbar-width(none, 0); @@ -195,11 +176,7 @@ align-items: center; justify-content: center; gap: 0.5em; - background-color: darken($global-background-color, 3%); - - [data-theme=dark] & { - background-color: lighten($global-background-color-dark, 3%); - } + background-color: light-dark(darken($global-background-color, 5%), lighten($global-background-color-dark, 5%)); a.collection-nav-item { padding-inline: 2px; @@ -207,11 +184,7 @@ transition: background-color 0.3s ease-out; &:hover { - background-color: darken($global-background-color, 10%); - - [data-theme=dark] & { - background-color: lighten($global-background-color-dark, 10%); - } + background-color: light-dark(darken($global-background-color, 10%), lighten($global-background-color-dark, 10%)); } } diff --git a/assets/css/_page/_single/_color-preview.scss b/assets/css/_page/_single/_color-preview.scss index bde5196e..61064e8c 100644 --- a/assets/css/_page/_single/_color-preview.scss +++ b/assets/css/_page/_single/_color-preview.scss @@ -11,12 +11,8 @@ margin-right: 0.25em; vertical-align: middle; border: 1px solid; - border-color: fixit-var(color-preview-border-color, #d1d9e0b3); pointer-events: none; transform: translateY(-1px); - - [data-theme=dark] & { - @include set-fixit-var(color-preview-border-color, #3d444db3); - } + border-color: light-dark(#d1d9e0b3, #3d444db3); } } diff --git a/assets/css/_page/_single/_diagram-copy-btn.scss b/assets/css/_page/_single/_diagram-copy-btn.scss index 23712c4e..f333049b 100644 --- a/assets/css/_page/_single/_diagram-copy-btn.scss +++ b/assets/css/_page/_single/_diagram-copy-btn.scss @@ -14,35 +14,15 @@ line-height: 1; padding: 0.5rem; border: 1px solid; - color: fixit-var(dcb-color, #25292e); - border-color: fixit-var(dcb-border-color, #d1d9e0); - background-color: fixit-var(dcb-background-color, #f6f8fa); - box-shadow: fixit-var(dcb-box-shadow, 0px 1px 0px 0px #1f23280a); + color: light-dark(#25292e, #d1d7e0); + border-color: light-dark(#d1d9e0, #3d444d); + background-color: light-dark(#f6f8fa, #2a313c); @include user-select(none); @include border-radius; @include z-index(base); - [data-theme=dark] & { - @include set-fixit-vars(( - dcb-color: #d1d7e0, - dcb-border-color: #3d444d, - dcb-background-color: #2a313c, - dcb-box-shadow: 0px 0px 0px 0px #000000, - )); - } - &:hover { - @include set-fixit-vars(( - dcb-border-color: #d1d9e0, - dcb-background-color: #eff2f5, - )); - - [data-theme=dark] & { - @include set-fixit-vars(( - dcb-border-color: #3d444d, - dcb-background-color: #2f3742, - )); - } + background-color: light-dark(darken(#f6f8fa, 3%), darken(#2a313c, 3%)); } .fa-clone { diff --git a/assets/css/_page/_single/_fixit-decryptor.scss b/assets/css/_page/_single/_fixit-decryptor.scss index 699fc9dd..a6dfc4d3 100644 --- a/assets/css/_page/_single/_fixit-decryptor.scss +++ b/assets/css/_page/_single/_fixit-decryptor.scss @@ -22,14 +22,12 @@ &:active, &:focus, &:hover { - color: darken($global-link-hover-color, 6%); + color: light-dark(darken($global-link-hover-color, 6%), lighten($global-link-hover-color-dark, 6%)); border-color: fixit-var(global-link-hover-color); - box-shadow: 0px 0px 5px 0px lighten($global-link-hover-color, 20%); - - [data-theme=dark] & { - color: lighten($global-link-hover-color-dark, 6%); - box-shadow: 0px 0px 5px 0px darken($global-link-hover-color-dark, 20%); - } + box-shadow: 0px 0px 5px 0px light-dark( + lighten($global-link-hover-color, 20%), + darken($global-link-hover-color-dark, 20%) + ); } } diff --git a/assets/css/_page/_single/_footer.scss b/assets/css/_page/_single/_footer.scss index 84419d13..be781b8c 100644 --- a/assets/css/_page/_single/_footer.scss +++ b/assets/css/_page/_single/_footer.scss @@ -15,14 +15,14 @@ .post-info-mod { font-size: 0.8em; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); @include link(false, false); } .post-info-license { font-size: 0.8em; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); @include link(false, false); } @@ -37,7 +37,7 @@ content: '|'; margin-left: 0.25rem; margin-right: 0.25rem; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } } diff --git a/assets/css/_page/_single/_index.scss b/assets/css/_page/_single/_index.scss index c83215fe..431f07cf 100644 --- a/assets/css/_page/_single/_index.scss +++ b/assets/css/_page/_single/_index.scss @@ -45,7 +45,7 @@ .post-meta { font-size: 0.875rem; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); span { display: inline-block; @@ -95,9 +95,10 @@ content: attr(data-end-flag); display: block; text-align: center; - font-size: 15px; - padding: 5px; - color: fixit-var(global-placeholder-color); + font-size: 0.875em; + padding: 0.25rem; + color: fixit-var(global-font-tertiary-color); + opacity: 0.8; } hr { @@ -167,10 +168,7 @@ line-height: 1.25; margin-top: 1.5rem; margin-bottom: 1rem; - - [data-theme=dark] & { - font-weight: bolder; - } + font-weight: light-dark(bold, bolder); &:hover, &:target { @@ -199,11 +197,7 @@ b, strong { - font-weight: bold; - - [data-theme=dark] & { - color: #ddd; - } + @include bold-dark; } @include link(false, false); @@ -214,9 +208,7 @@ b, strong { - [data-theme=dark] & { - color: inherit; - } + color: inherit; } } @@ -234,7 +226,7 @@ } li::marker { - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } li:hover::marker { color: fixit-var(single-link-hover-color); @@ -251,7 +243,7 @@ } } li[data-task='x'] { - @include set-fixit-var(checkbox-color, fixit-var(secondary)); + @include set-fixit-var(checkbox-color, fixit-var(global-font-secondary-color)); .checkbox-icon { opacity: 0.6; @@ -260,7 +252,7 @@ li[data-task='-'] { opacity: 0.6; text-decoration: line-through rgba(145, 145, 145, 0.6); - @include set-fixit-var(task-color, fixit-var(secondary)); + @include set-fixit-var(task-color, fixit-var(global-font-secondary-color)); } li[data-task='/'] { @include set-fixit-var(task-color, fixit-var(success)); @@ -285,7 +277,7 @@ background-color: fixit-var(code-block-background-color); rt { - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } } @@ -308,15 +300,20 @@ .table-wrapper { position: relative; overflow-x: auto; + overflow-y: hidden; + margin: 0.5rem 0; + background: fixit-var(table-background-color); + border: 1px solid fixit-var(table-border-color); + @include border-radius; @include set-fixit-var(scrollbar-thumb-color, fixit-var(table-background-color)); > table { width: 100%; max-width: 100%; - margin: 0.625rem 0; + margin: 0; border-spacing: 0; background: fixit-var(table-background-color); - border-collapse: collapse; + border-collapse: separate; thead { background: fixit-var(table-thead-color); @@ -324,30 +321,27 @@ &:not([class]) tbody { & tr:nth-child(odd) { - background: darken($table-background-color, 2.25%); - - [data-theme=dark] & { - background-color: lighten($table-background-color-dark, 2.75%); - } + background: light-dark(darken($table-background-color, 2.25%), lighten($table-background-color-dark, 2.75%)); } & tr:hover { - background: darken($table-background-color, 4.5%); - - [data-theme=dark] & { - background-color: lighten($table-background-color-dark, 5.5%); - } + background: light-dark(darken($table-background-color, 4.5%), lighten($table-background-color-dark, 5.5%)); } } th, td { padding: 0.3rem 1rem; - border: 1px solid darken($table-thead-color, 2%); + border-right: 1px solid fixit-var(table-border-color); + border-bottom: 1px solid fixit-var(table-border-color); + } - [data-theme=dark] & { - border-color: darken($table-thead-color-dark, 2%); - } + tr > :last-child { + border-right: none; + } + + > :last-child > tr:last-child > * { + border-bottom: none; } } } @@ -406,29 +400,20 @@ sup[id^='fnref']:has(.footnote-ref) { padding: 2px 4px; - background-color: rgba(238, 238, 238, 0.7); - color: #555555; + background-color: light-dark(rgba(238, 238, 238, 0.7), rgba(56, 139, 253, 0.1)); + color: light-dark(#555555, #58a6ff); cursor: pointer; @include border-radius(0.25rem); - [data-theme=dark] & { - background-color: rgba(56, 139, 253, 0.1); - color: #58a6ff; - } - @include link(true, false); } .footnotes { font-size: 0.75rem; white-space: normal; - color: #a3a3ae; + color: light-dark(#a3a3ae, #9d9d9f); @include link(false, true); - [data-theme=dark] & { - color: #9d9d9f; - } - ol { margin-block: 1em; padding-left: 1rem; diff --git a/assets/css/_page/_single/_related.scss b/assets/css/_page/_single/_related.scss index 45e5fff5..896de8b1 100644 --- a/assets/css/_page/_single/_related.scss +++ b/assets/css/_page/_single/_related.scss @@ -30,7 +30,7 @@ .related-count { flex-shrink: 0; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } .details-icon { @@ -49,7 +49,8 @@ padding-block: 0.4em; text-indent: -0.8em; list-style: none; - overflow-y: scroll; + overflow-y: auto; + overscroll-behavior: contain; max-height: 60vh; @include scrollbar-width(none, 0); diff --git a/assets/css/_page/_single/_reward.scss b/assets/css/_page/_single/_reward.scss index 8dcce909..70d2e40b 100644 --- a/assets/css/_page/_single/_reward.scss +++ b/assets/css/_page/_single/_reward.scss @@ -51,11 +51,7 @@ background-color: fixit-var(global-background-color); @include z-index(fixed); @include border-radius; - box-shadow: 0 0 1.5rem 0 rgba(0, 0, 0, 0.2); - - [data-theme=dark] & { - box-shadow: 0 0 1.5rem 0 rgba(255, 255, 255, 0.2); - } + box-shadow: 0 0 1.5rem 0 light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2)); } } @@ -65,13 +61,13 @@ cursor: pointer; outline: none; color: fixit-var(reward-color, lighten($reward-color, 5%)); - border: 2px solid lighten($reward-color, 10%); + border: 2px solid light-dark(lighten($reward-color, 10%), lighten($reward-color, 5%)); padding: 0.6rem 1rem; background-color: fixit-var(global-background-color); vertical-align: text-top; line-height: 1rem; - @include border-radius; transition: all 0.3s ease-out; + @include border-radius; &:active, &:hover { @@ -79,10 +75,6 @@ border-color: fixit-var(reward-color); background-color: fixit-var(reward-color); } - - [data-theme=dark] & { - border-color: lighten($reward-color, 5%); - } } @include media('print') { diff --git a/assets/css/_page/_single/_shortcodes/_admonition.scss b/assets/css/_page/_single/_shortcodes/_admonition.scss index e3eba117..0ad11018 100644 --- a/assets/css/_page/_single/_shortcodes/_admonition.scss +++ b/assets/css/_page/_single/_shortcodes/_admonition.scss @@ -18,11 +18,7 @@ strong { font-weight: bolder; - text-shadow: 0 0.05rem 0.05rem rgba(0, 0, 0, 0.2); - - [data-theme=dark] & { - text-shadow: 0 0.05rem 0.05rem rgba(255, 255, 255, 0.2); - } + text-shadow: light-dark(0 0.05rem 0.05rem rgba(0, 0, 0, 0.2), 0 0.05rem 0.05rem rgba(255, 255, 255, 0.2)); } // title-only admonition without content diff --git a/assets/css/_page/_single/_shortcodes/_cardlink.scss b/assets/css/_page/_single/_shortcodes/_cardlink.scss index a938d276..ca1e985f 100644 --- a/assets/css/_page/_single/_shortcodes/_cardlink.scss +++ b/assets/css/_page/_single/_shortcodes/_cardlink.scss @@ -34,7 +34,7 @@ align-items: center; justify-content: space-between; padding: 0.75rem; - background-color: rgba(245, 245, 245, 0.88); + background-color: light-dark(rgba(245, 245, 245, 0.88), rgba(61, 62, 65, 0.88)); } &text { @@ -58,7 +58,7 @@ align-items: center; margin-top: 0.25rem; font-size: 0.825rem; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } &icon-link { @@ -97,10 +97,4 @@ object-fit: cover; @include border-radius(full); } - - [data-theme=dark] & { - &content { - background-color: rgba(61, 62, 65, 0.88); - } - } } diff --git a/assets/css/_page/_single/_shortcodes/_file-tree.scss b/assets/css/_page/_single/_shortcodes/_file-tree.scss index c283394b..586b8184 100644 --- a/assets/css/_page/_single/_shortcodes/_file-tree.scss +++ b/assets/css/_page/_single/_shortcodes/_file-tree.scss @@ -8,11 +8,7 @@ transition: box-shadow 0.2s ease; &:hover { - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - - [data-theme=dark] & { - box-shadow: 0 2px 8px rgba(255, 255, 255, 0.1); - } + box-shadow: 0 2px 8px light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); } ul.file-tree { @@ -26,11 +22,7 @@ &.file-tree-folder { > .file-tree-toggle { cursor: pointer; - font-weight: bold; - - [data-theme=dark] & { - color: #ddd; - } + @include bold-dark; &:hover { i, @@ -79,7 +71,7 @@ translate: 0 -50%; width: 0.75rem; height: 1px; - background-color: fixit-var(secondary); + background-color: fixit-var(global-font-secondary-color); } } } @@ -96,7 +88,7 @@ bottom: 0.625rem; left: calc(1rem - 0.5px); width: 1px; - background-color: fixit-var(secondary); + background-color: fixit-var(global-font-secondary-color); height: fixit-var(file-tree-line-height); } } diff --git a/assets/css/_page/_single/_shortcodes/_instagram.scss b/assets/css/_page/_single/_shortcodes/_instagram.scss index 9ab1dc8b..7bebfbc3 100644 --- a/assets/css/_page/_single/_shortcodes/_instagram.scss +++ b/assets/css/_page/_single/_shortcodes/_instagram.scss @@ -1,5 +1,5 @@ iframe.instagram-media { - [data-theme=dark] & { + @include dark-mode { border: none !important; } } diff --git a/assets/css/_page/_single/_shortcodes/_mermaid.scss b/assets/css/_page/_single/_shortcodes/_mermaid.scss index deacd3c7..ab4247ea 100644 --- a/assets/css/_page/_single/_shortcodes/_mermaid.scss +++ b/assets/css/_page/_single/_shortcodes/_mermaid.scss @@ -21,7 +21,7 @@ } .mermaid { - [data-theme=dark] & { + @include dark-mode { height: 0; padding: 0; } @@ -31,7 +31,7 @@ height: 0; padding: 0; - [data-theme=dark] & { + @include dark-mode { height: auto; padding: 0.5rem 0.75rem; } diff --git a/assets/css/_page/_single/_shortcodes/_tabs.scss b/assets/css/_page/_single/_shortcodes/_tabs.scss index d68851cd..666f4dab 100644 --- a/assets/css/_page/_single/_shortcodes/_tabs.scss +++ b/assets/css/_page/_single/_shortcodes/_tabs.scss @@ -246,19 +246,11 @@ tab-container { color: #ffffff; background-color: fixit-var(primary); box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.3); - - [data-theme=dark] & { - background-color: fixit-var(primary); - box-shadow: 0 0.125rem 0.5rem rgba(255, 255, 255, 0.3); - } + box-shadow: 0 0.125rem 0.5rem light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3)); } &[aria-selected='false']:hover { - background-color: rgba(175, 184, 193, 0.4); - - [data-theme=dark] & { - background-color: rgba(99, 110, 123, 0.6); - } + background-color: light-dark(rgba(175, 184, 193, 0.4), rgba(99, 110, 123, 0.6)); } } @@ -476,6 +468,7 @@ tab-container { } &::part(tablist-tab-wrapper) { + position: relative; padding: 0.25rem; background-color: fixit-var(code-block-background-color); @include border-radius; @@ -490,7 +483,6 @@ tab-container { font-weight: 400; flex-grow: 1; @include z-index(base); - @include tab-button-size; @include border-radius; @@ -500,27 +492,33 @@ tab-container { } &[aria-selected='true'] { - anchor-name: #{$rootPrefix}tab-segment-active; - background-color: transparent; box-shadow: none; } } + &:not(:has(> .tab-button:hover)) > .tab-button[aria-selected='true'], + > .tab-button:hover { + anchor-name: #{$rootPrefix}tab-segment-active; + } + // Sliding active indicator via anchor positioning &::part(tablist-tab-wrapper)::before { content: ''; position: absolute; pointer-events: none; position-anchor: #{$rootPrefix}tab-segment-active; - inset: anchor(inside); + top: anchor(top); + left: anchor(left); + width: anchor-size(width); + height: anchor-size(height); background-color: fixit-var(global-background-color); - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); - transition: all 0.15s ease, top, bottom 0s; + transition: + top 0.3s ease, + left 0.3s ease, + width 0.3s ease, + height 0.3s ease; + box-shadow: 0 0.125rem 0.25rem light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); @include border-radius; - - [data-theme=dark] & { - box-shadow: 0 0.125rem 0.25rem rgba(255, 255, 255, 0.1); - } } } } @@ -623,11 +621,7 @@ tab-container .tab-panel:not([hidden]) { > .tab-button { &[aria-selected='true'] { background-color: fixit-var(global-background-color); - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); - - [data-theme=dark] & { - box-shadow: 0 0.125rem 0.25rem rgba(255, 255, 255, 0.1); - } + box-shadow: 0 0.125rem 0.25rem light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); } } } diff --git a/assets/css/_page/_single/_shortcodes/_timeline.scss b/assets/css/_page/_single/_shortcodes/_timeline.scss index 4ce643fe..75c50b74 100644 --- a/assets/css/_page/_single/_shortcodes/_timeline.scss +++ b/assets/css/_page/_single/_shortcodes/_timeline.scss @@ -45,7 +45,7 @@ ul.#{$prefix}timeline { .#{$prefix}timeline-item__timestamp { font-size: 0.8125rem; line-height: 1; - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); &.is-top { margin-bottom: 0.25rem; @@ -76,17 +76,17 @@ ul.#{$prefix}timeline { top: 1.25rem; transform: translateY(-50%); left: 0; + text-align: center; + border-style: solid; + color: transparent; width: fixit-var(timeline-circle-size); height: fixit-var(timeline-circle-size); font-size: fixit-var(timeline-circle-size); line-height: fixit-var(timeline-circle-size); - text-align: center; - @include border-radius(full); border-width: fixit-var(timeline-width); - border-style: solid; border-color: fixit-var(timeline-circle-color); background-color: fixit-var(global-background-color); - color: transparent; + @include border-radius(full); @include z-index(base); } &::after { diff --git a/assets/css/_page/_single/_toc.scss b/assets/css/_page/_single/_toc.scss index ad9d57b8..9626e9cc 100644 --- a/assets/css/_page/_single/_toc.scss +++ b/assets/css/_page/_single/_toc.scss @@ -51,7 +51,7 @@ background-color: fixit-var(code-block-background-color); rt { - color: fixit-var(secondary); + color: fixit-var(global-font-secondary-color); } } } @@ -120,7 +120,8 @@ } .toc-content { - overflow-y: scroll; + overflow-y: auto; + overscroll-behavior: contain; // TODO 这里计算错误,待重构使用 JS 计算 max-height: calc(100vh - #{2 * #{fixit-var(header-height)}}); @include scrollbar-width(none, 0); @@ -203,11 +204,10 @@ &:not(.open) { .toc-title { - @include set-fixit-var(code-header-background-color, darken($code-background-color, 6%)); - - [data-theme=dark] & { - @include set-fixit-var(code-header-background-color, darken($code-background-color-dark, 6%)); - } + @include set-fixit-var( + code-header-background-color, + light-dark(darken($code-header-background-color, 1%), darken($code-header-background-color-dark, 1%)) + ); } } diff --git a/assets/css/_page/_special/_search.scss b/assets/css/_page/_special/_search.scss index a0e49625..78d67bb1 100644 --- a/assets/css/_page/_special/_search.scss +++ b/assets/css/_page/_special/_search.scss @@ -15,7 +15,7 @@ margin: 0; .gsc-input-box { - [data-theme=dark] & { + @include dark-mode { border-color: #c2c2c2; } input { @@ -28,12 +28,12 @@ #___gcse_0, #___gcse_1 { - [data-theme=dark] & { + @include dark-mode { filter: invert(1) hue-rotate(0.5turn); } } #___gcse_1 img { - [data-theme=dark] & { + @include dark-mode { filter: invert(1) hue-rotate(0.5turn) opacity(0.75); } } diff --git a/assets/css/_variables.scss b/assets/css/_variables.scss index 78cf4ec0..0169e98d 100644 --- a/assets/css/_variables.scss +++ b/assets/css/_variables.scss @@ -19,31 +19,31 @@ $global-border-radius: 5px !default; // Color of the background $global-background-color: #ffffff !default; -$global-background-color-dark: #292a2e !default; +$global-background-color-dark: #1f252d !default; // Color of the text -$global-font-color: #161209 !default; -$global-font-color-dark: #b1b1ba !default; +$global-font-color: #1f2328 !default; +$global-font-color-dark: #b3bdcb !default; // Color of the secondary text $global-font-secondary-color: #8b949e !default; $global-font-secondary-color-dark: #7d8792 !default; -// Color of the placeholder -$global-placeholder-color: #b1b1ba !default; -$global-placeholder-color-dark: #909092 !default; +// Color of the tertiary text +$global-font-tertiary-color: lighten($global-font-secondary-color, 12%) !default; +$global-font-tertiary-color-dark: darken($global-font-secondary-color-dark, 12%) !default; // Color of the link $global-link-color: #161209 !default; -$global-link-color-dark: #b1b1ba !default; +$global-link-color-dark: $global-font-color-dark !default; // Color of the hover link $global-link-hover-color: #2983bb !default; // 潮蓝 $global-link-hover-color-dark: #fff !default; // Color of the border -$global-border-color: #e3e3e3 !default; -$global-border-color-dark: #454545 !default; +$global-border-color: #d7dee4 !default; +$global-border-color-dark: #383f47 !default; // ========== Global ========== // // ========== Scrollbar ========== // @@ -65,15 +65,15 @@ $selection-color-dark: rgba(50, 112, 194, 0.4) !default; $header-height: 3.5rem !default; // Color of the header background -$header-background-color: #f7f7f8 !default; -$header-background-color-dark: #252627 !default; +$header-background-color: #f6f8fa !default; +$header-background-color-dark: #151b23 !default; // Font style of the header title $header-title-font-family: $global-font-family !default; $header-title-font-size: 1.375rem !default; // Color of the active menu item -$menu-active-color: #161209 !default; +$menu-active-color: $global-link-color !default; $menu-active-color-dark: #fff !default; // Border color of the menu item @@ -84,13 +84,13 @@ $menu-border-color-dark: #5454547a !default; $submenu-height: $header-height * 0.5 !default; // Color of the search background -$search-background-color: #e9e9e9 !default; -$search-background-color-dark: #363636 !default; +$search-background-color: #e9eaed !default; +$search-background-color-dark: #202833 !default; // ========== Header ========== // // ========== Tag Cloud ========== // // Color range of tag cloud -$tag-cloud-start: #b1b1ba !default; +$tag-cloud-start: $global-font-color-dark !default; $tag-cloud-end: $global-font-color !default; $tag-cloud-start-dark: #909092 !default; $tag-cloud-end-dark: $global-font-color-dark !default; @@ -118,12 +118,16 @@ $single-link-hover-color: #ea517f !default; // 莲瓣红 $single-link-hover-color-dark: #cc5595 !default; // 龙须红 // Color of the table background -$table-background-color: #fff !default; -$table-background-color-dark: #272c34 !default; +$table-background-color: #f9fbfe !default; +$table-background-color-dark: #26303a !default; // Color of the table thead -$table-thead-color: #ededed !default; -$table-thead-color-dark: #20252b !default; +$table-thead-color: #e3ebf4 !default; +$table-thead-color-dark: #3a4653 !default; + +// Color of the table border +$table-border-color: #d4dee8 !default; +$table-border-color-dark: #4b5867 !default; // Color of the blockquote $blockquote-color: #697681 !default; @@ -140,7 +144,7 @@ $reward-img-width: 180px !default; // ========== Pagination ========== // // Color of the link in pagination $pagination-link-color: #bfbfbf !default; -$pagination-link-color-dark: #b1b1ba !default; +$pagination-link-color-dark: $global-font-color-dark !default; // Color of the hover link in pagination $pagination-link-hover-color: #000 !default; @@ -149,21 +153,27 @@ $pagination-link-hover-color-dark: #fff !default; // ========== Code ========== // // Color of the code -$code-color: #24292f !default; -$code-color-dark: #adbac7 !default; +$code-color: #26323d !default; +$code-color-dark: #c5d1dc !default; + +// Color of the code header text +$code-header-color: #70808f !default; +$code-header-color-dark: #99a7b5 !default; + +// Color of the code header background +$code-header-background-color: #dde6ef !default; +$code-header-background-color-dark: #161f28 !default; // Color of the code background -$code-background-color: #f4f6f8 !default; -$code-background-color-dark: #2d333b !default; +$code-background-color: #f3f7fb !default; +$code-background-color-dark: #2a333d !default; +// Color of the code error $code-error-color: #dc3545 !default; // Color of the highlight code -$code-highlight-color: #fff8c5 !default; -$code-highlight-color-dark: rgba(174, 124, 20, 0.15) !default; - -$code-header-color: #7d7d7d !default; -$code-header-color-dark: #b1b0b0 !default; +$code-highlight-color: #fff2b8 !default; +$code-highlight-color-dark: rgba(191, 144, 32, 0.28) !default; // Font size of the code $code-font-size: 0.875em !default; diff --git a/assets/js/head/color-scheme.js b/assets/js/head/color-scheme.js index 234fa34c..85478971 100644 --- a/assets/js/head/color-scheme.js +++ b/assets/js/head/color-scheme.js @@ -1,22 +1,16 @@ import params from '@params'; /** - * Check theme isDark before body rendering + * Initialize theme mode before body rendering. + * Modes: auto | light | dark */ (function () { const localStorage = window.localStorage; - if (!localStorage) { - return; - } - let isDark = false; - const themeUsed = localStorage.getItem('theme'); - if (themeUsed) { - isDark = themeUsed === 'dark'; - } else { - isDark = params.defaultTheme === 'auto' ? - window.matchMedia('(prefers-color-scheme: dark)').matches : - params.defaultTheme === 'dark'; - } - isDark && (document.documentElement.dataset.theme = 'dark'); - document.documentElement.style.setProperty('color-scheme', isDark ? 'dark' : 'light'); + const storedMode = localStorage?.getItem('theme-mode'); + const themeMode = storedMode || + (params.defaultTheme === 'light' || params.defaultTheme === 'dark' + ? params.defaultTheme + : 'auto'); + + document.documentElement.dataset.themeMode = themeMode; })(); diff --git a/assets/js/theme.js b/assets/js/theme.js index 8391c6f6..309268e9 100644 --- a/assets/js/theme.js +++ b/assets/js/theme.js @@ -3,6 +3,8 @@ import { forEach, getScrollTop, isMobile, + getThemeMode, + isDarkMode, isTocStatic, animateCSS, isValidDate, @@ -19,7 +21,8 @@ const copyText = createCopyText(); class FixIt { constructor() { this.config = window.config; - this.isDark = document.documentElement.dataset.theme === 'dark'; + this.themeMode = getThemeMode(); + this.isDark = isDarkMode(); this.newScrollTop = getScrollTop(); this.oldScrollTop = this.newScrollTop; this.scrollEventSet = new Set(); @@ -28,7 +31,6 @@ class FixIt { this.clickMaskEventSet = new Set(); this.beforeprintEventSet = new Set(); this.afterprintEventSet = new Set(); - this.disableScrollEvent = false; window.objectFitImages && objectFitImages(); } @@ -85,7 +87,6 @@ class FixIt { $mask.classList.toggle('blur'); $menuToggleMobile.classList.toggle('active'); $menuMobile.classList.toggle('active'); - this.disableScrollEvent = $mask.classList.contains('blur'); }, false); this._menuMobileOnClickMask = this._menuMobileOnClickMask || (() => { $menuToggleMobile.classList.remove('active'); @@ -102,17 +103,37 @@ class FixIt { } initSwitchTheme() { + const mql = window.matchMedia('(prefers-color-scheme: dark)'); + const modes = ['auto', 'light', 'dark']; + const applyThemeMode = (mode, persist = true) => { + this.themeMode = mode; + document.documentElement.dataset.themeMode = mode; + this.isDark = mode === 'auto' ? mql.matches : mode === 'dark'; + + if (persist) { + window.localStorage?.setItem('theme-mode', mode); + } + + for (let event of this.switchThemeEventSet) { + event(this.isDark); + } + }; + forEach(document.getElementsByClassName('theme-switch'), ($themeSwitch) => { $themeSwitch.addEventListener('click', () => { - document.documentElement.dataset.theme = this.isDark ? 'light' : 'dark'; - document.documentElement.style.setProperty('color-scheme', this.isDark ? 'light' : 'dark'); - this.isDark = !this.isDark; - window.localStorage?.setItem('theme', this.isDark ? 'dark' : 'light'); - for (let event of this.switchThemeEventSet) { - event(this.isDark); - } + const currentIndex = modes.indexOf(this.themeMode); + const nextMode = modes[(currentIndex + 1) % modes.length]; + applyThemeMode(nextMode); }, false); }); + + mql.addEventListener('change', (e) => { + if (this.themeMode !== 'auto') return; + this.isDark = e.matches; + for (let event of this.switchThemeEventSet) { + event(this.isDark); + } + }); } /** @@ -195,12 +216,10 @@ class FixIt { if (_isMobile) { this._searchMobileOnce = true; $searchInput.addEventListener('focus', () => { - this.disableScrollEvent = true; $mask.classList.add('blur'); $header.classList.add('open'); }, false); $searchCancel.addEventListener('click', () => { - this.disableScrollEvent = false; $mask.classList.remove('blur'); document.getElementById('menu-toggle-mobile').classList.remove('active'); document.getElementById('menu-mobile').classList.remove('active'); @@ -220,10 +239,8 @@ class FixIt { $mask.classList.add('blur'); $header.classList.add('open'); $searchInput.focus(); - this.disableScrollEvent = true; }, false); $searchClear.addEventListener('click', () => { - this.disableScrollEvent = false; $searchClear.style.display = 'none'; this._searchDesktop && this._searchDesktop.autocomplete.setVal(''); }, false); @@ -589,6 +606,7 @@ class FixIt { }, false); // line wrapping toggle button $codeHeader.querySelector('.line-wrap-btn')?.addEventListener('click', () => { + if ($codeBlock.querySelector('[contenteditable="true"]')) return; $codeBlock.classList.toggle('line-wrapping'); }, false); // edit button toggle button @@ -603,6 +621,7 @@ class FixIt { $hl.classList.remove('hl'); }); $codeBlock.classList.add('is-expanded'); + $codeBlock.classList.remove('line-wrapping'); $codePreEl.setAttribute('contenteditable', true); $codePreEl.focus(); } @@ -1514,31 +1533,26 @@ class FixIt { } onScroll() { - const $headers = []; const ACCURACY = 20; + const $autoHeaders = []; const $backToTop = document.querySelector('.back-to-top'); const $readingProgressBar = document.querySelector('.reading-progress-bar'); if (document.body.dataset.headerDesktop === 'auto') { - $headers.push(document.getElementById('header-desktop')); + $autoHeaders.push(document.getElementById('header-desktop')); } if (document.body.dataset.headerMobile === 'auto') { - $headers.push(document.getElementById('header-mobile')); + $autoHeaders.push(document.getElementById('header-mobile')); } $backToTop?.addEventListener('click', () => { scrollIntoView('body'); }); window.addEventListener('scroll', (event) => { - if (this.disableScrollEvent) { - event.preventDefault(); - return; - } this.newScrollTop = getScrollTop(); const scroll = this.newScrollTop - this.oldScrollTop; - const isTop = this.newScrollTop <= 0; if (Math.abs(scroll) > ACCURACY) { document.getElementById('mask').click(); const isScrollingDown = scroll > 0; - forEach($headers, ($header) => { + forEach($autoHeaders, ($header) => { if (isScrollingDown) { $header.classList.remove('header__fadeInDown'); animateCSS($header, ['header__fadeOutUp'], true); @@ -1548,7 +1562,7 @@ class FixIt { } }); } else if (this.newScrollTop <= 0) { - forEach($headers, ($header) => { + forEach($autoHeaders, ($header) => { $header.classList.remove('header__fadeOutUp'); animateCSS($header, ['header__fadeInDown'], true); }); @@ -1612,7 +1626,6 @@ class FixIt { for (let event of this.clickMaskEventSet) { event(); } - this.disableScrollEvent = false; e.target.classList.remove('blur'); }, false); } diff --git a/assets/js/utils/common.js b/assets/js/utils/common.js index 8b6310e7..94f752fd 100644 --- a/assets/js/utils/common.js +++ b/assets/js/utils/common.js @@ -1,3 +1,10 @@ +/** + * Iterate over an array-like collection. + * If any handler call returns a Promise, all Promises are collected and returned. + * @param {ArrayLike<*>|Array<*>} elements collection to iterate + * @param {Function} handler callback for each item + * @returns {Promise>} resolved results for async handlers + */ export function forEach(elements, handler) { elements = elements || []; const promises = []; @@ -10,24 +17,56 @@ export function forEach(elements, handler) { return Promise.all(promises); } +/** + * Get the current vertical scroll position. + * @returns {number} current scroll top + */ export function getScrollTop() { return (document.documentElement ?? document.body).scrollTop; } +/** + * Check whether the current viewport matches the mobile breakpoint. + * @returns {Boolean} whether the viewport is mobile-sized + */ export function isMobile() { return window.matchMedia('only screen and (max-width: 680px)').matches; } +/** + * Check whether the table of contents should use the static layout. + * @returns {Boolean} whether the TOC should be rendered as static + */ export function isTocStatic() { return document.getElementById('toc-static').dataset.kept === 'true' || window.matchMedia('only screen and (max-width: 960px)').matches; } /** - * add animate to element - * @param {Element} element animate element - * @param {String|Array} animation animation name - * @param {Boolean} reserved reserved animation - * @param {Function} callback remove callback + * Get the current theme mode from the root element. + * @returns {String} one of auto, light, or dark + */ +export function getThemeMode() { + return document.documentElement.dataset.themeMode || 'auto'; +} + +/** + * Check whether the current effective theme is dark. + * In auto mode, this follows the system color scheme preference. + * @returns {Boolean} whether dark mode is currently active + */ +export function isDarkMode() { + const themeMode = getThemeMode(); + return themeMode === 'auto' + ? window.matchMedia('(prefers-color-scheme: dark)').matches + : themeMode === 'dark'; +} + +/** + * Add one or more Animate.css classes to an element. + * @param {Element} element target element + * @param {String|Array} animation animation name or names + * @param {Boolean} reserved whether to keep animation classes after completion + * @param {Function} callback callback invoked after animation ends */ export function animateCSS(element, animation, reserved, callback) { !Array.isArray(animation) && (animation = [animation]); @@ -39,17 +78,17 @@ export function animateCSS(element, animation, reserved, callback) { } /** - * date validator - * @param {*} date may be date or not - * @returns {Boolean} + * Validate whether a value is a valid Date instance. + * @param {*} date value to validate + * @returns {Boolean} whether the value is a valid date */ export function isValidDate(date) { return date instanceof Date && !isNaN(date.getTime()); } /** - * scroll some element into view - * @param {String} selector element to scroll + * Scroll an element into view smoothly. + * @param {String} selector selector or id reference beginning with # */ export function scrollIntoView(selector) { const element = selector.startsWith('#') @@ -61,8 +100,8 @@ export function scrollIntoView(selector) { } /** - * get a hidden element for temporary use - * @returns {Object} { $el: Element, destroy: Function } + * Create a hidden staging element for temporary DOM operations. + * @returns {Object} staging helpers and the staging element itself */ export function getStagingDOM() { const stagingElement = document.createElement('div') @@ -92,8 +131,8 @@ export function getStagingDOM() { } /** - * create a copy text function with fallback - * @returns {Function} copy text function + * Create a text-copy helper with clipboard API fallback. + * @returns {Function} function that copies text and returns a Promise */ export function createCopyText() { if (navigator.clipboard) { @@ -114,10 +153,10 @@ export function createCopyText() { } /** - * check if a string is a JS object string + * Check whether a string looks like a JavaScript object literal. * @example isObjectLiteral("{a:1,b:2}") // true * @param {String} str string to check - * @returns {Boolean} whether the string is a JS object string + * @returns {Boolean} whether the string is an object literal */ export function isObjectLiteral(str) { if (typeof str !== 'string') { @@ -130,6 +169,11 @@ export function isObjectLiteral(str) { return false; } +/** + * Escape a string for safe HTML text output. + * @param {String} str string to escape + * @returns {String} escaped HTML string + */ export function HTMLEscape(str) { return str.replace(/[&<>"']/g, char => ({ '&': '&', diff --git a/assets/lib/aplayer/dark.scss b/assets/lib/aplayer/dark.scss index 55e2bd00..e2f1846e 100644 --- a/assets/lib/aplayer/dark.scss +++ b/assets/lib/aplayer/dark.scss @@ -1,44 +1,55 @@ -[data-theme=dark] .aplayer { - background: #212121; +@mixin aplayer-dark-theme { + $aplayer-bg: #181f28; + $aplayer-bg-elevated: #1f2731; + $aplayer-bg-hover: #252f3a; + $aplayer-bg-active: #2b3642; + $aplayer-border: #313a45; + $aplayer-text: #d1d7e0; + $aplayer-text-secondary: #9aa5b1; + $aplayer-text-muted: #838d98; + $aplayer-shadow: #161b22; + $aplayer-switcher-bg: #232d39; + + background: $aplayer-bg; &.aplayer-withlist { .aplayer-info { - border-bottom-color: #5c5c5c; + border-bottom-color: $aplayer-border; } } &.aplayer-fixed { .aplayer-list { - border-color: #5c5c5c; + border-color: $aplayer-border; } } .aplayer-body { - background-color: #212121; + background-color: $aplayer-bg; } .aplayer-info { - border-top-color: #212121; + border-top-color: $aplayer-border; } .aplayer-info { .aplayer-music { .aplayer-title { - color: #fff; + color: $aplayer-text; } .aplayer-author { - color: #fff; + color: $aplayer-text-secondary; } } .aplayer-controller { .aplayer-time { - color: #eee; + color: $aplayer-text-secondary; .aplayer-icon { path { - fill: #eee; + fill: $aplayer-text-secondary; } } } @@ -46,59 +57,69 @@ } .aplayer-list { - background-color: #212121; + background-color: $aplayer-bg; li { - color: #fff; - border-top-color: #666; + color: $aplayer-text; + border-top-color: $aplayer-border; &:hover { - background: #4e4e4e; + background: $aplayer-bg-hover; } &.aplayer-list-light { - background: #6c6c6c; + background: $aplayer-bg-active; } .aplayer-list-index { - color: #ddd; + color: $aplayer-text-muted; } .aplayer-list-author { - color: #ddd; + color: $aplayer-text-secondary; } } } .aplayer-lrc { - text-shadow: -1px -1px 0 #666; + text-shadow: -1px -1px 0 $aplayer-shadow; &:before { - background: -moz-linear-gradient(top, rgba(33,33,33,1) 0%, rgba(33,33,33,0) 100%); - background: -webkit-linear-gradient(top, rgba(33,33,33,1) 0%,rgba(33,33,33,0) 100%); - background: linear-gradient(to bottom, rgba(33,33,33,1) 0%,rgba(33,33,33,0) 100%); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#212121', endColorstr='#00212121',GradientType=0 ); + background: -moz-linear-gradient(top, rgba(24,31,40,1) 0%, rgba(24,31,40,0) 100%); + background: -webkit-linear-gradient(top, rgba(24,31,40,1) 0%, rgba(24,31,40,0) 100%); + background: linear-gradient(to bottom, rgba(24,31,40,1) 0%, rgba(24,31,40,0) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#181f28', endColorstr='#00181f28',GradientType=0 ); } &:after { - background: -moz-linear-gradient(top, rgba(33,33,33,0) 0%, rgba(33,33,33,0.8) 100%); - background: -webkit-linear-gradient(top, rgba(33,33,33,0) 0%,rgba(33,33,33,0.8) 100%); - background: linear-gradient(to bottom, rgba(33,33,33,0) 0%,rgba(33,33,33,0.8) 100%); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00212121', endColorstr='#cc212121',GradientType=0 ); + background: -moz-linear-gradient(top, rgba(24,31,40,0) 0%, rgba(24,31,40,0.9) 100%); + background: -webkit-linear-gradient(top, rgba(24,31,40,0) 0%, rgba(24,31,40,0.9) 100%); + background: linear-gradient(to bottom, rgba(24,31,40,0) 0%, rgba(24,31,40,0.9) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00181f28', endColorstr='#e6181f28',GradientType=0 ); } p { - color: #fff; + color: $aplayer-text; } } .aplayer-miniswitcher { - background: #484848; + background: $aplayer-switcher-bg; .aplayer-icon { path { - fill: #eee; + fill: $aplayer-text-secondary; } } } } + +[data-theme-mode='dark'] .aplayer { + @include aplayer-dark-theme; +} + +@media (prefers-color-scheme: dark) { + [data-theme-mode='auto'] .aplayer { + @include aplayer-dark-theme; + } +} diff --git a/assets/lib/valine/valine.scss b/assets/lib/valine/valine.scss index 38ab73b5..bbbf806e 100644 --- a/assets/lib/valine/valine.scss +++ b/assets/lib/valine/valine.scss @@ -1,7 +1,7 @@ // ========== Global ========== // // Color of the border -$border-color: #f0f0f0; -$border-color-dark: #363636; +$border-color: #d7dee4; +$border-color-dark: #383f47; // ========== Code ========== // // Color of the code @@ -9,8 +9,8 @@ $code-color: #E74C3C !default; $code-color-dark: #E5BF78 !default; // Color of the code background -$code-background-color: #f5f5f5 !default; -$code-background-color-dark: #272C34 !default; +$code-background-color: #f4f6f8 !default; +$code-background-color-dark: #2d333B !default; @mixin overflow-wrap($value) { word-wrap: $value; @@ -32,23 +32,14 @@ $code-background-color-dark: #272C34 !default; .vcards .vcard .vh, .vcards .vcard .vquote, blockquote { - border-color: $border-color; - - [data-theme=dark] & { - border-color: $border-color-dark; - } + border-color: light-dark($border-color, $border-color-dark); } code, pre, pre code { font-size: 90%; tab-size: 4; - color: $code-color; - background-color: $code-background-color; + color: light-dark($code-color, $code-color-dark); + background-color: light-dark($code-background-color, $code-background-color-dark); @include overflow-wrap(break-word); - - [data-theme=dark] & { - color: $code-color-dark; - background-color: $code-background-color-dark; - } } } diff --git a/hugo.toml b/hugo.toml index 33087fd5..d37f5a43 100644 --- a/hugo.toml +++ b/hugo.toml @@ -554,7 +554,7 @@ 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 +blur = false # Header title config [params.header.title] diff --git a/layouts/_partials/plugin/timeline.html b/layouts/_partials/plugin/timeline.html index 3b59e223..2ba19c84 100644 --- a/layouts/_partials/plugin/timeline.html +++ b/layouts/_partials/plugin/timeline.html @@ -49,7 +49,7 @@ {{- with $animation }} data-animation{{- end -}} > {{- range $index, $item := $events -}} - {{- $itemStyle := printf "--timeline-index: %v;" (add $index 1) -}} + {{- $itemStyle := printf "--fi-timeline-index: %v;" (add $index 1) -}} {{- with $item.color -}} {{- $itemStyle = add $itemStyle (printf "--timeline-circle-color: %v;" .) -}} {{- end -}} diff --git a/layouts/_shortcodes/auto-dark.html b/layouts/_shortcodes/auto-dark.html new file mode 100644 index 00000000..db8207c8 --- /dev/null +++ b/layouts/_shortcodes/auto-dark.html @@ -0,0 +1,7 @@ +{{- /* + Auto adapt to dark mode by inverting the color and hue + + @example {{< auto-dark >}}...{{< /auto-dark >}} +*/ -}} +
{{ .Inner }}
+{{- /* EOF */ -}} \ No newline at end of file