diff --git a/.gitignore b/.gitignore index fc20c978..d5f84606 100644 --- a/.gitignore +++ b/.gitignore @@ -30,5 +30,6 @@ $RECYCLE.BIN/ .directory CHANGELOG.md -.stash/ docs +.vercel +.env*.local diff --git a/apps/test/content/posts/codeblock-test.md b/apps/test/content/posts/codeblock-test.md index dc2be99a..0dc409c7 100644 --- a/apps/test/content/posts/codeblock-test.md +++ b/apps/test/content/posts/codeblock-test.md @@ -18,7 +18,7 @@ Testing code block in FixIt theme. 🎉🥚 in development mode! ``` -## Multiple modes +## Multiple `modes` ```js {mode="classic"} // Classic mode diff --git a/apps/test/content/posts/mermaid-diagrams-test.md b/apps/test/content/posts/mermaid-diagrams-test.md index a5d24e6c..c652989f 100644 --- a/apps/test/content/posts/mermaid-diagrams-test.md +++ b/apps/test/content/posts/mermaid-diagrams-test.md @@ -13,7 +13,7 @@ tags: The FixIt theme supports two ways to use Mermaid: code block syntax and shortcode syntax. -```mermaid +```mermaid {wrapper=false} --- config: theme: 'forest' diff --git a/assets/js/lib/mermaid.js b/assets/js/lib/mermaid.js index 6821b29d..ce0ad8a3 100644 --- a/assets/js/lib/mermaid.js +++ b/assets/js/lib/mermaid.js @@ -481,7 +481,7 @@ function initDiagramControls() { return Array.from(codeHeader.querySelectorAll('.action-btn')) .filter((btn) => btn !== pinnedFullscreenBtn) } - return Array.from(codeBlock.querySelectorAll('.code-copy-btn')) + return Array.from(codeBlock.querySelectorAll('.copy-icon-btn')) } const getCodeActions = () => { diff --git a/assets/js/pages/link.js b/assets/js/pages/link.js index 95d1e833..cb746b0f 100644 --- a/assets/js/pages/link.js +++ b/assets/js/pages/link.js @@ -10,7 +10,7 @@ function initLinkGuard() { const params = new URLSearchParams(window.location.search); const target = params.get('target'); const targetElement = document.querySelector('.target'); - const copyBtn = document.querySelector('.copy-btn'); + const copyBtn = document.querySelector('.copy-icon-btn'); const confirmBtn = document.querySelector('.confirm-btn'); if (target) { diff --git a/assets/js/theme.js b/assets/js/theme.js index 7abdfc6d..822fc3f6 100644 --- a/assets/js/theme.js +++ b/assets/js/theme.js @@ -542,7 +542,7 @@ class FixIt { initCopyCode(codeBlock, codePreEl) { const copyBtn = codeBlock.dataset.mode === 'classic' ? codeBlock.querySelector('.code-header .copy-btn') - : codeBlock.querySelector('.code-copy-btn'); + : codeBlock.querySelector('.copy-icon-btn'); if (codeBlock.dataset.copyable !== 'true' || !copyBtn) return; copyBtn.addEventListener('click', () => { const iswWrap = codeBlock.classList.contains('line-wrapping'); @@ -912,7 +912,7 @@ class FixIt { */ initDiagramCopyBtn() { const stagingDOM = getStagingDOM() - forEach(document.querySelectorAll('.diagram-copy-btn'), ($btn) => { + forEach(document.querySelectorAll('.diagram-container > .copy-icon-btn'), ($btn) => { $btn.addEventListener('click', () => { stagingDOM.stage($btn.parentElement.querySelector('template').content.cloneNode(true)) let code = stagingDOM.contentAsText(); @@ -920,10 +920,18 @@ class FixIt { code = JSON.stringify(JSON.parse(code), null, 2); } catch { } copyText(code).then(() => { + const copiedText = $btn.dataset.copiedText; + const originalTitle = $btn.dataset.ctOriginalTitle; $btn.toggleAttribute('data-copied', true); + $btn.dataset.ctTitle = copiedText; + const instance = window.CellTooltip.getOrCreateInstance($btn); + instance.refresh(); setTimeout(() => { $btn.toggleAttribute('data-copied', false); + $btn.dataset.ctTitle = originalTitle; + instance.hide(); }, 2000); + $btn.toggleAttribute('data-copied', true); }, () => { console.error('Clipboard write failed!', 'Your browser does not support clipboard API!'); }); @@ -1631,6 +1639,10 @@ class FixIt { window.CellTooltip.initAll('.action-btn[title]', { placement: 'bottom', }); + // copy icon button tooltip + window.CellTooltip.initAll('.copy-icon-btn[title]', { + placement: 'top', + }); // footnote refs tooltip this.initFootnotes(); } @@ -1644,7 +1656,7 @@ class FixIt { if (!dialog) return; const $target = dialog.querySelector('.target'); - const $copy = dialog.querySelector('.copy-btn'); + const $copy = dialog.querySelector('.copy-icon-btn'); const $confirm = dialog.querySelector('.confirm-btn'); const $cancel = dialog.querySelector('.cancel-btn'); diff --git a/assets/scss/_variables.scss b/assets/scss/_variables.scss index e9ec53d8..f9b8798c 100644 --- a/assets/scss/_variables.scss +++ b/assets/scss/_variables.scss @@ -3,6 +3,8 @@ // Note: These SCSS variables are now also defined as CSS custom properties // in _core/_root.scss for runtime theme switching and runtime customization. // ========================================================================== +@forward "core/maps"; +@use "sass:color"; // ========== Global ========== // $prefix: fi- !default; @@ -30,8 +32,8 @@ $global-font-secondary-color: #8b949e !default; $global-font-secondary-color-dark: #7d8792 !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; +$global-font-tertiary-color: color.adjust($global-font-secondary-color, $lightness: 12%) !default; +$global-font-tertiary-color-dark: color.adjust($global-font-secondary-color-dark, $lightness: -12%) !default; // Color of the link $global-link-color: #161209 !default; @@ -135,7 +137,7 @@ $blockquote-color-dark: #9ba3aa !default; // Color of reward $reward-color: tomato !default; -$reward-color-dark: darken($reward-color, 5%) !default; +$reward-color-dark: color.adjust($reward-color, $lightness: -5%) !default; // Width of reward image $reward-img-width: 180px !default; @@ -192,5 +194,3 @@ $github-corner-color-dark: black !default; $github-corner-fill: $header-background-color-dark !default; $github-corner-fill-dark: $header-background-color !default; // ========== GitHub Corners ========== // - -@import 'core/maps'; diff --git a/assets/scss/config.template.scss b/assets/scss/config.template.scss index b15c5a59..e1670583 100644 --- a/assets/scss/config.template.scss +++ b/assets/scss/config.template.scss @@ -3,7 +3,8 @@ @charset "utf-8"; -@import "_variables"; +@use "override" as *; + $base: "{{ relURL "" }}" !default; :root { diff --git a/assets/scss/core/_common.scss b/assets/scss/core/_common.scss index 590c6e8b..4d50f013 100644 --- a/assets/scss/core/_common.scss +++ b/assets/scss/core/_common.scss @@ -2,6 +2,9 @@ // Common Styles // 常用样式 // ============================== +@use "functions" as *; +@use "mixins" as *; +@use "maps" as maps; .text-truncate { overflow: hidden; @@ -39,13 +42,13 @@ padding-right: 0.25em !important; } -@each $color, $value in $theme-colors { +@each $color, $value in maps.$theme-colors { .text-#{$color} { color: fi-var(#{$color}) !important; } } -@each $key, $value in $orders { +@each $key, $value in maps.$orders { .order-#{$key} { order: #{$value} !important; } diff --git a/assets/scss/core/_index.scss b/assets/scss/core/_index.scss index d7b070c0..5d752bff 100644 --- a/assets/scss/core/_index.scss +++ b/assets/scss/core/_index.scss @@ -1,6 +1,4 @@ -@import "functions"; -@import "mixins"; -@import "common"; -@import "reboot"; -@import "root"; -@import "layouts"; +@use "common"; +@use "reboot"; +@use "root"; +@use "layouts"; diff --git a/assets/scss/core/_reboot.scss b/assets/scss/core/_reboot.scss index 2de9d3b5..43a40b3a 100644 --- a/assets/scss/core/_reboot.scss +++ b/assets/scss/core/_reboot.scss @@ -4,7 +4,8 @@ /// styles targeting irrelevant browsers while applying new styles. /// /// Normalize is licensed MIT. https://github.com/necolas/normalize.css - +@use "functions" as *; +@use "mixins" as *; // 1. Correct the line height in all browsers. @@ -341,17 +342,4 @@ dialog { background-color: fi-var(selection-color); } -%code { - padding: 0.2em 0.4em; - margin: 0; - font-size: fi-var(code-font-size); - line-height: fi-var(code-font-size); - font-family: fi-var(code-font-family); - color: fi-var(code-color); - background-color: fi-var(code-inline-background-color); - text-decoration: inherit; - @include border-radius; - @include overflow-wrap(break-word); -} - @include link(true, true); diff --git a/assets/scss/core/_root.scss b/assets/scss/core/_root.scss index d3f33fad..363a39f8 100644 --- a/assets/scss/core/_root.scss +++ b/assets/scss/core/_root.scss @@ -13,6 +13,10 @@ // // These CSS variables enable runtime theme switching and customization. // ================================================================================ +@use "sass:color"; +@use "functions" as *; +@use "mixins" as *; +@use "override" as *; :root { interpolate-size: allow-keywords; @@ -92,8 +96,8 @@ 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), - global-border-color-light: (light: lighten($global-border-color, 5%), dark: darken($global-border-color-dark, 5%)), - global-border-color-weight: (light: darken($global-border-color, 5%), dark: lighten($global-border-color-dark, 5%)), + global-border-color-light: (light: color.adjust($global-border-color, $lightness: 5%), dark: color.adjust($global-border-color-dark, $lightness: -5%)), + global-border-color-weight: (light: color.adjust($global-border-color, $lightness: -5%), dark: color.adjust($global-border-color-dark, $lightness: 5%)), // Header header-background-color: (light: $header-background-color, dark: $header-background-color-dark), @@ -131,7 +135,7 @@ reward-color: (light: $reward-color, dark: $reward-color-dark), // Divider - hr-active-color: (light: lighten($single-link-hover-color, 4%), dark: darken($global-link-hover-color-dark, 4%)), + hr-active-color: (light: color.adjust($single-link-hover-color, $lightness: 4%), dark: color.adjust($global-link-hover-color-dark, $lightness: -4%)), // Code code-color: (light: $code-color, dark: $code-color-dark), @@ -140,7 +144,6 @@ code-header-color: (light: $code-header-color, dark: $code-header-color-dark), 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%)), )); // Third-party component theme colors (extensions and plugins) diff --git a/assets/scss/core/functions/_index.scss b/assets/scss/core/functions/_index.scss index 486f9dd0..15e1e37b 100644 --- a/assets/scss/core/functions/_index.scss +++ b/assets/scss/core/functions/_index.scss @@ -1,2 +1,2 @@ -@import 'rgba'; -@import 'var'; +@forward "rgba" as fi-*; +@forward "var" as fi-*; diff --git a/assets/scss/core/functions/_rgba.scss b/assets/scss/core/functions/_rgba.scss index 1b212871..f421cf73 100644 --- a/assets/scss/core/functions/_rgba.scss +++ b/assets/scss/core/functions/_rgba.scss @@ -1,8 +1,10 @@ +@use "sass:string"; + /// Build an rgba-like color from any source color expression. /// @param {Color | String} $color - Source color, supports CSS vars such as fi-var(secondary). /// @param {Number} $alpha [1] - Alpha channel value in range 0..1. /// @example /// color: fi-rgba(fi-var(secondary), 0.2); -@function fi-rgba($color, $alpha: 1) { - @return unquote("rgb(from #{$color} r g b / #{$alpha})"); +@function rgba($color, $alpha: 1) { + @return string.unquote("rgb(from #{$color} r g b / #{$alpha})"); } diff --git a/assets/scss/core/functions/_var.scss b/assets/scss/core/functions/_var.scss index 96903ea6..b8158eff 100644 --- a/assets/scss/core/functions/_var.scss +++ b/assets/scss/core/functions/_var.scss @@ -1,3 +1,6 @@ +@use "sass:string"; +@use "override" as *; + /// Get CSS variable with prefix /// @param {String} $name - CSS variable name (without prefix) /// @param {*} $fallback - Optional fallback value @@ -5,10 +8,10 @@ /// @example /// color: fi-var(global-font-color); /// background: fi-var(custom-bg, #fff); -@function fi-var($name, $fallback: null) { - @if $fallback { - @return var(#{$rootPrefix}#{$name}, #{$fallback}); - } @else { - @return var(#{$rootPrefix}#{$name}); - } +@function var($name, $fallback: null) { + @if $fallback != null { + @return string.unquote("var(#{$rootPrefix}#{$name}, #{$fallback})"); + } + + @return string.unquote("var(#{$rootPrefix}#{$name})"); } diff --git a/assets/scss/core/layouts/_breadcrumb.scss b/assets/scss/core/layouts/_breadcrumb.scss index fc7eca91..a7362968 100644 --- a/assets/scss/core/layouts/_breadcrumb.scss +++ b/assets/scss/core/layouts/_breadcrumb.scss @@ -1,5 +1,8 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + .breadcrumb-container { - padding-inline: 1rem; + padding-inline: 0.5rem; font-size: 0.75rem; background-color: fi-var(global-background-color); display: flex; @@ -53,13 +56,13 @@ .breadcrumb { display: flex; flex-wrap: nowrap; - padding: 0.5rem 2px; + padding: 0.5rem; margin: 0 auto; list-style: none; box-shadow: inset 0 -1px 0 fi-var(global-border-color); overflow-x: auto; overflow-y: hidden; - @extend %page-style; + box-sizing: border-box; .breadcrumb-item { display: flex; diff --git a/assets/scss/core/layouts/_comment.scss b/assets/scss/core/layouts/_comment.scss index 143d27ad..8273a6e1 100644 --- a/assets/scss/core/layouts/_comment.scss +++ b/assets/scss/core/layouts/_comment.scss @@ -1,5 +1,7 @@ -body > #comments { - padding: 2rem 1rem; +@use "core/mixins" as *; + +.wrapper > #comments { + padding: 2rem 0.5rem; display: flex; &::before, @@ -14,7 +16,9 @@ body > #comments { } .comment { - @extend %page-style; + margin: 0 auto; + padding-inline: 0.5rem; + box-sizing: border-box; iframe { width: 100%; diff --git a/assets/scss/core/layouts/_footer.scss b/assets/scss/core/layouts/_footer.scss index b27af482..c3fc884a 100644 --- a/assets/scss/core/layouts/_footer.scss +++ b/assets/scss/core/layouts/_footer.scss @@ -1,3 +1,5 @@ +@use "core/mixins" as *; + footer { height: auto; width: 100%; diff --git a/assets/scss/core/layouts/_header.scss b/assets/scss/core/layouts/_header.scss index d507877b..54a466c7 100644 --- a/assets/scss/core/layouts/_header.scss +++ b/assets/scss/core/layouts/_header.scss @@ -1,3 +1,7 @@ +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; + $header-box-shadow: 0 0 1.5rem 0 light-dark(rgba(0, 0, 0, 0.125), rgba(255, 255, 255, 0.125)); header { @@ -10,12 +14,12 @@ header { box-shadow: $header-box-shadow; } - @include scroll-state-query('scroller scroll-state(scrollable: top)') { + @include scroll-state-query('scroller', 'scroll-state(scrollable: top)') { box-shadow: $header-box-shadow; } &.blur { - @include scroll-state-query('scroller scroll-state(scrollable: top)', '&') { + @include scroll-state-query('scroller', 'scroll-state(scrollable: top)', '&') { background-color: transparent; backdrop-filter: saturate(1.2) blur(10px); background-size: 4px 4px; diff --git a/assets/scss/core/layouts/_index.scss b/assets/scss/core/layouts/_index.scss index 2ad60680..18a312ec 100644 --- a/assets/scss/core/layouts/_index.scss +++ b/assets/scss/core/layouts/_index.scss @@ -1,88 +1,7 @@ -// Responsive layout -.wrapper { - display: flex; - flex-direction: column; - min-height: 100vh; - width: 100%; - - main { - flex: 1 0 auto; - - aside { - flex: 1; - padding: 0.5rem; - max-width: 500px; - - &:not(:has(~ aside)) { - display: none; - } - - @include media('sm', 'down') { - display: none !important; - } - } - - &.#{$prefix}container { - display: flex; - align-items: flex-start; - justify-content: center; - padding-inline: 0.5rem; - - &[data-reverse] { - flex-direction: row-reverse; - } - - &:has(.home:not(.posts)) { - align-items: center; - } - } - } - - @include media('md') { - main { - // hide empty aside and expand article when only one aside is empty - aside:first-child:empty:has(~ aside:not(:empty)), - aside:first-child:not(:empty) ~ aside#toc-auto:empty { - display: none; - } - - aside:first-child:empty:has(~ aside:not(:empty)) ~ article, - aside:first-child:not(:empty) ~ article:has(~ aside:empty) { - flex: 3; - } - } - - // when left aside is hidden: expand content and remove left spacer - &:has(main > aside:first-child:empty ~ aside:not(:empty)) { - .breadcrumb-container .breadcrumb, - #comments .comment { - flex: 3; - } - - .breadcrumb-container::before, - #comments::before { - display: none; - } - } - - // when right aside is hidden: expand content and remove right spacer - &:has(main > aside:first-child:not(:empty) ~ aside#toc-auto:empty) { - .breadcrumb-container .breadcrumb, - #comments .comment { - flex: 3; - } - - .breadcrumb-container::after, - #comments::after { - display: none; - } - } - } -} - -@import "header"; -@import "breadcrumb"; -@import "page-style"; -@import "pagination"; -@import "comment"; -@import "footer"; +@use "wrapper"; +@use "header"; +@use "page-style"; +@use "breadcrumb"; +@use "pagination"; +@use "comment"; +@use "footer"; diff --git a/assets/scss/core/layouts/_page-style.scss b/assets/scss/core/layouts/_page-style.scss index 0dc3265e..2d235c2d 100644 --- a/assets/scss/core/layouts/_page-style.scss +++ b/assets/scss/core/layouts/_page-style.scss @@ -1,47 +1,24 @@ -%page-style { +@use "core/mixins" as *; + +@include page-style; + +@include page-style('wide') { @include media('xl') { - width: ROUND(60%, 2px); - max-width: 1200px; + width: ROUND(64%, 2px); + max-width: 1600px; } @include media('lg') { - width: ROUND(56%, 2px); + width: ROUND(60%, 2px); } @include media('md') { - width: ROUND(52%, 2px); - } - - @include media('sm') { - width: ROUND(80%, 2px); - } - - @include media('xs') { - width: 100%; + width: ROUND(56%, 2px); } } -[data-page-style='wide'] { - %page-style { - @include media('xl') { - width: ROUND(64%, 2px); - max-width: 1600px; - } - - @include media('lg') { - width: ROUND(60%, 2px); - } - - @include media('md') { - width: ROUND(56%, 2px); - } - } -} - -[data-page-style='narrow'] { - %page-style { - @include media('xl') { - max-width: 800px; - } +@include page-style('narrow') { + @include media('xl') { + max-width: 800px; } } diff --git a/assets/scss/core/layouts/_pagination.scss b/assets/scss/core/layouts/_pagination.scss index f26e7e36..f9ac6fab 100644 --- a/assets/scss/core/layouts/_pagination.scss +++ b/assets/scss/core/layouts/_pagination.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + .pagination { display: flex; flex-direction: row; diff --git a/assets/scss/core/layouts/_wrapper.scss b/assets/scss/core/layouts/_wrapper.scss new file mode 100644 index 00000000..545c9ea4 --- /dev/null +++ b/assets/scss/core/layouts/_wrapper.scss @@ -0,0 +1,84 @@ +@use "core/mixins" as *; +@use "override" as *; + +// Responsive layout +.wrapper { + display: flex; + flex-direction: column; + min-height: 100vh; + width: 100%; + + main { + flex: 1 0 auto; + + aside { + flex: 1; + padding: 0.5rem; + max-width: 500px; + + &:not(:has(~ aside)) { + display: none; + } + + @include media('sm', 'down') { + display: none !important; + } + } + + &.#{$prefix}container { + display: flex; + align-items: flex-start; + justify-content: center; + padding-inline: 0.5rem; + + &[data-reverse] { + flex-direction: row-reverse; + } + + &:has(.home:not(.posts)) { + align-items: center; + } + } + } + + @include media('md') { + main { + // hide empty aside and expand article when only one aside is empty + aside:first-child:empty:has(~ aside:not(:empty)), + aside:first-child:not(:empty) ~ aside#toc-auto:empty { + display: none; + } + + aside:first-child:empty:has(~ aside:not(:empty)) ~ article, + aside:first-child:not(:empty) ~ article:has(~ aside:empty) { + flex: 3; + } + } + + // when left aside is hidden: expand content and remove left spacer + &:has(main > aside:first-child:empty ~ aside:not(:empty)) { + .breadcrumb-container .breadcrumb, + #comments .comment { + flex: 3; + } + + .breadcrumb-container::before, + #comments::before { + display: none; + } + } + + // when right aside is hidden: expand content and remove right spacer + &:has(main > aside:first-child:not(:empty) ~ aside#toc-auto:empty) { + .breadcrumb-container .breadcrumb, + #comments .comment { + flex: 3; + } + + .breadcrumb-container::after, + #comments::after { + display: none; + } + } + } +} diff --git a/assets/scss/core/maps/_index.scss b/assets/scss/core/maps/_index.scss index c7d05a1f..aea25988 100644 --- a/assets/scss/core/maps/_index.scss +++ b/assets/scss/core/maps/_index.scss @@ -1,5 +1,5 @@ -@import 'admonition'; -@import 'alert'; -@import 'code-type'; -@import 'colors'; -@import 'utilities'; +@forward 'admonition'; +@forward 'alert'; +@forward 'code-type'; +@forward 'colors'; +@forward 'utilities'; diff --git a/assets/scss/core/mixins/_bold-dark.scss b/assets/scss/core/mixins/_bold-dark.scss index 9e67c490..a033740e 100644 --- a/assets/scss/core/mixins/_bold-dark.scss +++ b/assets/scss/core/mixins/_bold-dark.scss @@ -1,3 +1,5 @@ +@use "theme-vars" as *; + /// 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 diff --git a/assets/scss/core/mixins/_border-radius.scss b/assets/scss/core/mixins/_border-radius.scss index 7c7466c5..8e340458 100644 --- a/assets/scss/core/mixins/_border-radius.scss +++ b/assets/scss/core/mixins/_border-radius.scss @@ -1,3 +1,5 @@ +@use "core/functions" as *; + /// Mixin to set border-radius /// @param {String|Number|List} $value - The value of the border-radius, position (top, bottom, left, right) or keyword (full) /// @param {Number} $radius - The radius of the border (only for position) @@ -7,19 +9,17 @@ /// @include border-radius(top); /// @include border-radius(top, 10px); /// @include border-radius(10px 20px 10px 20px); -@mixin border-radius($value: fi-var(global-border-radius), $radius: null) { - $r: if($radius, $radius, fi-var(global-border-radius)); - +@mixin border-radius($value: fi-var(global-border-radius), $radius: fi-var(global-border-radius)) { @if $value == full { border-radius: calc(infinity * 1px); } @else if $value == top { - border-radius: $r $r 0 0; + border-radius: $radius $radius 0 0; } @else if $value == bottom { - border-radius: 0 0 $r $r; + border-radius: 0 0 $radius $radius; } @else if $value == left { - border-radius: $r 0 0 $r; + border-radius: $radius 0 0 $radius; } @else if $value == right { - border-radius: 0 $r $r 0; + border-radius: 0 $radius $radius 0; } @else { border-radius: $value; } diff --git a/assets/scss/core/mixins/_box-shadow.scss b/assets/scss/core/mixins/_box-shadow.scss index 96eead4e..5929b004 100644 --- a/assets/scss/core/mixins/_box-shadow.scss +++ b/assets/scss/core/mixins/_box-shadow.scss @@ -1,3 +1,5 @@ +@use "core/functions" as *; + /// Mixin to apply box-shadow with timing control /// @param {String} $when - When to show shadow: always | hover | never (default: always) /// @param {List} $shadow - The box-shadow value diff --git a/assets/scss/core/mixins/_compatibility.scss b/assets/scss/core/mixins/_compatibility.scss index cdd23daf..66440388 100644 --- a/assets/scss/core/mixins/_compatibility.scss +++ b/assets/scss/core/mixins/_compatibility.scss @@ -1,3 +1,5 @@ +@use "core/functions" as *; + @mixin placeholder($color: fi-var(global-font-tertiary-color)) { input::placeholder { color: $color; @@ -13,19 +15,26 @@ /// 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} $container-name - Optional container name +/// @param {String} $container-condition - Container query condition (without outer parentheses) /// @param {String|null} $fallback-selector - Optional selector used as a fallback /// @example -/// @include scroll-state-query('scroller scroll-state(scrollable: top)') { +/// @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)', '&') { +/// @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) { +@mixin scroll-state-query($container-name, $container-condition, $fallback-selector: null) { @supports (container-type: scroll-state) { - @container #{$container-query} { - @content; + @if $container-name != null { + @container #{$container-name} (#{$container-condition}) { + @content; + } + } @else { + @container (#{$container-condition}) { + @content; + } } } diff --git a/assets/scss/core/mixins/_index.scss b/assets/scss/core/mixins/_index.scss index 45696ca4..5d768284 100644 --- a/assets/scss/core/mixins/_index.scss +++ b/assets/scss/core/mixins/_index.scss @@ -1,14 +1,16 @@ -@import 'bold-dark'; -@import 'border-radius'; -@import 'box-shadow'; -@import 'compatibility'; -@import 'link'; -@import 'link-guard'; -@import 'loading'; -@import 'media'; -@import 'scrollbar-width'; -@import 'sticky-top'; -@import 'task-list'; -@import 'theme-switch'; -@import 'theme-vars'; -@import 'z-index'; +@forward 'bold-dark'; +@forward 'border-radius'; +@forward 'box-shadow'; +@forward 'compatibility'; +@forward 'inline-code'; +@forward 'link'; +@forward 'link-guard'; +@forward 'loading'; +@forward 'media'; +@forward 'page-style'; +@forward 'scrollbar-width'; +@forward 'sticky-top'; +@forward 'task-list'; +@forward 'theme-switch'; +@forward 'theme-vars'; +@forward 'z-index'; diff --git a/assets/scss/core/mixins/_inline-code.scss b/assets/scss/core/mixins/_inline-code.scss new file mode 100644 index 00000000..25521c1b --- /dev/null +++ b/assets/scss/core/mixins/_inline-code.scss @@ -0,0 +1,16 @@ +@use "core/functions" as *; +@use "border-radius" as *; +@use "compatibility" as *; + +@mixin inline-code() { + padding: 0.2em 0.4em; + margin: 0; + font-size: fi-var(code-font-size); + line-height: fi-var(code-font-size); + font-family: fi-var(code-font-family); + color: fi-var(code-color); + background-color: fi-var(code-inline-background-color); + text-decoration: inherit; + @include border-radius; + @include overflow-wrap(break-word); +} diff --git a/assets/scss/core/mixins/_link-guard.scss b/assets/scss/core/mixins/_link-guard.scss index 1a00356a..840d1a13 100644 --- a/assets/scss/core/mixins/_link-guard.scss +++ b/assets/scss/core/mixins/_link-guard.scss @@ -1,3 +1,7 @@ +@use "core/functions" as *; +@use "border-radius" as *; +@use "media" as *; + /// Shared styles for link guard content blocks. /// Keep this as the single source of truth for both link page card and guard dialog body. @mixin link-guard-content { @@ -41,24 +45,9 @@ @include border-radius; } - .copy-btn { - box-sizing: content-box; - width: 1rem; - height: 1rem; + .copy-icon-btn { flex-shrink: 0; - padding: 0.5rem; - line-height: 1; background: light-dark(#f8fafc, #171b25); - border: 1px solid fi-var(global-border-color); - color: fi-var(global-font-secondary-color); - user-select: none; - @include border-radius; - - &[data-copied] .fa-clone { - --fa: "\f00c"; - font-weight: 900; - color: fi-var(global-link-hover-color); - } } .actions { diff --git a/assets/scss/core/mixins/_link.scss b/assets/scss/core/mixins/_link.scss index 4eb76cfe..fc2a2a1f 100644 --- a/assets/scss/core/mixins/_link.scss +++ b/assets/scss/core/mixins/_link.scss @@ -1,12 +1,24 @@ +@use "override" as *; + /// Link mixin /// @param {Boolean} $light use global link color /// @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); + $link-color-light: $single-link-color; + $link-color-dark: $single-link-color-dark; + $link-hover-color-light: $single-link-hover-color; + $link-hover-color-dark: $single-link-hover-color-dark; + + @if $light { + $link-color-light: $global-link-color; + $link-hover-color-light: $global-link-hover-color; + } + + @if $dark { + $link-color-dark: $global-link-color-dark; + $link-hover-color-dark: $global-link-hover-color-dark; + } a, a::before, diff --git a/assets/scss/core/mixins/_loading.scss b/assets/scss/core/mixins/_loading.scss index 5e984ccf..5b492fca 100644 --- a/assets/scss/core/mixins/_loading.scss +++ b/assets/scss/core/mixins/_loading.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "z-index" as *; + // Loading Mixin for before/after pseudo element @mixin loading { content: ''; diff --git a/assets/scss/core/mixins/_media.scss b/assets/scss/core/mixins/_media.scss index c91e241c..481d691a 100644 --- a/assets/scss/core/mixins/_media.scss +++ b/assets/scss/core/mixins/_media.scss @@ -3,6 +3,8 @@ // │ xs │ sm │ md │ lg │ xl │ // │ ≤680 │ 681–960 │ 961–1200 │ 1201–1440 │ ≥1441 │ // └──────────────────────────────────────────────────────┘ +@use "sass:map"; + $breakpoints: ( 'xs': ( 'min': null, @@ -53,9 +55,14 @@ $breakpoints: ( @content; } } @else { - $range: map-get($breakpoints, $target); - $min: if($range != null, map-get($range, 'min'), null); - $max: if($range != null, map-get($range, 'max'), null); + $range: map.get($breakpoints, $target); + $min: null; + $max: null; + + @if $range != null { + $min: map.get($range, 'min'); + $max: map.get($range, 'max'); + } @if $range == null { @warn "Unknown media target `#{$target}`. Expected: xs, sm, md, lg, xl, print, reduce-motion."; diff --git a/assets/scss/core/mixins/_page-style.scss b/assets/scss/core/mixins/_page-style.scss new file mode 100644 index 00000000..255e7d6f --- /dev/null +++ b/assets/scss/core/mixins/_page-style.scss @@ -0,0 +1,54 @@ +@use "media" as *; + +/// Page style helper for setting responsive content width. +/// +/// This mixin has two modes: +/// 1) Default mode (no arguments): emits the base page-style rules for all built-in targets. +/// 2) Variant mode (pass $variant): emits rules under `[data-page-style='$variant']` for the same targets. +/// +/// @param {String | Null} $variant - Variant name (e.g. wide/narrow/custom). When null, emit defaults. +/// +/// @example +/// // Theme internal: emit default rules +/// @include page-style; +/// +/// // Theme internal: emit a built-in variant +/// @include page-style('wide') { ... } +/// @include page-style('narrow') { ... } +/// +/// // User custom: add a custom variant without caring about internal selectors +/// @include page-style('custom') { +/// @include media('xl') { width: ROUND(70%, 2px); } +/// } +@mixin page-style($variant: null) { + $target: ':is(main > .page, .breadcrumb-container > .breadcrumb, #comments > .comment)'; + + @if $variant == null { + @at-root #{$target} { + @include media('xl') { + width: ROUND(60%, 2px); + max-width: 1200px; + } + + @include media('lg') { + width: ROUND(56%, 2px); + } + + @include media('md') { + width: ROUND(52%, 2px); + } + + @include media('sm') { + width: ROUND(80%, 2px); + } + + @include media('xs') { + width: 100%; + } + } + } @else { + @at-root [data-page-style='#{$variant}'] #{$target} { + @content; + } + } +} diff --git a/assets/scss/core/mixins/_scrollbar-width.scss b/assets/scss/core/mixins/_scrollbar-width.scss index a83ed534..83ee8c20 100644 --- a/assets/scss/core/mixins/_scrollbar-width.scss +++ b/assets/scss/core/mixins/_scrollbar-width.scss @@ -1,3 +1,5 @@ +@use "theme-vars" as *; + @mixin scrollbar-width($width: thin, $widthLegacy: 12px) { @include set-fi-vars(( scrollbar-width: $width, diff --git a/assets/scss/core/mixins/_sticky-top.scss b/assets/scss/core/mixins/_sticky-top.scss index 276f279b..0329dfed 100644 --- a/assets/scss/core/mixins/_sticky-top.scss +++ b/assets/scss/core/mixins/_sticky-top.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "media" as *; + /// Apply the shared sticky top offset used by TOC, collections, and code headers. /// /// @param {boolean} $transition - Whether to include the transition for top changes (default: true). diff --git a/assets/scss/core/mixins/_task-list.scss b/assets/scss/core/mixins/_task-list.scss index 242fde1a..42adcb47 100644 --- a/assets/scss/core/mixins/_task-list.scss +++ b/assets/scss/core/mixins/_task-list.scss @@ -1,3 +1,5 @@ +@use "theme-vars" as *; + // Task list utility mixins // ---------------------------------------- diff --git a/assets/scss/core/mixins/_theme-switch.scss b/assets/scss/core/mixins/_theme-switch.scss index 44915210..c997f9cb 100644 --- a/assets/scss/core/mixins/_theme-switch.scss +++ b/assets/scss/core/mixins/_theme-switch.scss @@ -1,3 +1,5 @@ +@use "override" as *; + // 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; diff --git a/assets/scss/core/mixins/_theme-vars.scss b/assets/scss/core/mixins/_theme-vars.scss index ff08b8e8..bc7b2a1c 100644 --- a/assets/scss/core/mixins/_theme-vars.scss +++ b/assets/scss/core/mixins/_theme-vars.scss @@ -1,6 +1,9 @@ // ======================================== // Theme Variable Mixins and Functions // ======================================== +@use "sass:color"; +@use "sass:map"; +@use "override" as *; /// Set a single CSS variable /// @param {String} $name - CSS variable name (without prefix) @@ -60,7 +63,7 @@ // 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%)}); + #{$rootPrefix}#{$name}: light-dark(#{$value}, #{color.adjust($value, $lightness: -5%)}); } } } @@ -75,7 +78,7 @@ // Light/Dark in one declaration (driven by color-scheme) @at-root :root { @each $name, $values in $vars { - #{$rootPrefix}#{$name}: light-dark(#{map-get($values, light)}, #{map-get($values, dark)}); + #{$rootPrefix}#{$name}: light-dark(#{map.get($values, light)}, #{map.get($values, dark)}); } } } diff --git a/assets/scss/core/mixins/_z-index.scss b/assets/scss/core/mixins/_z-index.scss index 78f014cc..248ea608 100644 --- a/assets/scss/core/mixins/_z-index.scss +++ b/assets/scss/core/mixins/_z-index.scss @@ -1,3 +1,5 @@ +@use "sass:map"; + $z-indexes: ( "hide": -1, "auto": auto, @@ -14,7 +16,7 @@ $z-indexes: ( /// @include z-index(base); /// @include z-index(base, 1); @mixin z-index($key, $diff: 0) { - $z-index: map-get($z-indexes, $key); + $z-index: map.get($z-indexes, $key); @if $z-index { z-index: $z-index + $diff; diff --git a/assets/scss/main.scss b/assets/scss/main.scss index 46be1c16..84024074 100644 --- a/assets/scss/main.scss +++ b/assets/scss/main.scss @@ -1,8 +1,6 @@ @charset "utf-8"; -@import "variables"; -@import "override"; -@import "core"; -@import "pages"; -@import 'widgets'; -@import "custom"; +@use "core"; +@use "pages"; +@use "widgets"; +@use "custom"; diff --git a/assets/scss/override.scss b/assets/scss/override.scss index c37b4ec3..a25e6b9d 100644 --- a/assets/scss/override.scss +++ b/assets/scss/override.scss @@ -1,4 +1,12 @@ // ============================== // Override Variables // 覆盖变量 +// @deprecated 1.0.0 use config params for _variables.scss +// +// Example: +// @forward "variables" with ( +// $global-font-size: 16px, +// $global-border-radius: 6px, +// ); // ============================== +@forward "variables"; diff --git a/assets/scss/pages/_base.scss b/assets/scss/pages/_base.scss new file mode 100644 index 00000000..a49e7519 --- /dev/null +++ b/assets/scss/pages/_base.scss @@ -0,0 +1,42 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + +.page { + padding-inline: 0.5rem; + box-sizing: border-box; + + .content { + [id] { + scroll-margin-top: fi-var(scroll-margin-top, 0.5rem); + + @include media('sm', 'up') { + [data-header-desktop='sticky'] &, + [data-header-desktop='auto'] & { + @include set-fi-var(scroll-margin-top, calc(#{fi-var(header-height)} + 0.5rem)); + } + + [data-header-desktop='sticky']:has(.breadcrumb-container.sticky) &, + [data-header-desktop='auto']:has(.breadcrumb-container.sticky) & { + @include set-fi-var(scroll-margin-top, calc(#{fi-var(header-height)} + #{fi-var(breadcrumb-height, 0px)} + 0.5rem)); + } + } + + @include media('xs') { + [data-header-mobile='sticky'] &, + [data-header-mobile='auto'] & { + @include set-fi-var(scroll-margin-top, calc(#{fi-var(header-height)} + 0.5rem)); + } + + [data-header-mobile='sticky']:has(.breadcrumb-container.sticky) &, + [data-header-mobile='auto']:has(.breadcrumb-container.sticky) & { + @include set-fi-var(scroll-margin-top, calc(#{fi-var(header-height)} + #{fi-var(breadcrumb-height, 0px)} + 0.5rem)); + } + } + } + } + + @include media('print') { + width: 100% !important; + padding-top: 0 !important; + } +} diff --git a/assets/scss/pages/_home.scss b/assets/scss/pages/_home.scss index 7a9c10ea..23bda7e3 100644 --- a/assets/scss/pages/_home.scss +++ b/assets/scss/pages/_home.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + .home { .home-profile { text-align: center; diff --git a/assets/scss/pages/_index.scss b/assets/scss/pages/_index.scss index 85d27f5b..ce5a3f11 100644 --- a/assets/scss/pages/_index.scss +++ b/assets/scss/pages/_index.scss @@ -1,49 +1,9 @@ -.page { - padding-inline: 0.5rem; - box-sizing: border-box; - @extend %page-style; - - .content { - [id] { - scroll-margin-top: fi-var(scroll-margin-top, 0.5rem); - - @include media('sm', 'up') { - [data-header-desktop='sticky'] &, - [data-header-desktop='auto'] & { - @include set-fi-var(scroll-margin-top, calc(#{fi-var(header-height)} + 0.5rem)); - } - - [data-header-desktop='sticky']:has(.breadcrumb-container.sticky) &, - [data-header-desktop='auto']:has(.breadcrumb-container.sticky) & { - @include set-fi-var(scroll-margin-top, calc(#{fi-var(header-height)} + #{fi-var(breadcrumb-height, 0px)} + 0.5rem)); - } - } - - @include media('xs') { - [data-header-mobile='sticky'] &, - [data-header-mobile='auto'] & { - @include set-fi-var(scroll-margin-top, calc(#{fi-var(header-height)} + 0.5rem)); - } - - [data-header-mobile='sticky']:has(.breadcrumb-container.sticky) &, - [data-header-mobile='auto']:has(.breadcrumb-container.sticky) & { - @include set-fi-var(scroll-margin-top, calc(#{fi-var(header-height)} + #{fi-var(breadcrumb-height, 0px)} + 0.5rem)); - } - } - } - } - - @include media('print') { - width: 100% !important; - padding-top: 0 !important; - } -} - -@import 'single'; -@import 'specials'; -@import 'archives'; -@import '404'; -@import 'home'; -@import 'link'; -@import 'offline'; -@import "patch"; +@use "base"; +@use "single"; +@use "specials"; +@use "archives"; +@use "404" as page404; +@use "home"; +@use "link"; +@use "offline"; +@use "patch"; diff --git a/assets/scss/pages/_link.scss b/assets/scss/pages/_link.scss index fce327e5..0a87090f 100644 --- a/assets/scss/pages/_link.scss +++ b/assets/scss/pages/_link.scss @@ -1,3 +1,7 @@ +// link guard intermediate page +@use "core/functions" as *; +@use "core/mixins" as *; + #content-link { align-self: center; diff --git a/assets/scss/pages/_patch.scss b/assets/scss/pages/_patch.scss index b14f8a2f..c77c796e 100644 --- a/assets/scss/pages/_patch.scss +++ b/assets/scss/pages/_patch.scss @@ -1,4 +1,5 @@ -/* Resolve style conflicts between third-party plugins */ +// Resolve style conflicts between third-party plugins +@use "override" as *; @keyframes #{$prefix}pulse { from { @@ -23,3 +24,8 @@ #zenuml-intersection-detector-container { display: none; } + +// lightgallery +.lg-toolbar .lg-download:after { + color: #999; +} diff --git a/assets/scss/pages/archives/_index.scss b/assets/scss/pages/archives/_index.scss index a9eb2bda..2f728e61 100644 --- a/assets/scss/pages/archives/_index.scss +++ b/assets/scss/pages/archives/_index.scss @@ -1,3 +1,5 @@ +@use "sass:meta"; + .archive { .single-title { padding: 1rem 0 0.5rem; @@ -22,6 +24,6 @@ margin-bottom: 1rem; } - @import 'terms'; - @import 'tags'; + @include meta.load-css('terms'); + @include meta.load-css('tags'); } diff --git a/assets/scss/pages/archives/_tags.scss b/assets/scss/pages/archives/_tags.scss index c87ded72..378fffd7 100644 --- a/assets/scss/pages/archives/_tags.scss +++ b/assets/scss/pages/archives/_tags.scss @@ -1,3 +1,8 @@ +@use "sass:color"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; + .tags { margin: 10px 0; @@ -52,8 +57,8 @@ @include set-fi-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) + color.mix($tag-cloud-end, $tag-cloud-start, $grade * 10), + color.mix($tag-cloud-end-dark, $tag-cloud-start-dark, $grade * 10) ) ); } diff --git a/assets/scss/pages/archives/_terms.scss b/assets/scss/pages/archives/_terms.scss index e40288bc..bc22000f 100644 --- a/assets/scss/pages/archives/_terms.scss +++ b/assets/scss/pages/archives/_terms.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + .taxonomy-cards { margin: 0 auto; display: flex; @@ -65,5 +68,5 @@ text-align: right; color: fi-var(global-font-secondary-color); white-space: nowrap; - @extend .variant-numeric; + font-variant-numeric: tabular-nums lining-nums; } diff --git a/assets/scss/pages/single/_alert.scss b/assets/scss/pages/single/_alert.scss index de40259e..ec8c2e59 100644 --- a/assets/scss/pages/single/_alert.scss +++ b/assets/scss/pages/single/_alert.scss @@ -1,4 +1,9 @@ -.alert { +@use "sass:map"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "core/maps" as maps; + +.single .alert { padding: 0.5rem 1rem; margin-block: 1rem; color: inherit; @@ -26,13 +31,13 @@ } } - @each $type, $item in $alert-color-map { - $dark-item: map-get($alert-color-map-dark, $type); + @each $type, $item in maps.$alert-color-map { + $dark-item: map.get(maps.$alert-color-map-dark, $type); &.alert-#{$type} { @include set-fi-vars(( - 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)) + 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)) )); } } diff --git a/assets/scss/pages/single/_base.scss b/assets/scss/pages/single/_base.scss new file mode 100644 index 00000000..bbe02bb3 --- /dev/null +++ b/assets/scss/pages/single/_base.scss @@ -0,0 +1,518 @@ +@use "sass:color"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; + +.single { + .single-title { + padding: 1rem 0 0.5rem; + margin: 0; + font-size: 1.6rem; + font-weight: bold; + line-height: 140%; + + .icon-pin { + display: inline-block; + width: max-content; + transform: rotate(30deg); + margin-right: 0.25em; + color: fi-var(danger); + } + + .icon-repost { + display: inline-block; + width: max-content; + margin-right: 0.25em; + color: fi-var(success); + } + + @include media('print') { + text-align: center; + } + } + + .single-subtitle { + margin-block: 0 0.5rem; + font-size: 1.2rem; + font-weight: normal; + line-height: 1.15; + + @include media('print') { + text-align: center; + } + } + + .post-meta { + font-size: 0.875rem; + color: fi-var(global-font-secondary-color); + + span { + display: inline-block; + } + + @include link(false, true); + + .author { + font-size: 1.05rem; + + img.avatar { + width: 1.3rem; + height: 1.3rem; + vertical-align: text-bottom; + border: 0.25px solid fi-var(global-border-color); + box-sizing: border-box; + object-position: center; + @include border-radius(full); + object-fit: cover; + } + } + + @include media('print') { + text-align: center; + + .comment-visitors, + .comment-count { + display: none; + } + } + } + + .featured-image { + margin: 1rem 0; + + img { + display: block; + width: 100%; + max-width: 100%; + height: auto; + margin: 0 auto; + overflow: hidden; + } + } + + #content[data-end-flag]:not([data-password])::after { + content: attr(data-end-flag); + display: block; + text-align: center; + font-size: 0.875em; + padding: 0.25rem; + color: fi-var(global-font-tertiary-color); + opacity: 0.8; + } + + hr { + position: relative; + height: 1em; + margin-block: 1em; + background-image: linear-gradient(to right, fi-var(global-border-color) 50%, transparent 50%); + background-size: 10px 2px; + background-position: center; + background-repeat: repeat-x; + border: none; + + &.awesome-hr { + &::before { + display: inline-block; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + position: absolute; + left: 5%; + top: 50%; + transform: translateY(-50%); + color: fi-var(hr-active-color); + font-size: 20px; + line-height: 1; + transition: left 1s ease-in-out, color 0.3s ease, border-color 0.3s ease; + font-family: var(--fa-family-classic); + font-weight: 600; + content: '\f0c4'; + @include z-index(base); + } + + &:hover::before { + left: calc(95% - 20px); + } + } + } + + .content { + margin-block: 1rem; + + > h1, + > h2 { + font-size: 1.5em; + padding-bottom: 0.3em; + border-bottom: 1px solid fi-var(global-border-color-light); + } + + > h3 { + font-size: 1.25em; + } + + > h4 { + font-size: 1em; + } + + > h5 { + font-size: 0.875em; + } + + > h6 { + font-size: 0.85em; + } + + .heading-element { + display: flex; + font-weight: bold; + line-height: 1.25; + margin-top: 1.5rem; + margin-bottom: 1rem; + font-weight: light-dark(bold, bolder); + + &:hover, + &:target { + > .heading-mark { + opacity: 1; + } + } + + > .heading-mark { + display: inline-flex; + align-items: center; + opacity: 0; + padding-inline: 0.5rem; + transition: all 0.2s ease-in-out; + @include link(false, false); + + svg { + fill: currentColor; + } + } + } + + p { + margin: 0.5rem 0; + } + + b, + strong { + @include bold-dark; + } + + @include link(false, false); + + a { + white-space: normal; + @include overflow-wrap(break-word); + + b, + strong { + color: inherit; + } + } + + > ul, + > ol, + > dl { + margin-block: 0.5em; + } + + :is(ol, ul) :is(ol, ul) { + padding-left: 20px; + } + + li::marker { + color: fi-var(secondary); + } + li:hover::marker { + color: fi-var(single-link-hover-color); + } + + // Task lists + li { + &[data-task] { + list-style: none; + @include task-icon { + margin: 0 0.25em 0 -1.5em; + color: fi-var(task-icon-color, fi-var(secondary)); + }; + @include task-text { + color: fi-var(task-text-color); + } + } + + &[data-task='x'] { + @include task-icon { + background-color: fi-rgba(fi-var(secondary), 0.2); + mask-image: linear-gradient(#000, #000); + mask-size: 0.875em 0.875em; + mask-repeat: no-repeat; + mask-position: center; + }; + } + + &[data-task='-'] { + @include task-icon { + opacity: 0.7; + }; + @include task-text(fi-var(secondary)) { + opacity: 0.7; + text-decoration: line-through fi-var(secondary); + } + } + + &[data-task='/'] { + @include task-icon(fi-var(success)); + @include task-text(fi-var(success)); + } + + &[data-task='!'], + &[data-task='!x'] { + @include task-icon(fi-var(danger)); + } + + &[data-task='?'] { + @include task-icon(fi-var(warning)); + } + } + + dl { + dt { + font-weight: bold; + } + dd { + margin: 0.25em 0 1em; + } + } + + ruby { + background-color: fi-var(code-block-background-color); + + rt { + color: fi-var(global-font-secondary-color); + } + } + + mark { + padding: 0.14em 0.28em; + background-color: fi-var(mark-background-color, mark); + color: fi-var(mark-color, marktext); + @include border-radius; + + @each $color, $value in $theme-colors { + &.mark-#{$color} { + @include set-fi-vars(( + mark-color: #fff, + mark-background-color: fi-var(#{$color}), + )); + } + } + } + + .table-wrapper { + position: relative; + overflow-x: auto; + overflow-y: hidden; + margin: 0.5rem 0; + background: fi-var(table-background-color); + border: 1px solid fi-var(table-border-color); + @include border-radius; + @include set-fi-var(scrollbar-thumb-color, fi-var(table-background-color)); + + > table { + width: 100%; + max-width: 100%; + margin: 0; + border-spacing: 0; + background: fi-var(table-background-color); + border-collapse: separate; + + thead { + background: fi-var(table-thead-color); + } + + &:not([class]) tbody { + & tr:nth-child(odd) { + background: light-dark(color.adjust($table-background-color, $lightness: -2.25%), color.adjust($table-background-color-dark, $lightness: 2.75%)); + } + + & tr:hover { + background: light-dark(color.adjust($table-background-color, $lightness: -4.5%), color.adjust($table-background-color-dark, $lightness: 5.5%)); + } + } + + th, + td { + padding: 0.3rem 1rem; + border-right: 1px solid fi-var(table-border-color); + border-bottom: 1px solid fi-var(table-border-color); + } + + tr > :last-child { + border-right: none; + } + + > :last-child > tr:last-child > * { + border-bottom: none; + } + } + } + + img { + max-width: 100%; + // SVG performs differently in Safari compared to other browsers + &:not([src*='.svg']) { + min-height: 1em; + } + } + svg { + max-width: 100%; + } + + figure { + margin: 0.5rem; + text-align: center; + + .image-caption:not(:empty) { + min-width: 20%; + max-width: 80%; + display: inline-block; + padding: 0.5rem; + margin: 0 auto; + font-size: 0.875rem; + color: #969696; + } + + img { + display: block; + margin: 0 auto; + height: auto; + overflow: hidden; + } + } + + blockquote { + display: block; + border-left: 0.25em solid fi-var(global-border-color); + color: fi-var(blockquote-color); + padding: 0 1em; + margin: 1rem 0; + + ul, + ol { + padding-left: 1.5em; + } + + dl dd { + margin-inline-start: 0.25em; + } + + @include link(false, true); + } + + sup[id^='fnref']:has(.footnote-ref) { + padding: 2px 4px; + 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); + + @include link(true, false); + } + + .footnotes { + font-size: 0.75rem; + white-space: normal; + color: light-dark(#a3a3ae, #9d9d9f); + @include link(false, true); + + ol { + margin-block: 1em; + padding-left: 1rem; + } + + hr { + background-color: fi-var(global-border-color); + height: 1px; + margin-block: 1em; + } + + code { + font-size: 0.75rem; + } + + a:not(.footnote-backref) { + color: inherit; + text-decoration: underline; + } + + p { + margin: 0.25rem 0; + } + } + + kbd { + display: inline-block; + padding: 0 0.25rem; + background-color: fi-var(global-background-color); + border: 1px solid fi-var(global-border-color); + font-size: 0.8rem; + font-family: fi-var(code-font-family); + color: fi-var(code-color); + box-shadow: inset 0 -1px 0 fi-var(global-border-color); + @include border-radius; + } + + .diagram-container { + position: relative; + margin-block: 0.5rem; + + > .copy-icon-btn { + position: absolute; + top: 0.5rem; + right: 0.5rem; + } + } + + // KaTeX styles + .katex-display { + max-width: 100%; + overflow-x: auto; + overflow-y: hidden; + // neutralize the margin-right setting of .katex .vlist-t2 + // to prevent scrollbars from appearing when the width is not full + padding-right: 2px; + } + .katex-error { + padding: 0.14em 0.28em; + background-color: mark; + cursor: help; + @include border-radius; + } + + // MathJax styles + mjx-container[jax='CHTML'] { + // formula Blocks + &[display='true'] { + max-width: 100%; + overflow-x: auto; + overflow-y: hidden; + } + + // fix the color of xypic diagrams + mjx-xypic { + g { + stroke: currentColor !important; + } + mjx-xypic-object { + color: currentColor !important; + fill: currentColor !important; + } + } + } + + json-viewer[boxed] + json-viewer[boxed] { + margin-top: 0.5rem; + } + } +} diff --git a/assets/scss/pages/single/_code.scss b/assets/scss/pages/single/_code.scss index b84b7ce8..9b35481f 100644 --- a/assets/scss/pages/single/_code.scss +++ b/assets/scss/pages/single/_code.scss @@ -1,134 +1,553 @@ -// inline code -code { - @extend %code; -} - -// indented code -pre { - margin: 0; - line-height: 1.45em; - padding: 0.5rem 0.75rem; - overflow: auto; - font-size: fi-var(code-block-font-size); - font-family: fi-var(code-font-family); - background-color: fi-var(code-block-background-color); - tab-size: 4; - @include border-radius; - - + pre { - margin-top: 0.5rem; - } +@use "sass:color"; +@use "sass:meta"; +@use "sass:string"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; +@use "code-syntax"; +@use "copy-icon-btn"; +.single { + // inline code code { - padding: 0; - background-color: transparent; - font-size: inherit; - line-height: inherit; - min-width: max-content; - @include border-radius(0); + @include inline-code; } - img { - min-height: 1em; - max-height: 1.2em; - vertical-align: text-bottom; - } -} - -.highlight, -.gist { - font-size: fi-var(code-block-font-size); - font-family: fi-var(code-font-family); - @include set-fi-var(scrollbar-thumb-color, inherit); - - table { - width: 100%; - max-width: 100%; - border-spacing: 0; - border-collapse: collapse; - } - - table, - table thead, - table tr, - table td { - margin: 0; - padding: 0; - border: none; - } -} - -// Code fences blocks or highlight shortcodes -.highlight { - margin: 0.5rem 0; - position: relative; - @include border-radius; - - // lineNumbersInTable=true - > div.chroma { + // color preview styles for inline code + .color-code { position: relative; + @include set-fi-var(color-code-size, 0.64em); + + .color-preview { + display: inline-block; + width: fi-var(color-code-size); + height: fi-var(color-code-size); + @include border-radius(full); + margin-right: 0.25em; + vertical-align: middle; + border: 1px solid; + pointer-events: none; + transform: translateY(-1px); + border-color: light-dark(#d1d9e0b3, #3d444db3); + } + } + + // indented code + pre { + margin: 0; + line-height: 1.45em; + padding: 0.5rem 0.75rem; + overflow: auto; + font-size: fi-var(code-block-font-size); + font-family: fi-var(code-font-family); background-color: fi-var(code-block-background-color); + tab-size: 4; @include border-radius; - &:is(pre):has(.ln) { - padding: 0.5rem; + + pre { + margin-top: 0.5rem; } - .lntable { - overflow: auto; - overscroll-behavior-x: none; - display: block; + code { + padding: 0; + background-color: transparent; + font-size: inherit; + line-height: inherit; + min-width: max-content; + @include border-radius(0); + } + + img { + min-height: 1em; + max-height: 1.2em; + vertical-align: text-bottom; + } + } + + .highlight, + .gist { + font-size: fi-var(code-block-font-size); + font-family: fi-var(code-font-family); + @include set-fi-var(scrollbar-thumb-color, inherit); + + table { + width: 100%; + max-width: 100%; + border-spacing: 0; + border-collapse: collapse; + } + + table, + table thead, + table tr, + table td { + margin: 0; + padding: 0; + border: none; + } + } + + // Code fences blocks or highlight shortcodes + .highlight { + margin: 0.5rem 0; + position: relative; + @include border-radius; + + // lineNumbersInTable=true + > div.chroma { + position: relative; background-color: fi-var(code-block-background-color); @include border-radius; - td { - vertical-align: top; - padding: 0; + &:is(pre):has(.ln) { + padding: 0.5rem; } - pre { - outline-offset: -1px; + .lntable { + overflow: auto; + overscroll-behavior-x: none; + display: block; + background-color: fi-var(code-block-background-color); + @include border-radius; + + td { + vertical-align: top; + padding: 0; + } + + pre { + outline-offset: -1px; + @include border-radius(0); + } + + // line numbers + .lntd:first-child { + position: sticky; + left: 0; + } + // code content + .lntd:last-child { + width: 100%; + code { + width: 100%; + } + } + } + } + + // line numbers (lineNumbersInTable=false) + .ln { + padding-right: 0.75rem; + display: inline-block; + text-align: right; + position: sticky; + left: 0; + } + // Line numbers + .lnt, + .ln { + color: fi-var(global-font-secondary-color); + user-select: none; + } + + &:not(.code-block) { + .lntd:last-child pre.chroma { + padding-left: 0; + } + } + + // code fences blocks wrapped + &.code-block { + &[data-shadow='always'] { + @include box-shadow(always); + } + + &[data-shadow='hover'] { + @include box-shadow(hover); + } + + .code-wrapper { + overflow: hidden; + height: auto; + position: relative; + counter-reset: codeNos calc(#{fi-var(line-start)} - 1); + transition: height 0.5s ease-out; + @include border-radius; + @include border-radius; + // attr() for arbitrary properties is currently only supported in Chromium-based browsers (see https://caniuse.com/css3-attr); + // non-supporting browsers fall back to values set via Hugo templating in render-codeblock.html + @include set-fi-vars(( + max-shown-lines: attr(data-max number, 10), + line-start: attr(data-line-start number, 1), + line-digit: attr(data-line-digit number, 1), + )); + + @include media('reduce-motion') { + transition: none; + } + } + + // code preview + &:has(.code-expand-btn):not(.is-expanded, .is-collapsed) { + .code-wrapper { + height: calc(1.45em * #{fi-var(max-shown-lines)} + #{fi-var(code-expand-offset, 0.5rem)}); + } + &[data-mode='mac'] .code-wrapper { + @include set-fi-var(code-expand-offset, 1.875rem + 0.5rem); + } + } + + .code-header { + display: flex; + justify-content: space-between; + align-items: center; + box-sizing: border-box; + width: 100%; + font-family: fi-var(global-font-family); + font-weight: bold; + line-height: 1.4em; + color: fi-var(code-header-color); + background-color: fi-var(code-header-background-color); + position: sticky; + transition: border-radius 0s; + cursor: pointer; + @include z-index(base, 1); + @include border-radius(top); + @include sticky-top-offset; + + + .code-wrapper { + @include border-radius(bottom); + + > pre.chroma { + @include border-radius(bottom); + } + } + + .code-title { + width: 100%; + padding: 0.4rem; + + &::after { + padding-left: 0.2rem; + content: attr(data-name, fi-var(code-type, 'Code')); + } + + // filename for code block + &[data-name]::after { + @include overflow-wrap(break-word); + } + + .arrow { + padding: 0 0.2rem; + transition: transform 0.2s ease; + } + + .title-inner { + position: absolute; + left: 50%; + transform: translateX(-50%); + background-color: fi-var(code-header-background-color); + padding-inline: 1em; + max-width: calc(100% - 200px); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + @include z-index(base, 2); + } + } + + .ellipses-btn { + display: none; + padding: 0.4rem; + } + + button { + padding: 0.4rem; + + @include media('print') { + display: none !important; + } + } + + .copy-btn[data-copied] .fa-clone { + --fa: "\f00c"; + font-weight: 900; + color: fi-var(global-link-hover-color); + } + + .download-btn[data-downloaded] .fa-download { + --fa: "\f1ce"; + font-weight: 900; + color: fi-var(global-link-hover-color); + } + + @each $type, $text in $code-type-map { + &.language-#{string.to-lower-case($type)} { + // e.g., --fi-code-type: "JavaScript"; + @include set-fi-var(code-type, '"#{$text}"'); + } + } + + @include media('print') { + top: initial !important; + } + } + + &.line-wrapping .line-wrap-btn, + &:not(.line-nos-hidden) .line-nos-btn, + .code-header button:hover, + &:has([contenteditable='true']) .edit-btn { + color: fi-var(global-link-hover-color); + } + + &.is-fullscreen { + position: fixed; + inset: 0; + margin: 0 !important; + overflow: auto; + @include z-index(fixed, 1); @include border-radius(0); + + .code-header { + top: 0 !important; + cursor: default; + @include border-radius(0); + + .arrow { + display: none; + } + } + + .code-wrapper { + overflow: auto; + // 1.4em for header height, 0.8rem for padding + height: calc(100dvh - 1.4em - 0.8rem); + } + + .code-expand-btn { + display: none; + } } - // line numbers - .lntd:first-child { + .code-expand-btn { + width: 100%; + padding-block: 1rem; + text-align: center; + position: absolute; + bottom: 0; + background: linear-gradient( + 180deg, + 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); + + .fa-angles-down { + opacity: 0; + animation-name: fadeOutDown; + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-delay: 0.3s; + transition: transform 0.2s ease; + } + } + + .copy-icon-btn { + position: absolute; + top: 0.5rem; + right: 0.5rem; + } + + // classic mode code block collapsed + &.is-collapsed { + .code-header { + transition: border-radius 0.2s ease 0.2s; + @include border-radius; + @include set-fi-var( + code-header-background-color, + light-dark(color.adjust($code-header-background-color, $lightness: -1%), color.adjust($code-header-background-color-dark, $lightness: -1%)) + ); + + + .code-wrapper { + height: 0; + } + } + + .arrow { + transform: rotate(-90deg); + } + + .ellipses-btn { + display: inline; + } + + .code-header .action-btn, + .code-expand-btn { + display: none; + } + } + + // code preview expanded + &.is-expanded { + margin-bottom: 1rem; + &:has([contenteditable='true']) .code-expand-btn { + display: none; + } + .code-expand-btn { + background: transparent; + padding-block: 0; + transform: translateY(1rem); + + .fa-angles-down { + animation-name: fadeOutRotate; + + @keyframes fadeOutRotate { + 0% { + opacity: 1; + transform: rotate(180deg); + } + 100% { + opacity: 0; + transform: rotate(180deg) translate3d(0, 100%, 0); + } + } + } + } + } + + // hide original line numbers + &.line-nos-hidden { + > .chroma .lntable { + width: 100%; + overscroll-behavior-x: contain; + + > tbody { + display: table; + width: 100%; + } + + .lntd:last-child { + width: 100%; + } + } + + .lntd:first-child, + .ln { + display: none; + } + } + + // line wrapping + &.line-wrapping { + > .chroma .lntable { + width: 100%; + display: table; + + // hide original line numbers + .lntd:first-child { + display: none; + } + } + .lntd:last-child pre.chroma, + .code-wrapper > pre.chroma { + max-width: 100%; + box-sizing: border-box; + + code { + text-wrap: wrap; + word-break: break-all; + overflow-wrap: break-word; + } + } + } + + // line numbers (pseudo-elements) for code blocks + &.line-wrapping:not(.line-nos-hidden) .lntd:last-child pre.chroma, + &:not(.line-nos-hidden) .code-wrapper > pre.chroma:not(:has(.ln)) { + > code > .line { + display: flex; + + &::before { + counter-increment: codeNos; + content: counter(codeNos); + white-space: nowrap; + text-align: right; + min-width: calc(#{fi-var(line-digit)} * 1ch); + padding-inline: 0.75rem; + flex-shrink: 0; + color: fi-var(global-font-secondary-color); + background-color: fi-var(code-block-background-color); + } + + // highlight for line numbers + &.hl::before { + background-color: transparent; + } + } + } + + // add sticky effect for line numbers when lineNos=false + &:not(.line-nos-hidden) .code-wrapper > pre.chroma > code > .line::before { position: sticky; left: 0; } - // code content - .lntd:last-child { - width: 100%; - code { - width: 100%; + + // when line numbers shown + &:not(.line-nos-hidden) { + .lntd:last-child pre.chroma:not([contenteditable='true']), + .code-wrapper > pre.chroma:not(:has(.ln)) { + padding-left: 0; + } + } + + // add border for non-classic mode + &:not([data-mode='classic']) { + border: 1px solid fi-var(global-border-color-light); + } + + &[data-mode='mac'] { + pre.chroma { + padding-top: 1.875rem; + + &::after { + background: fi-var(mac-red, #fc625d); + box-shadow: 20px 0 fi-var(mac-yellow, #fdbc40), 40px 0 fi-var(mac-green, #35cd4b); + left: 8px; + top: 8px; + position: absolute; + content: ' '; + height: 12px; + width: 12px; + @include border-radius(full); + + @include dark-mode { + @include set-fi-vars(( + mac-red: #ff5f56, + mac-yellow: #febc2e, + mac-green: #28c840, + )); + } + } + } + } + + &[data-mode='simple'] { + pre.chroma { + padding-block: 0.875rem; } } } } - // line numbers (lineNumbersInTable=false) - .ln { - padding-right: 0.75rem; - display: inline-block; - text-align: right; - position: sticky; - left: 0; - } - // Line numbers - .lnt, - .ln { - color: fi-var(global-font-secondary-color); - user-select: none; - } + // Code Tabs + .code-tabs { + margin: 0.5rem 0; + background-color: fi-var(code-block-background-color); + border: 1px solid fi-var(global-border-color-light); + @include border-radius; - &:not(.code-block) { - .lntd:last-child pre.chroma { - padding-left: 0; - } - } - - // code fences blocks wrapped - &.code-block { &[data-shadow='always'] { @include box-shadow(always); } @@ -137,125 +556,118 @@ pre { @include box-shadow(hover); } - .code-wrapper { - overflow: hidden; - height: auto; - position: relative; - counter-reset: codeNos calc(#{fi-var(line-start)} - 1); - transition: height 0.5s ease-out; - @include border-radius; - @include border-radius; - // attr() for arbitrary properties is currently only supported in Chromium-based browsers (see https://caniuse.com/css3-attr); - // non-supporting browsers fall back to values set via Hugo templating in render-codeblock.html - @include set-fi-vars(( - max-shown-lines: attr(data-max number, 10), - line-start: attr(data-line-start number, 1), - line-digit: attr(data-line-digit number, 1), - )); - - @include media('reduce-motion') { - transition: none; - } + &:has(.code-block.active .code-expand-btn) { + margin-bottom: 1rem; } - // code preview - &:has(.code-expand-btn):not(.is-expanded, .is-collapsed) { - .code-wrapper { - height: calc(1.45em * #{fi-var(max-shown-lines)} + #{fi-var(code-expand-offset, 0.5rem)}); - } - &[data-mode='mac'] .code-wrapper { - @include set-fi-var(code-expand-offset, 1.875rem + 0.5rem); - } + &:has(.code-block.active.line-wrapping) .tabs-actions .line-wrap-btn, + &:has(.code-block.active:not(.line-nos-hidden)) .tabs-actions .line-nos-btn, + &:has(.code-block.active [contenteditable='true']) .tabs-actions .edit-btn { + color: fi-var(global-link-hover-color); } - - .code-header { + + .tabs-header { display: flex; justify-content: space-between; align-items: center; - box-sizing: border-box; - width: 100%; - font-family: fi-var(global-font-family); - font-weight: bold; line-height: 1.4em; + font-size: fi-var(code-block-font-size); color: fi-var(code-header-color); background-color: fi-var(code-header-background-color); position: sticky; - transition: border-radius 0s; - cursor: pointer; + border-bottom: 1px solid fi-var(global-border-color); @include z-index(base, 1); @include border-radius(top); @include sticky-top-offset; - + .code-wrapper { - @include border-radius(bottom); + .tabs-items { + display: flex; + overflow-x: auto; + position: relative; + @include border-radius(top); - > pre.chroma { - @include border-radius(bottom); - } - } - - .code-title { - width: 100%; - padding: 0.4rem; - - &::after { - padding-left: 0.2rem; - content: attr(data-name, fi-var(code-type, 'Code')); - } - - // filename for code block - &[data-name]::after { - @include overflow-wrap(break-word); - } - - .arrow { - padding: 0 0.2rem; - transition: transform 0.2s ease; - } - - .title-inner { - position: absolute; - left: 50%; - transform: translateX(-50%); - background-color: fi-var(code-header-background-color); - padding-inline: 1em; - max-width: calc(100% - 200px); + .before-tabs { + padding: 0.4rem 0.6rem; white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - @include z-index(base, 2); + color: fi-var(global-font-color); + } + + .tab-item { + padding: 0.4rem 0.8rem; + cursor: pointer; + font-weight: bold; + color: fi-var(global-font-secondary-color); + white-space: nowrap; + user-select: none; + position: relative; + @include border-radius; + + &:not(:last-child)::after { + content: ''; + position: absolute; + right: 0; + top: 50%; + translate: 0 -50%; + width: 1px; + height: fi-var(code-block-font-size); + background-color: fi-var(global-border-color-weight); + } + + // hide dividers on both sides of the active tab + &.active::after, + &:has(+ .tab-item.active)::after { + // display: none; + background-color: fi-var(global-border-color); + } + + @include media('xs') { + padding-inline: 0.6rem; + } + } + + &:not(:has(.tab-item:hover)) .tab-item.active, + .tab-item:hover { + anchor-name: #{$rootPrefix}tab-hover; + color: fi-var(global-font-color); + } + + &::before { + content: ''; + position-anchor: #{$rootPrefix}tab-hover; + position: absolute; + inset: anchor(inside); + 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 light-dark(#fff, fi-rgba(fi-var(global-font-secondary-color), 0.4)); + pointer-events: none; + transition: 0.3s ease-in-out; + @include border-radius; } } - .ellipses-btn { - display: none; - padding: 0.4rem; - } + .tabs-actions { + display: flex; + flex-shrink: 0; - button { - padding: 0.4rem; + .action-btn { + padding: 0.4rem; - @include media('print') { - display: none !important; - } - } + &:hover { + color: fi-var(global-link-hover-color); + } - .copy-btn[data-copied] .fa-clone { - --fa: "\f00c"; - font-weight: 900; - color: fi-var(global-link-hover-color); - } + &.copy-btn[data-copied] .fa-clone { + --fa: "\f00c"; + font-weight: 900; + color: fi-var(global-link-hover-color); + } - .download-btn[data-downloaded] .fa-download { - --fa: "\f1ce"; - font-weight: 900; - color: fi-var(global-link-hover-color); - } - - @each $type, $text in $code-type-map { - &.language-#{to-lower-case($type)} { - // e.g., --fi-code-type: "JavaScript"; - @include set-fi-var(code-type, '"#{$text}"'); + &.download-btn[data-downloaded] .fa-download { + --fa: "\f1ce"; + font-weight: 900; + color: fi-var(global-link-hover-color); + } } } @@ -264,490 +676,61 @@ pre { } } - &.line-wrapping .line-wrap-btn, - &:not(.line-nos-hidden) .line-nos-btn, - .code-header button:hover, - &:has([contenteditable='true']) .edit-btn { - color: fi-var(global-link-hover-color); + .tabs-content { + .code-block { + display: none; + margin: 0; + @include border-radius(bottom); + @include box-shadow(never); + + &.active { + display: block; + } + + // hide original header + .code-header { + display: none; + } + } } &.is-fullscreen { position: fixed; inset: 0; margin: 0 !important; - overflow: auto; + display: flex; + flex-direction: column; + overflow: hidden; @include z-index(fixed, 1); @include border-radius(0); - .code-header { + .tabs-header { top: 0 !important; - cursor: default; @include border-radius(0); - - .arrow { - display: none; - } } - .code-wrapper { - overflow: auto; - // 1.4em for header height, 0.8rem for padding - height: calc(100dvh - 1.4em - 0.8rem); + .tabs-content { + flex: 1; + min-height: 0; + + .code-block.active { + height: 100%; + } + + .code-wrapper { + overflow: auto; + height: calc(100dvh - 1.4em - 0.8rem); + } } .code-expand-btn { display: none; } } + } - .code-expand-btn { - width: 100%; - padding-block: 1rem; - text-align: center; - position: absolute; - bottom: 0; - background: linear-gradient( - 180deg, - 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); - - .fa-angles-down { - opacity: 0; - animation-name: fadeOutDown; - animation-duration: 1.5s; - animation-iteration-count: infinite; - animation-delay: 0.3s; - transition: transform 0.2s ease; - } - } - - .code-copy-btn { - @extend .diagram-copy-btn; - } - - // classic mode code block collapsed - &.is-collapsed { - .code-header { - transition: border-radius 0.2s ease 0.2s; - @include border-radius; - @include set-fi-var( - code-header-background-color, - light-dark(darken($code-header-background-color, 1%), darken($code-header-background-color-dark, 1%)) - ); - - + .code-wrapper { - height: 0; - } - } - - .arrow { - transform: rotate(-90deg); - } - - .ellipses-btn { - display: inline; - } - - .code-header .action-btn, - .code-expand-btn { - display: none; - } - } - - // code preview expanded - &.is-expanded { - margin-bottom: 1rem; - &:has([contenteditable='true']) .code-expand-btn { - display: none; - } - .code-expand-btn { - background: transparent; - padding-block: 0; - transform: translateY(1rem); - - .fa-angles-down { - animation-name: fadeOutRotate; - - @keyframes fadeOutRotate { - 0% { - opacity: 1; - transform: rotate(180deg); - } - 100% { - opacity: 0; - transform: rotate(180deg) translate3d(0, 100%, 0); - } - } - } - } - } - - // hide original line numbers - &.line-nos-hidden { - > .chroma .lntable { - width: 100%; - overscroll-behavior-x: contain; - - > tbody { - display: table; - width: 100%; - } - - .lntd:last-child { - width: 100%; - } - } - - .lntd:first-child, - .ln { - display: none; - } - } - - // line wrapping - &.line-wrapping { - > .chroma .lntable { - width: 100%; - display: table; - - // hide original line numbers - .lntd:first-child { - display: none; - } - } - .lntd:last-child pre.chroma, - .code-wrapper > pre.chroma { - max-width: 100%; - box-sizing: border-box; - - code { - text-wrap: wrap; - word-break: break-all; - overflow-wrap: break-word; - } - } - } - - // line numbers (pseudo-elements) for code blocks - &.line-wrapping:not(.line-nos-hidden) .lntd:last-child pre.chroma, - &:not(.line-nos-hidden) .code-wrapper > pre.chroma:not(:has(.ln)) { - > code > .line { - display: flex; - - &::before { - counter-increment: codeNos; - content: counter(codeNos); - white-space: nowrap; - text-align: right; - min-width: calc(#{fi-var(line-digit)} * 1ch); - padding-inline: 0.75rem; - flex-shrink: 0; - color: fi-var(global-font-secondary-color); - background-color: fi-var(code-block-background-color); - } - - // highlight for line numbers - &.hl::before { - background-color: transparent; - } - } - } - - // add sticky effect for line numbers when lineNos=false - &:not(.line-nos-hidden) .code-wrapper > pre.chroma > code > .line::before { - position: sticky; - left: 0; - } - - // when line numbers shown - &:not(.line-nos-hidden) { - .lntd:last-child pre.chroma:not([contenteditable='true']), - .code-wrapper > pre.chroma:not(:has(.ln)) { - padding-left: 0; - } - } - - // add border for non-classic mode - &:not([data-mode='classic']) { - border: 1px solid fi-var(global-border-color-light); - } - - &[data-mode='mac'] { - pre.chroma { - padding-top: 1.875rem; - - &::after { - background: fi-var(mac-red, #fc625d); - box-shadow: 20px 0 fi-var(mac-yellow, #fdbc40), 40px 0 fi-var(mac-green, #35cd4b); - left: 8px; - top: 8px; - position: absolute; - content: ' '; - height: 12px; - width: 12px; - @include border-radius(full); - - @include dark-mode { - @include set-fi-vars(( - mac-red: #ff5f56, - mac-yellow: #febc2e, - mac-green: #28c840, - )); - } - } - } - } - - &[data-mode='simple'] { - pre.chroma { - padding-block: 0.875rem; - } - } + // prevent transition when code block height changed by fullscreen toggle + .instant-height .code-wrapper { + transition: height 0s !important; } } - -@import './code-syntax'; - -// gist shortcode -.gist { - background-color: fi-var(code-block-background-color); - @include border-radius(top); - - .gist-file, - .gist-data, - .gist-meta { - border: none; - } - - .gist-data { - background-color: inherit; - - .blob-wrapper { - padding-block: 0.5rem; - - table.highlight { - background-color: inherit; - overflow: initial; - - td.blob-num { - position: sticky; - left: 0; - background-color: fi-var(code-block-background-color); - @include z-index(base); - } - - td.blob-code { - padding-left: 2px !important; - } - } - } - } - - .gist-meta { - padding: 0.4rem 0.8rem; - @include link(false, false); - @include border-radius(bottom); - background-color: fi-var(gist-meta-background-color); - } - - @include dark-mode { - @import './code-syntax/github-dark-dimmed'; - } -} - -// Code Tabs -.code-tabs { - margin: 0.5rem 0; - background-color: fi-var(code-block-background-color); - border: 1px solid fi-var(global-border-color-light); - @include border-radius; - - &[data-shadow='always'] { - @include box-shadow(always); - } - - &[data-shadow='hover'] { - @include box-shadow(hover); - } - - &:has(.code-block.active .code-expand-btn) { - margin-bottom: 1rem; - } - - &:has(.code-block.active.line-wrapping) .tabs-actions .line-wrap-btn, - &:has(.code-block.active:not(.line-nos-hidden)) .tabs-actions .line-nos-btn, - &:has(.code-block.active [contenteditable='true']) .tabs-actions .edit-btn { - color: fi-var(global-link-hover-color); - } - - .tabs-header { - display: flex; - justify-content: space-between; - align-items: center; - line-height: 1.4em; - font-size: fi-var(code-block-font-size); - color: fi-var(code-header-color); - background-color: fi-var(code-header-background-color); - position: sticky; - border-bottom: 1px solid fi-var(global-border-color); - @include z-index(base, 1); - @include border-radius(top); - @include sticky-top-offset; - - .tabs-items { - display: flex; - overflow-x: auto; - position: relative; - @include border-radius(top); - - .before-tabs { - padding: 0.4rem 0.6rem; - white-space: nowrap; - color: fi-var(global-font-color); - } - - .tab-item { - padding: 0.4rem 0.8rem; - cursor: pointer; - font-weight: bold; - color: fi-var(global-font-secondary-color); - white-space: nowrap; - user-select: none; - position: relative; - @include border-radius; - - &:not(:last-child)::after { - content: ''; - position: absolute; - right: 0; - top: 50%; - translate: 0 -50%; - width: 1px; - height: fi-var(code-block-font-size); - background-color: fi-var(global-border-color-weight); - } - - // hide dividers on both sides of the active tab - &.active::after, - &:has(+ .tab-item.active)::after { - // display: none; - background-color: fi-var(global-border-color); - } - - @include media('xs') { - padding-inline: 0.6rem; - } - } - - &:not(:has(.tab-item:hover)) .tab-item.active, - .tab-item:hover { - anchor-name: #{$rootPrefix}tab-hover; - color: fi-var(global-font-color); - } - - &::before { - content: ''; - position-anchor: #{$rootPrefix}tab-hover; - position: absolute; - inset: anchor(inside); - 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 light-dark(#fff, fi-rgba(fi-var(global-font-secondary-color), 0.4)); - pointer-events: none; - transition: 0.3s ease-in-out; - @include border-radius; - } - } - - .tabs-actions { - display: flex; - flex-shrink: 0; - - .action-btn { - padding: 0.4rem; - - &:hover { - color: fi-var(global-link-hover-color); - } - - &.copy-btn[data-copied] .fa-clone { - --fa: "\f00c"; - font-weight: 900; - color: fi-var(global-link-hover-color); - } - - &.download-btn[data-downloaded] .fa-download { - --fa: "\f1ce"; - font-weight: 900; - color: fi-var(global-link-hover-color); - } - } - } - - @include media('print') { - top: initial !important; - } - } - - .tabs-content { - .code-block { - display: none; - margin: 0; - @include border-radius(bottom); - @include box-shadow(never); - - &.active { - display: block; - } - - // hide original header - .code-header { - display: none; - } - } - } - - &.is-fullscreen { - position: fixed; - inset: 0; - margin: 0 !important; - display: flex; - flex-direction: column; - overflow: hidden; - @include z-index(fixed, 1); - @include border-radius(0); - - .tabs-header { - top: 0 !important; - @include border-radius(0); - } - - .tabs-content { - flex: 1; - min-height: 0; - - .code-block.active { - height: 100%; - } - - .code-wrapper { - overflow: auto; - height: calc(100dvh - 1.4em - 0.8rem); - } - } - - .code-expand-btn { - display: none; - } - } -} - -// prevent transition when code block height changed by fullscreen toggle -.instant-height .code-wrapper { - transition: height 0s !important; -} diff --git a/assets/scss/pages/single/_collections.scss b/assets/scss/pages/single/_collections.scss index d1c0e5ae..73d0acc1 100644 --- a/assets/scss/pages/single/_collections.scss +++ b/assets/scss/pages/single/_collections.scss @@ -1,11 +1,16 @@ +@use "sass:color"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; + // Collection Navigation -.collection-card { +.single .collection-card { font-size: 0.875rem; margin-block: 1rem; padding: 1rem 1.25rem; position: relative; user-select: none; - background-color: light-dark(darken($global-background-color, 4%), lighten($global-background-color-dark, 4%)); + background-color: light-dark(color.adjust($global-background-color, $lightness: -4%), color.adjust($global-background-color-dark, $lightness: 4%)); @include border-radius(0.5rem); &:has(.collection-nav-item:nth-child(2))::before { @@ -99,7 +104,7 @@ &.open { .collection-summary { - background-color: light-dark(darken($global-background-color, 5%), lighten($global-background-color-dark, 5%)); + background-color: light-dark(color.adjust($global-background-color, $lightness: -5%), color.adjust($global-background-color-dark, $lightness: 5%)); } } @@ -109,7 +114,7 @@ gap: 0.25em; padding: 0.2em 0.5em; user-select: none; - background-color: light-dark(darken($global-background-color, 5%), lighten($global-background-color-dark, 5%)); + background-color: light-dark(color.adjust($global-background-color, $lightness: -5%), color.adjust($global-background-color-dark, $lightness: 5%)); > i.fa-layer-group { flex-shrink: 0; @@ -137,7 +142,7 @@ .collection-content { font-size: fi-var(collection-list-font-size); - background-color: light-dark(darken($global-background-color, 3%), lighten($global-background-color-dark, 3%)); + background-color: light-dark(color.adjust($global-background-color, $lightness: -3%), color.adjust($global-background-color-dark, $lightness: 3%)); .collection-list { margin: 0; @@ -175,7 +180,7 @@ align-items: center; justify-content: center; gap: 0.5em; - background-color: light-dark(darken($global-background-color, 5%), lighten($global-background-color-dark, 5%)); + background-color: light-dark(color.adjust($global-background-color, $lightness: -5%), color.adjust($global-background-color-dark, $lightness: 5%)); a.collection-nav-item { padding-inline: 2px; @@ -183,7 +188,7 @@ transition: background-color 0.3s ease-out; &:hover { - background-color: light-dark(darken($global-background-color, 10%), lighten($global-background-color-dark, 10%)); + background-color: light-dark(color.adjust($global-background-color, $lightness: -10%), color.adjust($global-background-color-dark, $lightness: 10%)); } } diff --git a/assets/scss/pages/single/_color-preview.scss b/assets/scss/pages/single/_color-preview.scss deleted file mode 100644 index 43ddfc9f..00000000 --- a/assets/scss/pages/single/_color-preview.scss +++ /dev/null @@ -1,18 +0,0 @@ -// Color preview styles for inline code -.color-code { - position: relative; - @include set-fi-var(color-code-size, 0.64em); - - .color-preview { - display: inline-block; - width: fi-var(color-code-size); - height: fi-var(color-code-size); - @include border-radius(full); - margin-right: 0.25em; - vertical-align: middle; - border: 1px solid; - pointer-events: none; - transform: translateY(-1px); - border-color: light-dark(#d1d9e0b3, #3d444db3); - } -} diff --git a/assets/scss/pages/single/_copy-icon-btn.scss b/assets/scss/pages/single/_copy-icon-btn.scss new file mode 100644 index 00000000..942772c3 --- /dev/null +++ b/assets/scss/pages/single/_copy-icon-btn.scss @@ -0,0 +1,68 @@ +@use "sass:color"; +@use "core/functions" as *; +@use "core/mixins" as *; + +.copy-icon-btn { + box-sizing: content-box; + width: 1rem; + height: 1rem; + line-height: 1; + padding: 0.5rem; + border: 1px solid fi-var(global-border-color); + color: light-dark(#25292e, #d1d7e0); + background-color: light-dark(#f6f8fa, #2a313c); + user-select: none; + @include border-radius; + @include z-index(base); + + &:hover { + background-color: light-dark( + color.adjust(#f6f8fa, $lightness: -3%), + color.adjust(#2a313c, $lightness: -3%) + ); + } + + .fa-clone { + font-size: 1rem; + } + + &[data-copied] .fa-clone { + --fa: "\f00c"; + --fa-style: 900; + color: fi-var(global-link-hover-color); + } + + @include media('print') { + display: none !important; + } +} + +.single .content { + @include media('lg', 'up') { + :is(.code-block, .diagram-container) { + .copy-icon-btn { + display: none; + opacity: 0; + transform: scale(0.9); + transition: + display 0.25s allow-discrete, + opacity 0.25s, + transform 0.25s, + color 0.1s, + background-color 0.1s, + box-shadow 0.1s, + border-color 0.1s; + } + &:hover .copy-icon-btn { + display: block; + opacity: 1; + transform: scale(1); + + @starting-style { + opacity: 0; + transform: scale(0.9); + } + } + } + } +} diff --git a/assets/scss/pages/single/_diagram-copy-btn.scss b/assets/scss/pages/single/_diagram-copy-btn.scss deleted file mode 100644 index 1aa44716..00000000 --- a/assets/scss/pages/single/_diagram-copy-btn.scss +++ /dev/null @@ -1,44 +0,0 @@ -// Diagram container -.diagram-container { - position: relative; - margin-block: 0.5rem; -} - -// Diagram copy button styles -.diagram-copy-btn { - box-sizing: content-box; - width: 1rem; - height: 1rem; - position: absolute; - top: 0.5rem; - right: 0.5rem; - line-height: 1; - padding: 0.5rem; - border: 1px solid; - color: light-dark(#25292e, #d1d7e0); - border-color: light-dark(#d1d9e0, #3d444d); - background-color: light-dark(#f6f8fa, #2a313c); - user-select: none; - @include border-radius; - @include z-index(base); - - &:hover { - background-color: light-dark(darken(#f6f8fa, 3%), darken(#2a313c, 3%)); - } - - .fa-clone { - font-size: 1rem; - } - - &[data-copied] { - .fa-clone { - --fa: '\f00c'; - --fa-style: 900; - color: fi-var(global-link-hover-color); - } - } - - @include media('print') { - display: none !important; - } -} diff --git a/assets/scss/pages/single/_fixit-decryptor.scss b/assets/scss/pages/single/_fixit-decryptor.scss index a2d06281..fdb510d8 100644 --- a/assets/scss/pages/single/_fixit-decryptor.scss +++ b/assets/scss/pages/single/_fixit-decryptor.scss @@ -1,3 +1,8 @@ +@use "sass:color"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; + .fixit-decryptor-container { font-family: fi-var(global-font-family); text-align: center; @@ -22,11 +27,11 @@ &:active, &:focus, &:hover { - color: light-dark(darken($global-link-hover-color, 6%), lighten($global-link-hover-color-dark, 6%)); + color: light-dark(color.adjust($global-link-hover-color, $lightness: -6%), color.adjust($global-link-hover-color-dark, $lightness: 6%)); border-color: fi-var(global-link-hover-color); box-shadow: 0px 0px 5px 0px light-dark( - lighten($global-link-hover-color, 20%), - darken($global-link-hover-color-dark, 20%) + color.adjust($global-link-hover-color, $lightness: 20%), + color.adjust($global-link-hover-color-dark, $lightness: -20%) ); } } diff --git a/assets/scss/pages/single/_footer.scss b/assets/scss/pages/single/_footer.scss index 86217644..cb9ea2dc 100644 --- a/assets/scss/pages/single/_footer.scss +++ b/assets/scss/pages/single/_footer.scss @@ -1,4 +1,7 @@ -.post-footer { +@use "core/functions" as *; +@use "core/mixins" as *; + +.single .post-footer { margin: 2rem 0; &~.post-reward { diff --git a/assets/scss/pages/single/_index.scss b/assets/scss/pages/single/_index.scss index 939fbb4d..d3da1424 100644 --- a/assets/scss/pages/single/_index.scss +++ b/assets/scss/pages/single/_index.scss @@ -1,549 +1,10 @@ -@import 'toc'; -@import 'fixit-decryptor'; -@import 'collections'; -@import 'related'; -@import 'color-preview'; - -.single { - .single-title { - padding: 1rem 0 0.5rem; - margin: 0; - font-size: 1.6rem; - font-weight: bold; - line-height: 140%; - - .icon-pin { - display: inline-block; - width: max-content; - transform: rotate(30deg); - margin-right: 0.25em; - color: fi-var(danger); - } - - .icon-repost { - display: inline-block; - width: max-content; - margin-right: 0.25em; - color: fi-var(success); - } - - @include media('print') { - text-align: center; - } - } - - .single-subtitle { - margin-block: 0 0.5rem; - font-size: 1.2rem; - font-weight: normal; - line-height: 1.15; - - @include media('print') { - text-align: center; - } - } - - .post-meta { - font-size: 0.875rem; - color: fi-var(global-font-secondary-color); - - span { - display: inline-block; - } - - @include link(false, true); - - .author { - font-size: 1.05rem; - - img.avatar { - width: 1.3rem; - height: 1.3rem; - vertical-align: text-bottom; - border: 0.25px solid fi-var(global-border-color); - box-sizing: border-box; - object-position: center; - @include border-radius(full); - object-fit: cover; - } - } - - @include media('print') { - text-align: center; - - .comment-visitors, - .comment-count { - display: none; - } - } - } - - .featured-image { - margin: 1rem 0; - - img { - display: block; - width: 100%; - max-width: 100%; - height: auto; - margin: 0 auto; - overflow: hidden; - } - } - - #content[data-end-flag]:not([data-password])::after { - content: attr(data-end-flag); - display: block; - text-align: center; - font-size: 0.875em; - padding: 0.25rem; - color: fi-var(global-font-tertiary-color); - opacity: 0.8; - } - - hr { - position: relative; - height: 1em; - margin-block: 1em; - background-image: linear-gradient(to right, fi-var(global-border-color) 50%, transparent 50%); - background-size: 10px 2px; - background-position: center; - background-repeat: repeat-x; - border: none; - - &.awesome-hr { - &::before { - display: inline-block; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - position: absolute; - left: 5%; - top: 50%; - transform: translateY(-50%); - color: fi-var(hr-active-color); - font-size: 20px; - line-height: 1; - transition: left 1s ease-in-out, color 0.3s ease, border-color 0.3s ease; - font-family: var(--fa-family-classic); - font-weight: 600; - content: '\f0c4'; - @include z-index(base); - } - - &:hover::before { - left: calc(95% - 20px); - } - } - } - - .content { - margin-block: 1rem; - - > h1, - > h2 { - font-size: 1.5em; - padding-bottom: 0.3em; - border-bottom: 1px solid fi-var(global-border-color-light); - } - - > h3 { - font-size: 1.25em; - } - - > h4 { - font-size: 1em; - } - - > h5 { - font-size: 0.875em; - } - - > h6 { - font-size: 0.85em; - } - - .heading-element { - display: flex; - font-weight: bold; - line-height: 1.25; - margin-top: 1.5rem; - margin-bottom: 1rem; - font-weight: light-dark(bold, bolder); - - &:hover, - &:target { - > .heading-mark { - opacity: 1; - } - } - - > .heading-mark { - display: inline-flex; - align-items: center; - opacity: 0; - padding-inline: 0.5rem; - transition: all 0.2s ease-in-out; - @include link(false, false); - - svg { - fill: currentColor; - } - } - } - - p { - margin: 0.5rem 0; - } - - b, - strong { - @include bold-dark; - } - - @include link(false, false); - - a { - white-space: normal; - @include overflow-wrap(break-word); - - b, - strong { - color: inherit; - } - } - - > ul, - > ol, - > dl { - margin-block: 0.5em; - } - - :is(ol, ul) :is(ol, ul) { - padding-left: 20px; - } - - li::marker { - color: fi-var(secondary); - } - li:hover::marker { - color: fi-var(single-link-hover-color); - } - - // Task lists - li { - &[data-task] { - list-style: none; - @include task-icon { - margin: 0 0.25em 0 -1.5em; - color: fi-var(task-icon-color, fi-var(secondary)); - }; - @include task-text { - color: fi-var(task-text-color); - } - } - - &[data-task='x'] { - @include task-icon { - background-color: fi-rgba(fi-var(secondary), 0.2); - mask-image: linear-gradient(#000, #000); - mask-size: 0.875em 0.875em; - mask-repeat: no-repeat; - mask-position: center; - }; - } - - &[data-task='-'] { - @include task-icon { - opacity: 0.7; - }; - @include task-text(fi-var(secondary)) { - opacity: 0.7; - text-decoration: line-through fi-var(secondary); - } - } - - &[data-task='/'] { - @include task-icon(fi-var(success)); - @include task-text(fi-var(success)); - } - - &[data-task='!'], - &[data-task='!x'] { - @include task-icon(fi-var(danger)); - } - - &[data-task='?'] { - @include task-icon(fi-var(warning)); - } - } - - dl { - dt { - font-weight: bold; - } - dd { - margin: 0.25em 0 1em; - } - } - - ruby { - background-color: fi-var(code-block-background-color); - - rt { - color: fi-var(global-font-secondary-color); - } - } - - mark { - padding: 0.14em 0.28em; - background-color: fi-var(mark-background-color, mark); - color: fi-var(mark-color, marktext); - @include border-radius; - - @each $color, $value in $theme-colors { - &.mark-#{$color} { - @include set-fi-vars(( - mark-color: #fff, - mark-background-color: fi-var(#{$color}), - )); - } - } - } - - .table-wrapper { - position: relative; - overflow-x: auto; - overflow-y: hidden; - margin: 0.5rem 0; - background: fi-var(table-background-color); - border: 1px solid fi-var(table-border-color); - @include border-radius; - @include set-fi-var(scrollbar-thumb-color, fi-var(table-background-color)); - - > table { - width: 100%; - max-width: 100%; - margin: 0; - border-spacing: 0; - background: fi-var(table-background-color); - border-collapse: separate; - - thead { - background: fi-var(table-thead-color); - } - - &:not([class]) tbody { - & tr:nth-child(odd) { - background: light-dark(darken($table-background-color, 2.25%), lighten($table-background-color-dark, 2.75%)); - } - - & tr:hover { - background: light-dark(darken($table-background-color, 4.5%), lighten($table-background-color-dark, 5.5%)); - } - } - - th, - td { - padding: 0.3rem 1rem; - border-right: 1px solid fi-var(table-border-color); - border-bottom: 1px solid fi-var(table-border-color); - } - - tr > :last-child { - border-right: none; - } - - > :last-child > tr:last-child > * { - border-bottom: none; - } - } - } - - img { - max-width: 100%; - // SVG performs differently in Safari compared to other browsers - &:not([src*='.svg']) { - min-height: 1em; - } - } - svg { - max-width: 100%; - } - - figure { - margin: 0.5rem; - text-align: center; - - .image-caption:not(:empty) { - min-width: 20%; - max-width: 80%; - display: inline-block; - padding: 0.5rem; - margin: 0 auto; - font-size: 0.875rem; - color: #969696; - } - - img { - display: block; - margin: 0 auto; - height: auto; - overflow: hidden; - } - } - - blockquote { - display: block; - border-left: 0.25em solid fi-var(global-border-color); - color: fi-var(blockquote-color); - padding: 0 1em; - margin: 1rem 0; - - ul, - ol { - padding-left: 1.5em; - } - - dl dd { - margin-inline-start: 0.25em; - } - - @include link(false, true); - } - - sup[id^='fnref']:has(.footnote-ref) { - padding: 2px 4px; - 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); - - @include link(true, false); - } - - .footnotes { - font-size: 0.75rem; - white-space: normal; - color: light-dark(#a3a3ae, #9d9d9f); - @include link(false, true); - - ol { - margin-block: 1em; - padding-left: 1rem; - } - - hr { - background-color: fi-var(global-border-color); - height: 1px; - margin-block: 1em; - } - - code { - font-size: 0.75rem; - } - - a:not(.footnote-backref) { - color: inherit; - text-decoration: underline; - } - - p { - margin: 0.25rem 0; - } - } - - kbd { - display: inline-block; - padding: 0 0.25rem; - background-color: fi-var(global-background-color); - border: 1px solid fi-var(global-border-color); - font-size: 0.8rem; - font-family: fi-var(code-font-family); - color: fi-var(code-color); - box-shadow: inset 0 -1px 0 fi-var(global-border-color); - @include border-radius; - } - - // KaTeX styles - .katex-display { - max-width: 100%; - overflow-x: auto; - overflow-y: hidden; - // neutralize the margin-right setting of .katex .vlist-t2 - // to prevent scrollbars from appearing when the width is not full - padding-right: 2px; - } - .katex-error { - padding: 0.14em 0.28em; - background-color: mark; - cursor: help; - @include border-radius; - } - - // MathJax styles - mjx-container[jax='CHTML'] { - // formula Blocks - &[display='true'] { - max-width: 100%; - overflow-x: auto; - overflow-y: hidden; - } - - // fix the color of xypic diagrams - mjx-xypic { - g { - stroke: currentColor !important; - } - mjx-xypic-object { - color: currentColor !important; - fill: currentColor !important; - } - } - } - - json-viewer[boxed] + json-viewer[boxed] { - margin-top: 0.5rem; - } - - @include media('lg', 'up') { - :is(.code-block, .diagram-container) { - :is(.code-copy-btn, .diagram-copy-btn) { - display: none; - opacity: 0; - transform: scale(0.9); - transition: - display 0.25s allow-discrete, - opacity 0.25s, - transform 0.25s, - color 0.1s, - background-color 0.1s, - box-shadow 0.1s, - border-color 0.1s; - } - &:hover { - :is(.code-copy-btn, .diagram-copy-btn) { - display: block; - opacity: 1; - transform: scale(1); - @starting-style { - opacity: 0; - transform: scale(0.9); - } - } - } - } - } - } - - @import 'shortcodes'; - @import 'alert'; - @import 'code'; - @import 'diagram-copy-btn'; - @import 'reward'; - @import 'footer'; - // @import 'comment'; -} - -.lg-toolbar .lg-icon::after { - color: #999; -} +@use "base"; +@use "shortcodes"; +@use "alert"; +@use "code"; +@use "collections"; +@use "fixit-decryptor"; +@use "footer"; +@use "related"; +@use "reward"; +@use "toc"; diff --git a/assets/scss/pages/single/_related.scss b/assets/scss/pages/single/_related.scss index a4a6824c..dc8fa3d4 100644 --- a/assets/scss/pages/single/_related.scss +++ b/assets/scss/pages/single/_related.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + // Related Content List (aside) .related-details { &:not(:first-of-type)::before { diff --git a/assets/scss/pages/single/_reward.scss b/assets/scss/pages/single/_reward.scss index e0762308..648e6fae 100644 --- a/assets/scss/pages/single/_reward.scss +++ b/assets/scss/pages/single/_reward.scss @@ -1,4 +1,9 @@ -.post-reward { +@use "sass:color"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; + +.single .post-reward { padding: 1rem; text-align: center; @@ -60,8 +65,8 @@ box-sizing: border-box; cursor: pointer; outline: none; - color: fi-var(reward-color, lighten($reward-color, 5%)); - border: 2px solid light-dark(lighten($reward-color, 10%), lighten($reward-color, 5%)); + color: fi-var(reward-color, color.adjust($reward-color, $lightness: 5%)); + border: 2px solid light-dark(color.adjust($reward-color, $lightness: 10%), color.adjust($reward-color, $lightness: 5%)); padding: 0.6rem 1rem; background-color: fi-var(global-background-color); vertical-align: text-top; diff --git a/assets/scss/pages/single/_toc.scss b/assets/scss/pages/single/_toc.scss index dcc812c7..2ea910cd 100644 --- a/assets/scss/pages/single/_toc.scss +++ b/assets/scss/pages/single/_toc.scss @@ -1,3 +1,8 @@ +@use "sass:color"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; + // Common styles for all TOC types .toc { .toc-title { @@ -10,7 +15,7 @@ font-size: fi-var(toc-content-font-size); code { - @extend %code; + @include inline-code; } ul { @@ -205,7 +210,7 @@ .toc-title { @include set-fi-var( code-header-background-color, - light-dark(darken($code-header-background-color, 1%), darken($code-header-background-color-dark, 1%)) + light-dark(color.adjust($code-header-background-color, $lightness: -1%), color.adjust($code-header-background-color-dark, $lightness: -1%)) ); } } diff --git a/assets/scss/pages/single/code-syntax/_dark.scss b/assets/scss/pages/single/code-syntax/_dark.scss index 0bf58d21..ab3e84ce 100644 --- a/assets/scss/pages/single/code-syntax/_dark.scss +++ b/assets/scss/pages/single/code-syntax/_dark.scss @@ -1,3 +1,5 @@ +@use "override" as *; + /* Maintained by @Lruihao for FixIt theme */ // /* Background */ .bg { } diff --git a/assets/scss/pages/single/code-syntax/_github-dark-dimmed.scss b/assets/scss/pages/single/code-syntax/_github-dark-dimmed.scss deleted file mode 100644 index a8674615..00000000 --- a/assets/scss/pages/single/code-syntax/_github-dark-dimmed.scss +++ /dev/null @@ -1,175 +0,0 @@ -/// GitHub Dark Dimmed -/// copyright (c) 2012 - current GitHub, Inc. -/// Licensed under MIT (https://github.com/primer/github-syntax-theme-generator/blob/master/LICENSE) -/// Maintained by @Lruihao for FixIt theme -/// Created on 2022-03-01 -/// Updated on 2026-02-03 -& { - @include set-fi-vars(( - // Blob colors - color-blob-num: #778491, - color-blob-code-inner: #c5d1de, - // Syntax colors - color-prettylights-syntax-comment: #768390, - color-prettylights-syntax-constant: #6cb6ff, - color-prettylights-syntax-constant-other-reference-link: #96d0ff, - color-prettylights-syntax-entity: #dcbdfb, - color-prettylights-syntax-storage-modifier-import: #adbac7, - color-prettylights-syntax-entity-tag: #8ddb8c, - color-prettylights-syntax-keyword: #f47067, - color-prettylights-syntax-string: #96d0ff, - color-prettylights-syntax-variable: #f69d50, - color-prettylights-syntax-brackethighlighter-unmatched: #e5534b, - color-prettylights-syntax-brackethighlighter-angle: #768390, - color-prettylights-syntax-invalid-illegal-text: #cdd9e5, - color-prettylights-syntax-invalid-illegal-bg: #922323, - color-prettylights-syntax-carriage-return-text: #cdd9e5, - color-prettylights-syntax-carriage-return-bg: #ad2e2c, - color-prettylights-syntax-string-regexp: #8ddb8c, - color-prettylights-syntax-markup-list: #eac55f, - color-prettylights-syntax-markup-heading: #316dca, - color-prettylights-syntax-markup-italic: #adbac7, - color-prettylights-syntax-markup-bold: #adbac7, - color-prettylights-syntax-markup-deleted-text: #ffd8d3, - color-prettylights-syntax-markup-deleted-bg: #78191b, - color-prettylights-syntax-markup-inserted-text: #b4f1b4, - color-prettylights-syntax-markup-inserted-bg: #1b4721, - color-prettylights-syntax-markup-changed-text: #ffddb0, - color-prettylights-syntax-markup-changed-bg: #682d0f, - color-prettylights-syntax-markup-ignored-text: #adbac7, - color-prettylights-syntax-markup-ignored-bg: #255ab2, - color-prettylights-syntax-meta-diff-range: #dcbdfb, - color-prettylights-syntax-sublimelinter-gutter-mark: #545d68, - )); - - .blob-num { - color: fi-var(color-blob-num); - } - .blob-code-inner { - color: fi-var(color-blob-code-inner); - } - - .pl-c /* comment, punctuation.definition.comment, string.comment */ { - color: fi-var(color-prettylights-syntax-comment); - } - - .pl-c1 /* constant, entity.name.constant, variable.other.constant, variable.language, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header, meta.output */, - .pl-s .pl-v /* string variable */ { - color: fi-var(color-prettylights-syntax-constant); - } - - .pl-e /* entity */, - .pl-en /* entity.name */ { - color: fi-var(color-prettylights-syntax-entity); - } - - .pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */, - .pl-s .pl-s1 /* string source */ { - color: fi-var(color-prettylights-syntax-storage-modifier-import); - } - - .pl-ent /* entity.name.tag, markup.quote */ { - color: fi-var(color-prettylights-syntax-entity-tag); - } - - .pl-k /* keyword, storage, storage.type */ { - color: fi-var(color-prettylights-syntax-keyword); - } - - .pl-s /* string */, - .pl-pds /* punctuation.definition.string, source.regexp, string.regexp.character-class */, - .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, - .pl-sr /* string.regexp */, - .pl-sr .pl-cce /* string.regexp constant.character.escape */, - .pl-sr .pl-sre /* string.regexp source.ruby.embedded */, - .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ { - color: fi-var(color-prettylights-syntax-string); - } - - .pl-v /* variable */, - .pl-smw /* sublimelinter.mark.warning */ { - color: fi-var(color-prettylights-syntax-variable); - } - - .pl-bu /* invalid.broken, invalid.deprecated, invalid.unimplemented, message.error, brackethighlighter.unmatched, sublimelinter.mark.error */ { - color: fi-var(color-prettylights-syntax-brackethighlighter-unmatched); - } - - .pl-ii /* invalid.illegal */ { - color: fi-var(color-prettylights-syntax-invalid-illegal-text); - background-color: fi-var(color-prettylights-syntax-invalid-illegal-bg); - } - - .pl-c2 /* carriage-return */ { - color: fi-var(color-prettylights-syntax-carriage-return-text); - background-color: fi-var(color-prettylights-syntax-carriage-return-bg); - } - - .pl-c2::before /* carriage-return */ { - content: '^M'; - } - - .pl-sr .pl-cce /* string.regexp constant.character.escape */ { - font-weight: bold; - color: fi-var(color-prettylights-syntax-string-regexp); - } - - .pl-ml /* markup.list */ { - color: fi-var(color-prettylights-syntax-markup-list); - } - - .pl-mh /* markup.heading */, - .pl-mh .pl-en /* markup.heading entity.name */, - .pl-ms /* meta.separator */ { - font-weight: bold; - color: fi-var(color-prettylights-syntax-markup-heading); - } - - .pl-mi /* markup.italic */ { - font-style: italic; - color: fi-var(color-prettylights-syntax-markup-italic); - } - - .pl-mb /* markup.bold */ { - font-weight: bold; - color: fi-var(color-prettylights-syntax-markup-bold); - } - - .pl-md /* markup.deleted, meta.diff.header.from-file, punctuation.definition.deleted */ { - color: fi-var(color-prettylights-syntax-markup-deleted-text); - background-color: fi-var(color-prettylights-syntax-markup-deleted-bg); - } - - .pl-mi1 /* markup.inserted, meta.diff.header.to-file, punctuation.definition.inserted */ { - color: fi-var(color-prettylights-syntax-markup-inserted-text); - background-color: fi-var(color-prettylights-syntax-markup-inserted-bg); - } - - .pl-mc /* markup.changed, punctuation.definition.changed */ { - color: fi-var(color-prettylights-syntax-markup-changed-text); - background-color: fi-var(color-prettylights-syntax-markup-changed-bg); - } - - .pl-mi2 /* markup.ignored, markup.untracked */ { - color: fi-var(color-prettylights-syntax-markup-ignored-text); - background-color: fi-var(color-prettylights-syntax-markup-ignored-bg); - } - - .pl-mdr /* meta.diff.range */ { - font-weight: bold; - color: fi-var(color-prettylights-syntax-meta-diff-range); - } - - .pl-ba /* brackethighlighter.tag, brackethighlighter.curly, brackethighlighter.round, brackethighlighter.square, brackethighlighter.angle, brackethighlighter.quote */ { - color: fi-var(color-prettylights-syntax-brackethighlighter-angle); - } - - .pl-sg /* sublimelinter.gutter-mark */ { - color: fi-var(color-prettylights-syntax-sublimelinter-gutter-mark); - } - - .pl-corl /* constant.other.reference.link, string.other.link */ { - text-decoration: underline; - color: fi-var(color-prettylights-syntax-constant-other-reference-link); - } -} diff --git a/assets/scss/pages/single/code-syntax/_index.scss b/assets/scss/pages/single/code-syntax/_index.scss index 78ab2dd7..e011a28f 100644 --- a/assets/scss/pages/single/code-syntax/_index.scss +++ b/assets/scss/pages/single/code-syntax/_index.scss @@ -1,13 +1,17 @@ -/** - * Code syntax highlighting for Chroma - * - * To generate syntax highlighting styles from the command line, - * See https://gohugo.io/quick-reference/syntax-highlighting-styles/#styles - * For example: - * hugo gen chromastyles --style=github > assets/css/pages/single/code-syntax/light.scss - * hugo gen chromastyles --style=github-dark > assets/css/pages/single/code-syntax/dark.scss - */ -.highlight { +// ==================================================================================================== +// Code syntax highlighting for Chroma +// +// To generate syntax highlighting styles from the command line, +// See https://gohugo.io/quick-reference/syntax-highlighting-styles/#styles +// For example: +// hugo gen chromastyles --style=github > assets/scss/pages/single/code-syntax/_light.scss +// hugo gen chromastyles --style=github-dark > assets/scss/pages/single/code-syntax/_dark.scss +// ==================================================================================================== +@use "sass:meta"; +@use "core/functions" as *; +@use "core/mixins" as *; + +.single .highlight { /* LineLink */ .lnlinks { outline: none; @@ -21,9 +25,9 @@ box-shadow: -0.75rem 0 0 fi-var(code-highlight-color), 0.75rem 0 0 fi-var(code-highlight-color); } - @import 'light'; + @include meta.load-css('light'); @include dark-mode { - @import 'dark'; + @include meta.load-css('dark'); } } diff --git a/assets/scss/pages/single/code-syntax/_light.scss b/assets/scss/pages/single/code-syntax/_light.scss index dbd454eb..f8a7f6a2 100644 --- a/assets/scss/pages/single/code-syntax/_light.scss +++ b/assets/scss/pages/single/code-syntax/_light.scss @@ -1,3 +1,5 @@ +@use "override" as *; + /* Maintained by @Lruihao for FixIt theme */ // /* Background */ .bg { } diff --git a/assets/scss/pages/single/shortcodes/_admonition.scss b/assets/scss/pages/single/shortcodes/_admonition.scss index 33eac5b1..94b2bd3e 100644 --- a/assets/scss/pages/single/shortcodes/_admonition.scss +++ b/assets/scss/pages/single/shortcodes/_admonition.scss @@ -1,4 +1,10 @@ -.admonition { +@use "sass:color"; +@use "sass:map"; +@use "core/functions" as *; +@use "core/mixins" as *; +@use "core/maps" as maps; + +.single .admonition { position: relative; margin: 1rem 0; padding: 0 0.75rem; @@ -80,19 +86,19 @@ // default admonition type is note @include set-fi-vars(( - admonition-color: map-get(map-get($admonition-color-map, note), color), - admonition-bg-color: map-get(map-get($admonition-color-map, note), bg-color), - admonition-bg-color-collapsed: opacify(map-get(map-get($admonition-color-map, note), bg-color), 0.15), + admonition-color: map.get(map.get(maps.$admonition-color-map, note), color), + admonition-bg-color: map.get(map.get(maps.$admonition-color-map, note), bg-color), + admonition-bg-color-collapsed: color.adjust(map.get(map.get(maps.$admonition-color-map, note), bg-color), $alpha: 0.15), )); // set color for each admonition type - @each $type, $item in $admonition-color-map { + @each $type, $item in maps.$admonition-color-map { @if $type != 'note' { &.#{$type} { @include set-fi-vars(( - admonition-color: map-get($item, color), - admonition-bg-color: map-get($item, bg-color), - admonition-bg-color-collapsed: opacify(map-get($item, bg-color), 0.15), + admonition-color: map.get($item, color), + admonition-bg-color: map.get($item, bg-color), + admonition-bg-color-collapsed: color.adjust(map.get($item, bg-color), $alpha: 0.15), )); } } diff --git a/assets/scss/pages/single/shortcodes/_bilibili.scss b/assets/scss/pages/single/shortcodes/_bilibili.scss index ce47f3c6..a0202b54 100644 --- a/assets/scss/pages/single/shortcodes/_bilibili.scss +++ b/assets/scss/pages/single/shortcodes/_bilibili.scss @@ -1,4 +1,4 @@ -.bilibili { +.single .bilibili { position: relative; width: 100%; height: auto; diff --git a/assets/scss/pages/single/shortcodes/_bluesky.scss b/assets/scss/pages/single/shortcodes/_bluesky.scss index 82e4e9d3..9f70e32e 100644 --- a/assets/scss/pages/single/shortcodes/_bluesky.scss +++ b/assets/scss/pages/single/shortcodes/_bluesky.scss @@ -1,3 +1,3 @@ -.bluesky-embed { +.single .bluesky-embed { margin: 1rem auto; } diff --git a/assets/scss/pages/single/shortcodes/_cardlink.scss b/assets/scss/pages/single/shortcodes/_cardlink.scss index 3e50923a..fbae38fd 100644 --- a/assets/scss/pages/single/shortcodes/_cardlink.scss +++ b/assets/scss/pages/single/shortcodes/_cardlink.scss @@ -1,6 +1,8 @@ -/// Card link style -/// @author @Lruihao https://lruihao.cn -.card-link { +// Card link style +@use "core/functions" as *; +@use "core/mixins" as *; + +.single .card-link { position: relative; display: block; margin: 0.5rem auto; diff --git a/assets/scss/pages/single/shortcodes/_center-quote.scss b/assets/scss/pages/single/shortcodes/_center-quote.scss index d549243f..9e98ee42 100644 --- a/assets/scss/pages/single/shortcodes/_center-quote.scss +++ b/assets/scss/pages/single/shortcodes/_center-quote.scss @@ -1,4 +1,6 @@ -blockquote.blockquote-center { +@use "core/functions" as *; + +.single blockquote.blockquote-center { border-left: none; margin: 40px 0; padding: 0; diff --git a/assets/scss/pages/single/shortcodes/_douyin.scss b/assets/scss/pages/single/shortcodes/_douyin.scss index 86279cc0..9f573abc 100644 --- a/assets/scss/pages/single/shortcodes/_douyin.scss +++ b/assets/scss/pages/single/shortcodes/_douyin.scss @@ -1,4 +1,6 @@ -.douyin { +@use "core/mixins" as *; + +.single .douyin { position: relative; width: 100%; height: 0; @@ -13,10 +15,9 @@ left: 0; top: 0; } -} -@include media('xs') { - .douyin { + // [todo] get width and height from api + @include media('xs') { width: 92%; margin-inline: 4%; padding-bottom: 190%; diff --git a/assets/scss/pages/single/shortcodes/_echarts.scss b/assets/scss/pages/single/shortcodes/_echarts.scss index 857751c1..93b80591 100644 --- a/assets/scss/pages/single/shortcodes/_echarts.scss +++ b/assets/scss/pages/single/shortcodes/_echarts.scss @@ -1,4 +1,6 @@ -.echarts { +@use "core/mixins" as *; + +.single .echarts { text-align: center; &:not([_echarts_instance_])::before { diff --git a/assets/scss/pages/single/shortcodes/_file-tree.scss b/assets/scss/pages/single/shortcodes/_file-tree.scss index 9a0ec9d8..9681fd49 100644 --- a/assets/scss/pages/single/shortcodes/_file-tree.scss +++ b/assets/scss/pages/single/shortcodes/_file-tree.scss @@ -1,4 +1,7 @@ -.file-tree-wrapper { +@use "core/functions" as *; +@use "core/mixins" as *; + +.single .file-tree-wrapper { border: 1px solid fi-var(global-border-color); padding: 0.5rem; font-size: 0.8rem; diff --git a/assets/scss/pages/single/shortcodes/_gist.scss b/assets/scss/pages/single/shortcodes/_gist.scss new file mode 100644 index 00000000..8d475911 --- /dev/null +++ b/assets/scss/pages/single/shortcodes/_gist.scss @@ -0,0 +1,216 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + +.single .gist { + background-color: fi-var(code-block-background-color); + @include border-radius; + + .gist-file, + .gist-data, + .gist-meta { + border: none; + } + + .gist-data { + background-color: inherit; + + .blob-wrapper { + padding-block: 0.5rem; + overscroll-behavior-x: none; + + table.highlight { + background-color: inherit; + overflow: initial; + + td.blob-num { + position: sticky; + left: 0; + background-color: fi-var(code-block-background-color); + @include z-index(base); + } + + td.blob-code { + padding-left: 2px !important; + } + } + } + } + + .gist-meta { + padding: 0.4rem 0.8rem; + background-color: fi-var(code-header-background-color); + @include link(false, false); + @include border-radius(bottom); + } + + // GitHub Dark Dimmed style + @include dark-mode { + @include set-fi-vars(( + // Blob colors + color-blob-num: #778491, + color-blob-code-inner: #c5d1de, + // Syntax colors + color-prettylights-syntax-comment: #768390, + color-prettylights-syntax-constant: #6cb6ff, + color-prettylights-syntax-constant-other-reference-link: #96d0ff, + color-prettylights-syntax-entity: #dcbdfb, + color-prettylights-syntax-storage-modifier-import: #adbac7, + color-prettylights-syntax-entity-tag: #8ddb8c, + color-prettylights-syntax-keyword: #f47067, + color-prettylights-syntax-string: #96d0ff, + color-prettylights-syntax-variable: #f69d50, + color-prettylights-syntax-brackethighlighter-unmatched: #e5534b, + color-prettylights-syntax-brackethighlighter-angle: #768390, + color-prettylights-syntax-invalid-illegal-text: #cdd9e5, + color-prettylights-syntax-invalid-illegal-bg: #922323, + color-prettylights-syntax-carriage-return-text: #cdd9e5, + color-prettylights-syntax-carriage-return-bg: #ad2e2c, + color-prettylights-syntax-string-regexp: #8ddb8c, + color-prettylights-syntax-markup-list: #eac55f, + color-prettylights-syntax-markup-heading: #316dca, + color-prettylights-syntax-markup-italic: #adbac7, + color-prettylights-syntax-markup-bold: #adbac7, + color-prettylights-syntax-markup-deleted-text: #ffd8d3, + color-prettylights-syntax-markup-deleted-bg: #78191b, + color-prettylights-syntax-markup-inserted-text: #b4f1b4, + color-prettylights-syntax-markup-inserted-bg: #1b4721, + color-prettylights-syntax-markup-changed-text: #ffddb0, + color-prettylights-syntax-markup-changed-bg: #682d0f, + color-prettylights-syntax-markup-ignored-text: #adbac7, + color-prettylights-syntax-markup-ignored-bg: #255ab2, + color-prettylights-syntax-meta-diff-range: #dcbdfb, + color-prettylights-syntax-sublimelinter-gutter-mark: #545d68, + )); + + .blob-num { + color: fi-var(color-blob-num); + } + .blob-code-inner { + color: fi-var(color-blob-code-inner); + } + + .pl-c /* comment, punctuation.definition.comment, string.comment */ { + color: fi-var(color-prettylights-syntax-comment); + } + + .pl-c1 /* constant, entity.name.constant, variable.other.constant, variable.language, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header, meta.output */, + .pl-s .pl-v /* string variable */ { + color: fi-var(color-prettylights-syntax-constant); + } + + .pl-e /* entity */, + .pl-en /* entity.name */ { + color: fi-var(color-prettylights-syntax-entity); + } + + .pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */, + .pl-s .pl-s1 /* string source */ { + color: fi-var(color-prettylights-syntax-storage-modifier-import); + } + + .pl-ent /* entity.name.tag, markup.quote */ { + color: fi-var(color-prettylights-syntax-entity-tag); + } + + .pl-k /* keyword, storage, storage.type */ { + color: fi-var(color-prettylights-syntax-keyword); + } + + .pl-s /* string */, + .pl-pds /* punctuation.definition.string, source.regexp, string.regexp.character-class */, + .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, + .pl-sr /* string.regexp */, + .pl-sr .pl-cce /* string.regexp constant.character.escape */, + .pl-sr .pl-sre /* string.regexp source.ruby.embedded */, + .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ { + color: fi-var(color-prettylights-syntax-string); + } + + .pl-v /* variable */, + .pl-smw /* sublimelinter.mark.warning */ { + color: fi-var(color-prettylights-syntax-variable); + } + + .pl-bu /* invalid.broken, invalid.deprecated, invalid.unimplemented, message.error, brackethighlighter.unmatched, sublimelinter.mark.error */ { + color: fi-var(color-prettylights-syntax-brackethighlighter-unmatched); + } + + .pl-ii /* invalid.illegal */ { + color: fi-var(color-prettylights-syntax-invalid-illegal-text); + background-color: fi-var(color-prettylights-syntax-invalid-illegal-bg); + } + + .pl-c2 /* carriage-return */ { + color: fi-var(color-prettylights-syntax-carriage-return-text); + background-color: fi-var(color-prettylights-syntax-carriage-return-bg); + } + + .pl-c2::before /* carriage-return */ { + content: '^M'; + } + + .pl-sr .pl-cce /* string.regexp constant.character.escape */ { + font-weight: bold; + color: fi-var(color-prettylights-syntax-string-regexp); + } + + .pl-ml /* markup.list */ { + color: fi-var(color-prettylights-syntax-markup-list); + } + + .pl-mh /* markup.heading */, + .pl-mh .pl-en /* markup.heading entity.name */, + .pl-ms /* meta.separator */ { + font-weight: bold; + color: fi-var(color-prettylights-syntax-markup-heading); + } + + .pl-mi /* markup.italic */ { + font-style: italic; + color: fi-var(color-prettylights-syntax-markup-italic); + } + + .pl-mb /* markup.bold */ { + font-weight: bold; + color: fi-var(color-prettylights-syntax-markup-bold); + } + + .pl-md /* markup.deleted, meta.diff.header.from-file, punctuation.definition.deleted */ { + color: fi-var(color-prettylights-syntax-markup-deleted-text); + background-color: fi-var(color-prettylights-syntax-markup-deleted-bg); + } + + .pl-mi1 /* markup.inserted, meta.diff.header.to-file, punctuation.definition.inserted */ { + color: fi-var(color-prettylights-syntax-markup-inserted-text); + background-color: fi-var(color-prettylights-syntax-markup-inserted-bg); + } + + .pl-mc /* markup.changed, punctuation.definition.changed */ { + color: fi-var(color-prettylights-syntax-markup-changed-text); + background-color: fi-var(color-prettylights-syntax-markup-changed-bg); + } + + .pl-mi2 /* markup.ignored, markup.untracked */ { + color: fi-var(color-prettylights-syntax-markup-ignored-text); + background-color: fi-var(color-prettylights-syntax-markup-ignored-bg); + } + + .pl-mdr /* meta.diff.range */ { + font-weight: bold; + color: fi-var(color-prettylights-syntax-meta-diff-range); + } + + .pl-ba /* brackethighlighter.tag, brackethighlighter.curly, brackethighlighter.round, brackethighlighter.square, brackethighlighter.angle, brackethighlighter.quote */ { + color: fi-var(color-prettylights-syntax-brackethighlighter-angle); + } + + .pl-sg /* sublimelinter.gutter-mark */ { + color: fi-var(color-prettylights-syntax-sublimelinter-gutter-mark); + } + + .pl-corl /* constant.other.reference.link, string.other.link */ { + text-decoration: underline; + color: fi-var(color-prettylights-syntax-constant-other-reference-link); + } + } +} diff --git a/assets/scss/pages/single/shortcodes/_index.scss b/assets/scss/pages/single/shortcodes/_index.scss index d6adb673..90699f22 100644 --- a/assets/scss/pages/single/shortcodes/_index.scss +++ b/assets/scss/pages/single/shortcodes/_index.scss @@ -1,14 +1,14 @@ -@import 'admonition'; -@import 'bilibili'; -@import 'bluesky'; -@import 'douyin'; -@import 'cardlink'; -@import 'center-quote'; -@import 'echarts'; -@import 'file-tree'; -@import 'instagram'; -@import 'mapbox'; -@import 'mermaid'; -@import 'tabs'; -@import 'timeline'; -@import 'version'; +@use "admonition"; +@use "bilibili"; +@use "bluesky"; +@use "douyin"; +@use "cardlink"; +@use "center-quote"; +@use "echarts"; +@use "file-tree"; +@use "gist"; +@use "mapbox"; +@use "mermaid"; +@use "tabs"; +@use "timeline"; +@use "version"; diff --git a/assets/scss/pages/single/shortcodes/_instagram.scss b/assets/scss/pages/single/shortcodes/_instagram.scss deleted file mode 100644 index 7bebfbc3..00000000 --- a/assets/scss/pages/single/shortcodes/_instagram.scss +++ /dev/null @@ -1,5 +0,0 @@ -iframe.instagram-media { - @include dark-mode { - border: none !important; - } -} diff --git a/assets/scss/pages/single/shortcodes/_mapbox.scss b/assets/scss/pages/single/shortcodes/_mapbox.scss index 8ea75dc2..38572a59 100644 --- a/assets/scss/pages/single/shortcodes/_mapbox.scss +++ b/assets/scss/pages/single/shortcodes/_mapbox.scss @@ -1,4 +1,6 @@ -.mapbox { +@use "core/mixins" as *; + +.single .mapbox { margin: 0.5rem 0; padding: 0.5rem 0; @include border-radius; diff --git a/assets/scss/pages/single/shortcodes/_mermaid.scss b/assets/scss/pages/single/shortcodes/_mermaid.scss index 6088c5a1..73bc5091 100644 --- a/assets/scss/pages/single/shortcodes/_mermaid.scss +++ b/assets/scss/pages/single/shortcodes/_mermaid.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + @mixin mermaid-base { position: relative; box-sizing: border-box; diff --git a/assets/scss/pages/single/shortcodes/_tabs.scss b/assets/scss/pages/single/shortcodes/_tabs.scss index 1f75a5f8..954c1895 100644 --- a/assets/scss/pages/single/shortcodes/_tabs.scss +++ b/assets/scss/pages/single/shortcodes/_tabs.scss @@ -1,6 +1,9 @@ // ======================================== // Tabs shortcode - Multiple Design Styles // ======================================== +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; @mixin tab-button-size($padding-inline: 1.25rem, $padding-block: 0.75rem) { @if $padding-inline != 0 { diff --git a/assets/scss/pages/single/shortcodes/_timeline.scss b/assets/scss/pages/single/shortcodes/_timeline.scss index ee6331a3..c65707e3 100644 --- a/assets/scss/pages/single/shortcodes/_timeline.scss +++ b/assets/scss/pages/single/shortcodes/_timeline.scss @@ -1,4 +1,8 @@ -ul.#{$prefix}timeline { +@use "core/functions" as *; +@use "core/mixins" as *; +@use "override" as *; + +.single ul.#{$prefix}timeline { list-style: none; box-sizing: border-box; overflow: auto; diff --git a/assets/scss/pages/single/shortcodes/_version.scss b/assets/scss/pages/single/shortcodes/_version.scss index f87f7f15..5730f7a4 100644 --- a/assets/scss/pages/single/shortcodes/_version.scss +++ b/assets/scss/pages/single/shortcodes/_version.scss @@ -1,4 +1,4 @@ -.version-link { +.single .version-link { white-space: nowrap; img.version { diff --git a/assets/scss/pages/specials/_friends.scss b/assets/scss/pages/specials/_friends.scss index dc1a9113..12ac7c76 100644 --- a/assets/scss/pages/specials/_friends.scss +++ b/assets/scss/pages/specials/_friends.scss @@ -1,6 +1,7 @@ // Style of layout friends +@use "core/mixins" as *; -.special.friends { +&.friends { .friend-links { margin-top: 1rem; display: flex; diff --git a/assets/scss/pages/specials/_index.scss b/assets/scss/pages/specials/_index.scss index b9e7881e..72e62c17 100644 --- a/assets/scss/pages/specials/_index.scss +++ b/assets/scss/pages/specials/_index.scss @@ -1,9 +1,11 @@ +@use "sass:meta"; + .special { .single-title, .single-subtitle { text-align: right; } -} -@import 'friends'; -@import 'search'; + @include meta.load-css('friends'); + @include meta.load-css('search'); +} diff --git a/assets/scss/pages/specials/_search.scss b/assets/scss/pages/specials/_search.scss index 78d67bb1..52c145dc 100644 --- a/assets/scss/pages/specials/_search.scss +++ b/assets/scss/pages/specials/_search.scss @@ -1,5 +1,7 @@ // Style of layout search -.special.search { +@use "core/mixins" as *; + +&.search { // Google CSE .gcse-searchbox:empty, .gcse-searchresults:empty { diff --git a/assets/scss/widgets/_details.scss b/assets/scss/widgets/_details.scss index d0c4b2ec..8cba2d79 100644 --- a/assets/scss/widgets/_details.scss +++ b/assets/scss/widgets/_details.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + .details { > .details-summary { transition: border-radius 0.2s ease 0.2s; diff --git a/assets/scss/widgets/_fixed-button.scss b/assets/scss/widgets/_fixed-button.scss index abcc2c90..600297eb 100644 --- a/assets/scss/widgets/_fixed-button.scss +++ b/assets/scss/widgets/_fixed-button.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + .fixed-buttons { position: fixed; right: 1.5rem; diff --git a/assets/scss/widgets/_index.scss b/assets/scss/widgets/_index.scss index c23c7dad..287e8cb8 100644 --- a/assets/scss/widgets/_index.scss +++ b/assets/scss/widgets/_index.scss @@ -1,9 +1,9 @@ -@import 'third-party'; -@import 'details'; -@import 'fixed-button'; -@import 'icon'; -@import 'link-guard'; -@import 'mask'; -@import 'noscript-warning'; -@import 'reading-progress'; -@import 'scrollbar'; +@use "third-party"; +@use "details"; +@use "fixed-button"; +@use "icon"; +@use "link-guard"; +@use "mask"; +@use "noscript-warning"; +@use "reading-progress"; +@use "scrollbar"; diff --git a/assets/scss/widgets/_link-guard.scss b/assets/scss/widgets/_link-guard.scss index 8a04f579..063f4abb 100644 --- a/assets/scss/widgets/_link-guard.scss +++ b/assets/scss/widgets/_link-guard.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + #link-guard-dialog { width: MIN(600px, calc(100% - 2rem)); max-width: 100%; diff --git a/assets/scss/widgets/_mask.scss b/assets/scss/widgets/_mask.scss index b51784b3..b6c8e511 100644 --- a/assets/scss/widgets/_mask.scss +++ b/assets/scss/widgets/_mask.scss @@ -1,3 +1,5 @@ +@use "core/mixins" as *; + #mask { background-repeat: no-repeat; background-position: center; diff --git a/assets/scss/widgets/_noscript-warning.scss b/assets/scss/widgets/_noscript-warning.scss index 9dd7087b..b5d026b5 100644 --- a/assets/scss/widgets/_noscript-warning.scss +++ b/assets/scss/widgets/_noscript-warning.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + .noscript-warning { background-color: fi-var(danger); color: white; diff --git a/assets/scss/widgets/_reading-progress.scss b/assets/scss/widgets/_reading-progress.scss index 10543b69..0f156c51 100644 --- a/assets/scss/widgets/_reading-progress.scss +++ b/assets/scss/widgets/_reading-progress.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + .reading-progress-bar { height: fi-var(progress-h, 2px); width: fi-var(progress, 0); diff --git a/assets/scss/widgets/_scrollbar.scss b/assets/scss/widgets/_scrollbar.scss index 4d2bd9f0..36411112 100644 --- a/assets/scss/widgets/_scrollbar.scss +++ b/assets/scss/widgets/_scrollbar.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + /* Modern browsers with `scrollbar-*` support (high priority) */ @supports (scrollbar-width: auto) { * { diff --git a/assets/scss/widgets/third-party/_cookieconsent.scss b/assets/scss/widgets/third-party/_cookieconsent.scss index 6b07c6bb..4e73930d 100644 --- a/assets/scss/widgets/third-party/_cookieconsent.scss +++ b/assets/scss/widgets/third-party/_cookieconsent.scss @@ -1,3 +1,5 @@ +@use "core/functions" as *; + .cc-window.cc-banner { .cc-btn { color: fi-var(global-font-color); diff --git a/assets/scss/widgets/third-party/_github-corner.scss b/assets/scss/widgets/third-party/_github-corner.scss index 7f3aebb1..cd9921d5 100644 --- a/assets/scss/widgets/third-party/_github-corner.scss +++ b/assets/scss/widgets/third-party/_github-corner.scss @@ -1,3 +1,6 @@ +@use "core/functions" as *; +@use "core/mixins" as *; + @keyframes octocat-wave { 0%, 100% { diff --git a/assets/scss/widgets/third-party/_index.scss b/assets/scss/widgets/third-party/_index.scss index b850cc65..905b5eb7 100644 --- a/assets/scss/widgets/third-party/_index.scss +++ b/assets/scss/widgets/third-party/_index.scss @@ -1,5 +1,5 @@ -@import 'cell-tooltip'; -@import 'cookieconsent'; -@import 'github-corner'; -@import 'post-chat'; -@import 'typeit'; +@use "cell-tooltip"; +@use "cookieconsent"; +@use "github-corner"; +@use "post-chat"; +@use "typeit"; diff --git a/assets/scss/widgets/third-party/_post-chat.scss b/assets/scss/widgets/third-party/_post-chat.scss index 2086b8e8..a04419a2 100644 --- a/assets/scss/widgets/third-party/_post-chat.scss +++ b/assets/scss/widgets/third-party/_post-chat.scss @@ -1,3 +1,5 @@ +@use "core/mixins" as *; + #postChat_button, .post-TianliGPT { @include media('print') { diff --git a/assets/scss/widgets/third-party/_typeit.scss b/assets/scss/widgets/third-party/_typeit.scss index 688401b1..84256a83 100644 --- a/assets/scss/widgets/third-party/_typeit.scss +++ b/assets/scss/widgets/third-party/_typeit.scss @@ -1,3 +1,5 @@ +@use "core/functions" as *; + .typeit { .highlight { padding: 0.375rem; diff --git a/build.sh b/build.sh new file mode 100644 index 00000000..2fbb5ba3 --- /dev/null +++ b/build.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# @file +# Builds a Hugo site hosted on Vercel. +# +# The Vercel build image automatically installs Node.js dependencies. +# +# @example +# chmod a+x build.sh && ./build.sh +#------------------------------------------------------------------------------ + +# Exit on error, undefined variables, or pipe failures +set -euo pipefail + +build_temp_dir="" + +# Perform cleanup +cleanup() { + if [[ -n "${build_temp_dir:-}" && -d "${build_temp_dir}" ]]; then + rm -rf "${build_temp_dir}" + fi +} + +# Register the cleanup trap +trap cleanup EXIT SIGINT SIGTERM + +# Build demo/test with the correct Hugo baseURL on Vercel preview deployments. +# - demo: https://${VERCEL_URL} +# - test: https://${VERCEL_URL}/test +build() { + local target="$1" + if [[ ! "${target}" =~ ^(demo|test)$ ]]; then + echo "Unknown build target: ${target}" >&2 + return 2 + fi + + if [[ "${VERCEL_ENV:-}" != "preview" ]]; then + pnpm "build:${target}" + return + fi + + local base_url="https://${VERCEL_URL}" + if [[ "${target}" == "test" ]]; then + base_url+="/test" + fi + pnpm "build:${target}" --buildDrafts --baseURL "${base_url}" +} + +main() { + # Define tool versions + # You can also manage these via Environment Variables in the Vercel dashboard. + if [[ -z "${DART_SASS_VERSION:-}" ]]; then + DART_SASS_VERSION=1.99.0 + fi + if [[ -z "${GO_VERSION:-}" ]]; then + GO_VERSION=1.26.1 + fi + + # Set the build timezone + export TZ=Asia/Shanghai + + # Create and move into a temporary directory for downloads + build_temp_dir=$(mktemp -d) + pushd "${build_temp_dir}" > /dev/null + + # Create the local tools directory + mkdir -p "${HOME}/.local" + + # Install Dart Sass + echo "Installing Dart Sass ${DART_SASS_VERSION}..." + curl -sLJO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz" + tar -C "${HOME}/.local" -xf "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz" + export PATH="${HOME}/.local/dart-sass:${PATH}" + + # Install Go + # echo "Installing Go ${GO_VERSION}..." + # curl -sLJO "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" + # tar -C "${HOME}/.local" -xf "go${GO_VERSION}.linux-amd64.tar.gz" + # export PATH="${HOME}/.local/go/bin:${PATH}" + + # Install Go from the package manager + dnf install -y golang.x86_64 + + # Return to the project root + popd > /dev/null + + # Verify installations + echo "Verifying installations..." + echo Dart Sass: "$(sass --version)" + echo Go: "$(go version)" + echo Hugo: "$(hugo version)" + echo Node.js: "$(node --version)" + + # Configure Git + echo "Configuring Git..." + git config core.quotepath false + if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then + git fetch --unshallow + fi + + # Build the site + echo "Building the site..." + build demo & + build test & + wait + pnpm -F integration start +} + +main "$@" diff --git a/layouts/_partials/function/code-copy-btn.html b/layouts/_partials/function/code-copy-btn.html index e4a836c7..f990cecd 100644 --- a/layouts/_partials/function/code-copy-btn.html +++ b/layouts/_partials/function/code-copy-btn.html @@ -4,7 +4,7 @@ {{- if $config.copyable -}} {{- $copyIcon := dict "Class" "fa-regular fa-clone fa-width-auto" | partial "plugin/icon.html" -}} - {{- $copyBtn := printf `` (T "assets.copyToClipboard") (T "assets.copyToClipboard") $copyIcon -}} + {{- $copyBtn := printf `` (T "assets.copyToClipboard") (T "assets.copyToClipboard") (T "assets.copiedText") $copyIcon -}} {{- $wrapper = replaceRE `(
-
-