diff --git a/assets/js/base.js b/assets/js/base.js index 49f27d0160d..c7a3c149ff9 100644 --- a/assets/js/base.js +++ b/assets/js/base.js @@ -19,8 +19,8 @@ limitations under the License. 'use strict'; $(function() { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="popover"]').popover(); + $('[data-bs-toggle="tooltip"]').tooltip(); + $('[data-bs-toggle="popover"]').popover(); $('.popover-dismiss').popover({ trigger: 'focus' diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js new file mode 100644 index 00000000000..b90676dcc79 --- /dev/null +++ b/assets/js/click-to-copy.js @@ -0,0 +1,98 @@ +let codeListings = document.querySelectorAll('.highlight > pre'); + +for (let index = 0; index < codeListings.length; index++) { + const codeSample = codeListings[index].querySelector('code'); + if (!codeSample) { + continue; + } + + const copyButton = document.createElement('button'); + const buttonAttributes = { + type: 'button', + title: 'Copy to clipboard', + 'data-bs-toggle': 'tooltip', + 'data-bs-placement': 'top', + 'data-bs-container': 'body', + }; + + Object.keys(buttonAttributes).forEach((key) => { + copyButton.setAttribute(key, buttonAttributes[key]); + }); + + copyButton.classList.add( + 'fas', + 'fa-copy', + 'btn', + 'btn-sm', + 'td-click-to-copy' + ); + const tooltip = new bootstrap.Tooltip(copyButton); + + copyButton.onclick = () => { + copyCode(codeSample, copyButton, tooltip); + }; + + copyButton.onmouseout = () => { + copyButton.setAttribute('data-bs-original-title', 'Copy to clipboard'); + tooltip.hide(); + }; + + const buttonDiv = document.createElement('div'); + buttonDiv.classList.add('click-to-copy'); + buttonDiv.append(copyButton); + codeListings[index].insertBefore(buttonDiv, codeSample); +} + +const copyCode = (codeSample, copyButton, tooltip) => { + if (!codeSample) { + return; + } + + const isConsoleBlock = codeSample.matches( + "code[data-lang='console'], code.language-console" + ); + let text; + + if (isConsoleBlock) { + const clone = codeSample.cloneNode(true); + pruneUnselectableElements(codeSample, clone); + text = clone.textContent; + // For each command, strip the space after the prompt and before the + // command: + text = text.replace(/^ /gm, ''); + } else { + text = codeSample.textContent; + } + text = text ? text.trim() : ''; + + if (!navigator.clipboard) { + console.warn('Clipboard API not available (requires HTTPS or localhost)'); + return; + } + + navigator.clipboard.writeText(text + '\n').then(() => { + copyButton.setAttribute('data-bs-original-title', 'Copied!'); + tooltip.show(); + }).catch(error => { + console.warn('Failed to copy text to clipboard:', error); + }); +}; + +const pruneUnselectableElements = (sourceNode, cloneNode) => { + const sourceChildren = sourceNode.children; + const cloneChildren = cloneNode.children; + + for (let i = sourceChildren.length - 1; i >= 0; i--) { + const sourceChild = sourceChildren[i]; + const cloneChild = cloneChildren[i]; + const style = window.getComputedStyle(sourceChild); + const unselectable = style.userSelect === 'none'; + + if (unselectable) { + cloneChild.remove(); + continue; + } + + pruneUnselectableElements(sourceChild, cloneChild); + } +}; diff --git a/assets/js/dark-mode.js b/assets/js/dark-mode.js new file mode 100644 index 00000000000..e44fab779bf --- /dev/null +++ b/assets/js/dark-mode.js @@ -0,0 +1,92 @@ +/*! + * This is a Docsy-adapted version of https://github.com/twbs/examples/blob/main/color-modes/js/color-modes.js. + * + * Original header: + * + * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors + * Licensed under the Creative Commons Attribution 3.0 Unported License. + */ + +(() => { + 'use strict' + + const themeKey = 'td-color-theme' + const getStoredTheme = () => localStorage.getItem(themeKey) + const setStoredTheme = theme => localStorage.setItem(themeKey, theme) + + const getPreferredTheme = () => { + const storedTheme = getStoredTheme() + if (storedTheme) { + return storedTheme + } + + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' + } + + const setTheme = theme => { + if (theme === 'auto') { + document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')) + } else { + document.documentElement.setAttribute('data-bs-theme', theme) + } + } + + setTheme(getPreferredTheme()) + + document.documentElement.removeAttribute('data-theme-init') + + const showActiveTheme = (theme, focus = false) => { + const themeSwitcher = document.querySelector('#bd-theme') + + if (!themeSwitcher) { + return + } + + const themeSwitcherText = document.querySelector('#bd-theme-text') + const activeThemeIcon = document.querySelector('.theme-icon-active use') + const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`) + if (!btnToActive) { + return + } + const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href') + + document.querySelectorAll('[data-bs-theme-value]').forEach(element => { + element.classList.remove('active') + element.setAttribute('aria-pressed', 'false') + }) + + btnToActive.classList.add('active') + btnToActive.setAttribute('aria-pressed', 'true') + activeThemeIcon.setAttribute('href', svgOfActiveBtn) + if (themeSwitcherText) { + const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})` + themeSwitcher.setAttribute('aria-label', themeSwitcherLabel) + } + + if (focus) { + themeSwitcher.focus() + } + } + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + const storedTheme = getStoredTheme() + if (storedTheme !== 'light' && storedTheme !== 'dark') { + setTheme(getPreferredTheme()) + } + }) + + window.addEventListener('DOMContentLoaded', () => { + showActiveTheme(getPreferredTheme()) + + document.querySelectorAll('[data-bs-theme-value]') + .forEach(toggle => { + toggle.addEventListener('click', () => { + const theme = toggle.getAttribute('data-bs-theme-value') + setStoredTheme(theme) + setTheme(theme) + showActiveTheme(theme, true) + }) + }) + }) +})() diff --git a/assets/js/offline-search.js b/assets/js/offline-search.js index ae622d2f4c3..90c1a71094b 100644 --- a/assets/js/offline-search.js +++ b/assets/js/offline-search.js @@ -162,7 +162,7 @@ const $entry = $('
').addClass('mt-4'); $entry.append( - $('').addClass('d-block text-muted').text(r.ref) + $('').addClass('d-block text-body-secondary').text(r.ref) ); $entry.append( diff --git a/assets/scss/_alerts.scss b/assets/scss/_alerts.scss index 5ce5cc22a2c..1d5dcba40c7 100644 --- a/assets/scss/_alerts.scss +++ b/assets/scss/_alerts.scss @@ -2,7 +2,7 @@ .alert { font-weight: $font-weight-medium; - background: $white; + background: var(--bs-body-bg); color: inherit; border-radius: 0; diff --git a/assets/scss/_boxes.scss b/assets/scss/_boxes.scss index 164b0f6f6e3..60aba0551b6 100644 --- a/assets/scss/_boxes.scss +++ b/assets/scss/_boxes.scss @@ -3,8 +3,9 @@ // box-variant creates the main style for a colored section used on the site. @mixin box-variant($parent, $color-name, $color-value) { - $text-color: color-yiq($color-value); - $link-color: mix($blue, $text-color, lightness($color-value)); + @if type-of($color-value) == 'color' { + $text-color: color-contrast($color-value); + $link-color: mix($blue, $text-color); $link-hover-color: rgba($link-color, 0.5) !default; #{$parent} { @@ -27,15 +28,24 @@ content: ""; } } + + p > a { + color: $link-color; + + &:hover, + &:focus { + color: $link-hover-color; + } + } } } - // Improve contrast for the links in paragraphs. - @include link-variant("#{$parent}--#{$color-name} p > a", $link-color, $link-hover-color, false); - @if $enable-gradients { @include bg-gradient-variant("#{$parent}--1#{$color-name}#{$parent}--gradient", $color-value,true); } + } @else { + @warn "box-variant: #{$color-value} is not a color, skipping #{$parent}--#{$color-name}"; + } } // Common min-height modifiers used for boxes. diff --git a/assets/scss/_chroma-dark.scss b/assets/scss/_chroma-dark.scss new file mode 100644 index 00000000000..55757d15d82 --- /dev/null +++ b/assets/scss/_chroma-dark.scss @@ -0,0 +1,79 @@ +/* Generated using: hugo gen chromastyles --style=github-dark */ + +/* Background */ .bg { color:#e6edf3;background-color:#0d1117; } +/* PreWrapper */ .chroma { color:#e6edf3;background-color:#0d1117;-webkit-text-size-adjust:none; } +/* Error */ .chroma .err { color:#f85149 } +/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit } +/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; } +/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; } +/* LineHighlight */ .chroma .hl { background-color:#6e7681 } +/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679 } +/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681 } +/* Line */ .chroma .line { display:flex; } +/* Keyword */ .chroma .k { color:#ff7b72 } +/* KeywordConstant */ .chroma .kc { color:#79c0ff } +/* KeywordDeclaration */ .chroma .kd { color:#ff7b72 } +/* KeywordNamespace */ .chroma .kn { color:#ff7b72 } +/* KeywordPseudo */ .chroma .kp { color:#79c0ff } +/* KeywordReserved */ .chroma .kr { color:#ff7b72 } +/* KeywordType */ .chroma .kt { color:#ff7b72 } +/* NameClass */ .chroma .nc { color:#f0883e;font-weight:bold } +/* NameConstant */ .chroma .no { color:#79c0ff;font-weight:bold } +/* NameDecorator */ .chroma .nd { color:#d2a8ff;font-weight:bold } +/* NameEntity */ .chroma .ni { color:#ffa657 } +/* NameException */ .chroma .ne { color:#f0883e;font-weight:bold } +/* NameLabel */ .chroma .nl { color:#79c0ff;font-weight:bold } +/* NameNamespace */ .chroma .nn { color:#ff7b72 } +/* NameProperty */ .chroma .py { color:#79c0ff } +/* NameTag */ .chroma .nt { color:#7ee787 } +/* NameVariable */ .chroma .nv { color:#79c0ff } +/* NameVariableClass */ .chroma .vc { color:#79c0ff } +/* NameVariableGlobal */ .chroma .vg { color:#79c0ff } +/* NameVariableInstance */ .chroma .vi { color:#79c0ff } +/* NameVariableMagic */ .chroma .vm { color:#79c0ff } +/* NameFunction */ .chroma .nf { color:#d2a8ff;font-weight:bold } +/* NameFunctionMagic */ .chroma .fm { color:#d2a8ff;font-weight:bold } +/* Literal */ .chroma .l { color:#a5d6ff } +/* LiteralDate */ .chroma .ld { color:#79c0ff } +/* LiteralString */ .chroma .s { color:#a5d6ff } +/* LiteralStringAffix */ .chroma .sa { color:#79c0ff } +/* LiteralStringBacktick */ .chroma .sb { color:#a5d6ff } +/* LiteralStringChar */ .chroma .sc { color:#a5d6ff } +/* LiteralStringDelimiter */ .chroma .dl { color:#79c0ff } +/* LiteralStringDoc */ .chroma .sd { color:#a5d6ff } +/* LiteralStringDouble */ .chroma .s2 { color:#a5d6ff } +/* LiteralStringEscape */ .chroma .se { color:#79c0ff } +/* LiteralStringHeredoc */ .chroma .sh { color:#79c0ff } +/* LiteralStringInterpol */ .chroma .si { color:#a5d6ff } +/* LiteralStringOther */ .chroma .sx { color:#a5d6ff } +/* LiteralStringRegex */ .chroma .sr { color:#79c0ff } +/* LiteralStringSingle */ .chroma .s1 { color:#a5d6ff } +/* LiteralStringSymbol */ .chroma .ss { color:#a5d6ff } +/* LiteralNumber */ .chroma .m { color:#a5d6ff } +/* LiteralNumberBin */ .chroma .mb { color:#a5d6ff } +/* LiteralNumberFloat */ .chroma .mf { color:#a5d6ff } +/* LiteralNumberHex */ .chroma .mh { color:#a5d6ff } +/* LiteralNumberInteger */ .chroma .mi { color:#a5d6ff } +/* LiteralNumberIntegerLong */ .chroma .il { color:#a5d6ff } +/* LiteralNumberOct */ .chroma .mo { color:#a5d6ff } +/* Operator */ .chroma .o { color:#ff7b72;font-weight:bold } +/* OperatorWord */ .chroma .ow { color:#ff7b72;font-weight:bold } +/* Comment */ .chroma .c { color:#8b949e;font-style:italic } +/* CommentHashbang */ .chroma .ch { color:#8b949e;font-style:italic } +/* CommentMultiline */ .chroma .cm { color:#8b949e;font-style:italic } +/* CommentSingle */ .chroma .c1 { color:#8b949e;font-style:italic } +/* CommentSpecial */ .chroma .cs { color:#8b949e;font-weight:bold;font-style:italic } +/* CommentPreproc */ .chroma .cp { color:#8b949e;font-weight:bold;font-style:italic } +/* CommentPreprocFile */ .chroma .cpf { color:#8b949e;font-weight:bold;font-style:italic } +/* GenericDeleted */ .chroma .gd { color:#ffa198;background-color:#490202 } +/* GenericEmph */ .chroma .ge { font-style:italic } +/* GenericError */ .chroma .gr { color:#ffa198 } +/* GenericHeading */ .chroma .gh { color:#79c0ff;font-weight:bold } +/* GenericInserted */ .chroma .gi { color:#56d364;background-color:#0f5323 } +/* GenericOutput */ .chroma .go { color:#8b949e } +/* GenericPrompt */ .chroma .gp { color:#8b949e } +/* GenericStrong */ .chroma .gs { font-weight:bold } +/* GenericSubheading */ .chroma .gu { color:#79c0ff } +/* GenericTraceback */ .chroma .gt { color:#ff7b72 } +/* GenericUnderline */ .chroma .gl { text-decoration:underline } +/* TextWhitespace */ .chroma .w { color:#6e7681 } diff --git a/assets/scss/_chroma-light.scss b/assets/scss/_chroma-light.scss new file mode 100644 index 00000000000..a60a93799d7 --- /dev/null +++ b/assets/scss/_chroma-light.scss @@ -0,0 +1,74 @@ +/* Generated using: hugo gen chromastyles --style=github */ + +/* Background */ .bg { background-color:#f7f7f7; } +/* PreWrapper */ .chroma { background-color:#f7f7f7;-webkit-text-size-adjust:none; } +/* Error */ .chroma .err { color:#f6f8fa;background-color:#82071e } +/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit } +/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; } +/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; } +/* LineHighlight */ .chroma .hl { background-color:#dedede } +/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f } +/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f } +/* Line */ .chroma .line { display:flex; } +/* Keyword */ .chroma .k { color:#cf222e } +/* KeywordConstant */ .chroma .kc { color:#cf222e } +/* KeywordDeclaration */ .chroma .kd { color:#cf222e } +/* KeywordNamespace */ .chroma .kn { color:#cf222e } +/* KeywordPseudo */ .chroma .kp { color:#cf222e } +/* KeywordReserved */ .chroma .kr { color:#cf222e } +/* KeywordType */ .chroma .kt { color:#cf222e } +/* NameAttribute */ .chroma .na { color:#1f2328 } +/* NameClass */ .chroma .nc { color:#1f2328 } +/* NameConstant */ .chroma .no { color:#0550ae } +/* NameDecorator */ .chroma .nd { color:#0550ae } +/* NameEntity */ .chroma .ni { color:#6639ba } +/* NameLabel */ .chroma .nl { color:#900;font-weight:bold } +/* NameNamespace */ .chroma .nn { color:#24292e } +/* NameOther */ .chroma .nx { color:#1f2328 } +/* NameTag */ .chroma .nt { color:#0550ae } +/* NameBuiltin */ .chroma .nb { color:#6639ba } +/* NameBuiltinPseudo */ .chroma .bp { color:#6a737d } +/* NameVariable */ .chroma .nv { color:#953800 } +/* NameVariableClass */ .chroma .vc { color:#953800 } +/* NameVariableGlobal */ .chroma .vg { color:#953800 } +/* NameVariableInstance */ .chroma .vi { color:#953800 } +/* NameVariableMagic */ .chroma .vm { color:#953800 } +/* NameFunction */ .chroma .nf { color:#6639ba } +/* NameFunctionMagic */ .chroma .fm { color:#6639ba } +/* LiteralString */ .chroma .s { color:#0a3069 } +/* LiteralStringAffix */ .chroma .sa { color:#0a3069 } +/* LiteralStringBacktick */ .chroma .sb { color:#0a3069 } +/* LiteralStringChar */ .chroma .sc { color:#0a3069 } +/* LiteralStringDelimiter */ .chroma .dl { color:#0a3069 } +/* LiteralStringDoc */ .chroma .sd { color:#0a3069 } +/* LiteralStringDouble */ .chroma .s2 { color:#0a3069 } +/* LiteralStringEscape */ .chroma .se { color:#0a3069 } +/* LiteralStringHeredoc */ .chroma .sh { color:#0a3069 } +/* LiteralStringInterpol */ .chroma .si { color:#0a3069 } +/* LiteralStringOther */ .chroma .sx { color:#0a3069 } +/* LiteralStringRegex */ .chroma .sr { color:#0a3069 } +/* LiteralStringSingle */ .chroma .s1 { color:#0a3069 } +/* LiteralStringSymbol */ .chroma .ss { color:#032f62 } +/* LiteralNumber */ .chroma .m { color:#0550ae } +/* LiteralNumberBin */ .chroma .mb { color:#0550ae } +/* LiteralNumberFloat */ .chroma .mf { color:#0550ae } +/* LiteralNumberHex */ .chroma .mh { color:#0550ae } +/* LiteralNumberInteger */ .chroma .mi { color:#0550ae } +/* LiteralNumberIntegerLong */ .chroma .il { color:#0550ae } +/* LiteralNumberOct */ .chroma .mo { color:#0550ae } +/* Operator */ .chroma .o { color:#0550ae } +/* OperatorWord */ .chroma .ow { color:#0550ae } +/* Punctuation */ .chroma .p { color:#1f2328 } +/* Comment */ .chroma .c { color:#57606a } +/* CommentHashbang */ .chroma .ch { color:#57606a } +/* CommentMultiline */ .chroma .cm { color:#57606a } +/* CommentSingle */ .chroma .c1 { color:#57606a } +/* CommentSpecial */ .chroma .cs { color:#57606a } +/* CommentPreproc */ .chroma .cp { color:#57606a } +/* CommentPreprocFile */ .chroma .cpf { color:#57606a } +/* GenericDeleted */ .chroma .gd { color:#82071e;background-color:#ffebe9 } +/* GenericEmph */ .chroma .ge { color:#1f2328 } +/* GenericInserted */ .chroma .gi { color:#116329;background-color:#dafbe1 } +/* GenericOutput */ .chroma .go { color:#1f2328 } +/* GenericUnderline */ .chroma .gl { text-decoration:underline } +/* TextWhitespace */ .chroma .w { color:#fff } diff --git a/assets/scss/_code.scss b/assets/scss/_code.scss index d491beeb458..0e43e14ae82 100644 --- a/assets/scss/_code.scss +++ b/assets/scss/_code.scss @@ -1,16 +1,88 @@ -// Code formatting. +[data-bs-theme='light'] { + @import 'chroma-light'; +} + +@if $enable-dark-mode { + [data-bs-theme='dark'] { + @import 'chroma-dark'; + + .chroma { + background-color: var(--td-pre-bg) !important; + + // Color tuning for the `native` Chroma style: + .go { + color: #cccccc95; + } + } + } +} .td-content { // Highlighted code. .highlight { @extend .card; - margin: 2rem 0; padding: 0; + position: relative; + + .click-to-copy { + display: block; + text-align: right; + } + + &:hover button.td-click-to-copy, + &:focus-within button.td-click-to-copy { + opacity: 1; + } pre { margin: 0; padding: 1rem; + border-radius: inherit; + + // Custom modern scrollbar for code blocks + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + &::-webkit-scrollbar-track { + background: transparent; + } + &::-webkit-scrollbar-thumb { + background-color: rgba(128, 128, 128, 0.4); + border-radius: 4px; + } + &::-webkit-scrollbar-thumb:hover { + background-color: rgba(128, 128, 128, 0.6); + } + scrollbar-width: thin; + scrollbar-color: rgba(128, 128, 128, 0.4) transparent; + + // Default click-to-copy button + button.td-click-to-copy { + position: absolute; + color: var(--bs-tertiary-color); + border-width: 0; + background-color: transparent; + background-image: none; + --bs-btn-box-shadow: 0; + padding: var(--bs-btn-padding-y) calc(var(--bs-btn-padding-x) / 2); + right: 4px; + top: 2px; + opacity: 0; + transition: opacity 0.3s ease-in-out; + + &:hover { + color: var(--bs-secondary-color); + background-color: var(--bs-dark-bg-subtle); + } + + &:active { + color: var(--bs-secondary-color); + background-color: var(--bs-dark-bg-subtle); + transform: translateY(2px); + } + } } } @@ -21,7 +93,7 @@ margin: 0; font-size: 85%; word-break: normal; - background-color: rgba($black, 0.05); + background-color: var(--td-pre-bg); border-radius: $border-radius; br { @@ -33,9 +105,26 @@ // Code blocks pre { word-wrap: normal; - background-color: $gray-100; + background-color: var(--td-pre-bg); padding: $spacer; - + + // Custom modern scrollbar for code blocks + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + &::-webkit-scrollbar-track { + background: transparent; + } + &::-webkit-scrollbar-thumb { + background-color: rgba(128, 128, 128, 0.4); + border-radius: 4px; + } + &::-webkit-scrollbar-thumb:hover { + background-color: rgba(128, 128, 128, 0.6); + } + scrollbar-width: thin; + scrollbar-color: rgba(128, 128, 128, 0.4) transparent; > code { background-color: inherit !important; diff --git a/assets/scss/_colors.scss b/assets/scss/_colors.scss index 3f34aa84e30..37b49399443 100644 --- a/assets/scss/_colors.scss +++ b/assets/scss/_colors.scss @@ -1,21 +1,31 @@ // Add some local palette classes so you can do -bg-warning -text-warning etc. Even -bg-1 if you want to paint by numbers. @mixin palette-variant($color-name, $color-value) { - $text-color: color-yiq($color-value); - $link-color: mix($blue, $text-color, lightness($color-value)); - - $link-hover-color: rgba($link-color, .5) !default; - - .-bg-#{$color-name} { - color: $text-color; - background-color: $color-value; - } - - // Make links in paragraphs stand out more. - @include link-variant(".-bg-#{$color-name} p > a", $link-color, $link-hover-color, false); - - - .-text-#{$color-name} { - color: $color-value; + // Ensure $color-value is a valid Sass color before using color functions + @if type-of($color-value) == 'color' { + $text-color: color-contrast($color-value); + $link-color: mix($blue, $text-color); + + $link-hover-color: rgba($link-color, .5) !default; + + .-bg-#{$color-name} { + color: $text-color; + background-color: $color-value; + + p > a { + color: $link-color; + + &:hover, + &:focus { + color: $link-hover-color; + } + } + } + + .-text-#{$color-name} { + color: $color-value; + } + } @else { + @warn "palette-variant: #{$color-value} is not a color, skipping .-bg-#{$color-name} and .-text-#{$color-name}"; } } @@ -35,7 +45,6 @@ @for $i from 1 through length($td-box-colors) { $value: nth($td-box-colors, $i); $name: $i - 1; - $text-color: color-yiq($value); @include palette-variant($name, $value); } \ No newline at end of file diff --git a/assets/scss/_content.scss b/assets/scss/_content.scss index 3e72e00ebb3..43e0d813dbe 100644 --- a/assets/scss/_content.scss +++ b/assets/scss/_content.scss @@ -4,6 +4,11 @@ .td-content { order: 1; + + // Prevent long links or unbreakable strings from causing horizontal page scroll + overflow-wrap: break-word; + word-wrap: break-word; + word-break: break-word; p, li, td { font-weight: $font-weight-body-text; @@ -37,17 +42,20 @@ > table { @extend .table-striped; - @extend .table-responsive; - @extend .table; + + display: block; + width: 100%; + overflow-x: auto; + word-break: normal; } > blockquote { padding: 0 0 0 1rem; margin-bottom: $spacer; - color: $gray-600; - border-left: 6px solid $secondary; + color: var(--bs-secondary-color); + border-left: 6px solid var(--bs-primary); } > ul li, > ol li { diff --git a/assets/scss/_copy-to-llm.scss b/assets/scss/_copy-to-llm.scss index 5be3d82e659..cdde731b3ee 100644 --- a/assets/scss/_copy-to-llm.scss +++ b/assets/scss/_copy-to-llm.scss @@ -2,6 +2,17 @@ float: right; margin-top: 0.25rem; + .btn-outline-secondary { + --bs-btn-color: var(--bs-body-color); + --bs-btn-border-color: var(--bs-border-color); + --bs-btn-hover-color: var(--bs-body-bg); + --bs-btn-hover-bg: var(--bs-body-color); + --bs-btn-hover-border-color: var(--bs-body-color); + --bs-btn-active-color: var(--bs-body-bg); + --bs-btn-active-bg: var(--bs-body-color); + --bs-btn-active-border-color: var(--bs-body-color); + } + &__btn--success { color: $success !important; } @@ -14,12 +25,12 @@ transform: translateX(-50%); margin-bottom: 0.5rem; padding: 0.5rem 0.75rem; - background: $white; - border: 1px solid $gray-300; + background: var(--bs-tertiary-bg); + border: 1px solid var(--bs-border-color); border-radius: $border-radius; - box-shadow: 0 4px 12px rgba($black, 0.1); + box-shadow: 0 4px 12px rgba(var(--bs-body-color-rgb), 0.1); font-size: 0.875rem; - color: $gray-700; + color: var(--bs-body-color); white-space: nowrap; z-index: 11; diff --git a/assets/scss/_nav.scss b/assets/scss/_nav.scss index 59b54d6ebc9..a13de6edf79 100644 --- a/assets/scss/_nav.scss +++ b/assets/scss/_nav.scss @@ -76,10 +76,13 @@ } .dropdown { - min-width: 100px; + min-width: auto; + @include media-breakpoint-up(lg) { + min-width: 100px; + } } - @include media-breakpoint-down(md) { + @include media-breakpoint-down(lg) { padding-right: 0.5rem; padding-left: 0.75rem; @@ -103,6 +106,20 @@ } } } + + // Theme toggler styles + .td-navbar__light-dark-menu { + .bi { + width: 1em; + height: 1em; + vertical-align: -0.125em; + fill: currentcolor; + } + + .dropdown-menu { + min-width: 8rem; + } + } } // Icons @@ -195,7 +212,7 @@ nav.foldable-nav { padding-left: 0.4em; padding-right: 0.4em; font-size: 1em; - color: $gray-900; + color: var(--bs-body-color); transition: all 0.5s; &:hover { transform: rotate(90deg); @@ -203,7 +220,7 @@ nav.foldable-nav { } .ul-1 .with-child > input:checked ~ label:before { - color: $primary; + color: var(--bs-primary); transform: rotate(90deg); transition: transform 0.5s; } @@ -216,13 +233,13 @@ nav.foldable-nav { @media (hover: hover) and (pointer: fine) { nav.foldable-nav { .ul-1 .with-child > label:hover:before { - color: $primary; + color: var(--bs-primary); transform: rotate(30deg); transition: transform 0.5s; } .ul-1 .with-child > input:checked ~ label:hover:before { - color: $primary; + color: var(--bs-primary); transform: rotate(60deg) !important; transition: transform 0.5s; } diff --git a/assets/scss/_sidebar-toc.scss b/assets/scss/_sidebar-toc.scss index 618604c04cf..c1001ccba92 100644 --- a/assets/scss/_sidebar-toc.scss +++ b/assets/scss/_sidebar-toc.scss @@ -2,7 +2,7 @@ // Right side toc // .td-sidebar-toc { - border-left: 1px solid $border-color; + border-left: 1px solid var(--bs-border-color); @supports (position: sticky) { position: sticky; @@ -47,10 +47,10 @@ > ul > li > ul > li > a {} a { - color: $gray-600; + color: var(--bs-secondary-color); &:hover { - color: $blue; + color: var(--bs-link-color); text-decoration: none; } } diff --git a/assets/scss/_sidebar-tree.scss b/assets/scss/_sidebar-tree.scss index 1c82ad68c08..52c5addb659 100644 --- a/assets/scss/_sidebar-tree.scss +++ b/assets/scss/_sidebar-tree.scss @@ -49,7 +49,7 @@ } a { - color: $gray-900; + color: var(--bs-secondary-color); } } @@ -58,7 +58,7 @@ padding-bottom: 0.375rem; &__page { - color: $gray-700; + color: var(--bs-body-color); font-weight: $font-weight-light; font-size: $font-14; } @@ -66,19 +66,19 @@ a { &:hover { - color: $blue; + color: var(--bs-link-color); text-decoration: none; } &.active { - color: blue; + color: var(--bs-link-color); font-weight: $font-weight-bold; } } .dropdown { a { - color: $gray-700; + color: var(--bs-body-color); } .nav-link { @@ -103,7 +103,7 @@ .td-sidebar-link.tree-root{ font-weight: $font-weight-bold; color: $td-sidebar-tree-root-color; - border-bottom: 1px $td-sidebar-tree-root-color solid; + border-bottom: 1px solid var(--bs-tertiary-color); margin-bottom: 1rem; } } @@ -111,9 +111,9 @@ .td-sidebar { @include media-breakpoint-up(md) { padding-top: 4rem; - background-color: $td-sidebar-bg-color; + background-color: var(--bs-body-bg); padding-right: 1rem; - border-right: 1px solid $td-sidebar-border-color; + border-right: 1px solid var(--bs-border-color); } @@ -121,7 +121,7 @@ &__toggle { line-height: 1; - color: $gray-900; + color: var(--bs-body-color); margin: 1rem; } @@ -157,7 +157,7 @@ #content-desktop {display: block;} #content-mobile {display: none;} - @include media-breakpoint-down(md) { + @include media-breakpoint-down(lg) { #content-desktop {display: none;} #content-mobile {display: block;} diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index d9f6d793e1d..54a188a2d83 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -6,6 +6,30 @@ assets/scss/_styles_project.scss */ +// Global scrollbar styles for a consistent, modern look across the whole site +* { + scrollbar-width: thin; + scrollbar-color: rgba(128, 128, 128, 0.4) transparent; +} + +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background-color: rgba(128, 128, 128, 0.4); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background-color: rgba(128, 128, 128, 0.6); +} + .o-banner { @include media-breakpoint-up(md) { width: 100%; @@ -15,13 +39,18 @@ assets/scss/_styles_project.scss top: 4rem; } - background: $gray-100; + background: var(--bs-tertiary-bg); + color: var(--bs-body-color); text-align: center; & p { padding: 0.5rem; margin-bottom: initial; } + + a { + color: var(--bs-primary) !important; + } } pre.mermaid { @@ -57,3 +86,48 @@ pre.mermaid { top: 1rem; z-index: 10; } + +// Dark mode color adjustments +@if $enable-dark-mode { + @include color-mode(dark) { + // Improve box shadow contrast in dark mode + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.8); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.8); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.8); + + // Adjust primary/secondary for better contrast in dark mode + --bs-primary: #{tint-color($primary, 20%)}; + + // Fix hardcoded white/light backgrounds in sections + .td-box--white, + .td-box--light { + background-color: var(--bs-body-bg) !important; + color: var(--bs-body-color) !important; + } + + // Ensure footer custom icon images are visible + .bg-dark { + img[src$=".svg"] { + filter: brightness(0) invert(1); + } + } + + // Cloudwego link colors for dark mode + a.cloudwego-link { + color: #6db8f0; + } + + a.cloudwego-link:hover { + color: #a8d4f7; + } + } +} + +@include media-breakpoint-down(md) { + // targeted fix for sidebar margins that often cause overflow on mobile + .td-sidebar-nav, + .td-sidebar__search { + margin-left: 0 !important; + margin-right: 0 !important; + } +} diff --git a/assets/scss/_variables.scss b/assets/scss/_variables.scss index db9e8aac66a..e82b112c139 100644 --- a/assets/scss/_variables.scss +++ b/assets/scss/_variables.scss @@ -6,10 +6,11 @@ See https://github.com/twbs/bootstrap/pull/23260 @fa-font-path : "../webfonts"; -// Bootstrap flags. For more, see https://getbootstrap.com/docs/4.0/getting-started/theming/ +// Bootstrap flags. For more, see https://getbootstrap.com/docs/5.3/customize/options/ $enable-gradients: true !default; $enable-rounded: true !default; $enable-shadows: true !default; +$enable-dark-mode: true !default; // Theme flags. @@ -100,14 +101,15 @@ $h6-font-size: $font-size-base !default; // Display styles -$display1-weight: $font-weight-bold !default; -$display2-weight: $font-weight-bold !default; -$display3-weight: $font-weight-bold !default; -$display4-weight: $font-weight-bold !default; -$display1-size: 3rem !default; -$display2-size: 2.5rem !default; -$display3-size: 2rem !default; -$display4-size: 1.75rem !default; +$display-font-weight: $font-weight-bold !default; +$display-font-sizes: ( + 1: 3rem, + 2: 2.5rem, + 3: 2rem, + 4: 1.75rem, + 5: 1.5rem, + 6: 1.25rem +) !default; // Space @@ -129,5 +131,17 @@ $navbar-dark-hover-color: rgba($white, 0.5) !default; $navbar-dark-active-color: $white !default; $navbar-dark-disabled-color: rgba($white, 0.25) !default; -// The yiq lightness value that determines when the lightness of color changes from "dark" to "light". -$yiq-contrasted-threshold: 200 !default; +// The minimum contrast ratio for text readability. +$min-contrast-ratio: 3 !default; + +// Code background variables +:root, +[data-bs-theme='light'] { + --td-pre-bg: #f8f9fa; + --td-code-fg: #212529; +} + +[data-bs-theme='dark'] { + --td-pre-bg: #1d1f21; + --td-code-fg: #c5c8c6; +} diff --git a/assets/scss/home.scss b/assets/scss/home.scss index 2485ee3ca52..7be70d8c06b 100644 --- a/assets/scss/home.scss +++ b/assets/scss/home.scss @@ -79,8 +79,8 @@ margin: 0 8px; padding: 16px; max-width: calc(25% - 16px); - box-shadow: 0 0 4px #eee; - border: 1px solid #f0f0f0; + box-shadow: 0 0 4px var(--bs-tertiary-bg); + border: 1px solid var(--bs-border-color); } .home-project-item a { @@ -90,7 +90,7 @@ .home-project-item h2 { font-weight: 300; - color: #3c8fa8; + color: var(--bs-primary); font-size: 25px; white-space: nowrap; overflow: hidden; @@ -100,7 +100,7 @@ .home-project-item .fa { margin-right: 5px; - color: #3c8fa8; + color: var(--bs-primary); } .home-project-item p { @@ -109,16 +109,16 @@ text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; margin-bottom: unset; - color: #000; + color: var(--bs-body-color); } .home-project-item:hover { transition: 0.4s all; - background-color: #63b0c7; + background-color: var(--bs-primary); border-radius: 3px; color: #fff; - box-shadow: 0 0 4px #80c6db; - border: 1px solid #63b0c7; + box-shadow: 0 0 4px var(--bs-primary); + border: 1px solid var(--bs-primary); } .home-project-item:hover p, @@ -152,7 +152,7 @@ .home-used-by-wrapper { padding: 0 15vw !important; display: flex; - flex-direction: column; + flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center; @@ -165,7 +165,6 @@ .home-used-by-item { margin: 8px 16px; - border: 1px solid #fff; text-align: center; padding: 16px; } @@ -177,8 +176,8 @@ .home-used-by-item:hover { border-radius: 3px; - box-shadow: 0 0 4px #f3f4f5; - border: 1px solid #e0e2e6; + box-shadow: 0 0 4px var(--bs-tertiary-bg); + border: 1px solid var(--bs-border-color); } // footer section @@ -197,7 +196,7 @@ font-size: 1.5rem; padding-bottom: 16px; margin-bottom: 16px; - border-bottom: 2px solid #acacac; + border-bottom: 2px solid var(--bs-border-color); } .home-footer-links-item ul { diff --git a/assets/scss/main.scss b/assets/scss/main.scss index ea4a033c3d8..133ba5d754b 100644 --- a/assets/scss/main.scss +++ b/assets/scss/main.scss @@ -46,7 +46,7 @@ html { footer { min-height: 150px; - @include media-breakpoint-down(md) { + @include media-breakpoint-down(lg) { min-height: 200px; } } diff --git a/assets/scss/markdown.scss b/assets/scss/markdown.scss index f903a83f79f..045700dc491 100644 --- a/assets/scss/markdown.scss +++ b/assets/scss/markdown.scss @@ -22,8 +22,8 @@ .markdown div[class*=language-] { margin-bottom: 16px; - background: rgba(225,230,238,.3); - border: 1px solid #979797; + background: var(--td-pre-bg); + border: 1px solid var(--bs-border-color); border-radius: 0 } @@ -36,7 +36,8 @@ .markdown div[class*=language-] pre { padding: 1em; margin: .5em 0; - overflow: auto + overflow: auto; + background: transparent !important; } .markdown pre.mermaid { @@ -46,7 +47,8 @@ } .markdown div[class*=language-] pre code { - color: rgba(0,0,0,.5) + color: inherit; + background: transparent !important; } .markdown h2 { @@ -107,7 +109,7 @@ .markdown ol li,.markdown p,.markdown ul li { font-size: 14px; - color: rgba(0,0,0,.5); + color: var(--bs-secondary-color); line-height: 24px; letter-spacing: .8px } @@ -119,13 +121,13 @@ } .markdown ol li a,.markdown p a,.markdown ul li a { - color: #002fa7; + color: var(--bs-link-color); text-decoration: none; cursor: pointer } .markdown ol li b,.markdown p b,.markdown ul li b { - color: #000 + color: var(--bs-body-color) } .markdown .icon.outbound,.markdown a[href^="#"] { @@ -148,7 +150,7 @@ .markdown table tr { height: 60px; - color: rgba(0,0,0,.5) + color: var(--bs-secondary-color) } @media (max-width:1000px) { @@ -158,8 +160,8 @@ } .markdown table tr th { - background-color: #f2f2f2; - color: #000 + background-color: var(--bs-tertiary-bg); + color: var(--bs-body-color) } .markdown table tr td,.markdown table tr th { @@ -173,7 +175,7 @@ } .markdown table tr:nth-child(2n) td,.markdown table tr:nth-child(2n) th { - background-color: #f2f2f2 + background-color: var(--bs-tertiary-bg) } .markdown ol,.markdown ul { @@ -226,7 +228,7 @@ top:120px; font-size:60px; line-height:60px; - color:rgba(0,0,0,.05) + color: var(--bs-tertiary-color) } .common-banner .banner .outside { position:absolute; @@ -234,7 +236,7 @@ top:156px; font-size:48px; line-height:48px; - color:#000; + color: var(--bs-body-color); font-weight:400 } .common-banner .banner-mobile { diff --git a/assets/scss/safety.scss b/assets/scss/safety.scss index bdaa5146de0..ee76e641a7b 100644 --- a/assets/scss/safety.scss +++ b/assets/scss/safety.scss @@ -9,28 +9,30 @@ border-radius: 0px; } .btn-outline-primary{ - color: #4b4b4b; - border-color: #989898; - } + color: var(--bs-body-color); + border-color: var(--bs-border-color); - .btn-outline-primary:hover{ - color: #fff; - background: #4075d9; - border-color: #4075d9; + &:hover { + color: #fff; + background: var(--bs-primary); + border-color: var(--bs-primary); + } } - .btn-group > .btn:not(:last-child):not(.dropdown-toggle), - .btn-group > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; + .btn-check:checked + .btn-outline-primary, + .btn-outline-primary:active, + .btn-outline-primary.active { + color: #fff !important; + background-color: var(--bs-primary) !important; + border-color: var(--bs-primary) !important; } + } + } - .btn-outline-primary:not(:disabled):not(.disabled):active, - .btn-outline-primary:not(:disabled):not(.disabled).active, - .show > .btn-outline-primary.dropdown-toggle { - color: #fff; - background: #4075d9; - } + [data-bs-theme='dark'] & { + .btn-outline-primary { + color: var(--bs-body-color); + border-color: var(--bs-border-color); } } .custom-table { @@ -50,8 +52,8 @@ .page-item.active .page-link { z-index: 3; color: #fff; - background-color: #4075d9; - border-color: #4075d9; + background-color: var(--bs-primary); + border-color: var(--bs-primary); } } } diff --git a/assets/scss/support/_utilities.scss b/assets/scss/support/_utilities.scss index d99a0d56b53..379cd38e599 100644 --- a/assets/scss/support/_utilities.scss +++ b/assets/scss/support/_utilities.scss @@ -31,7 +31,7 @@ .td-border-top { border: none; - border-top: 1px solid #eee; + border-top: 1px solid var(--bs-border-color); } .td-border-none { diff --git a/config.toml b/config.toml index 2512bc848cb..abe52cb052c 100644 --- a/config.toml +++ b/config.toml @@ -103,6 +103,7 @@ unsafe = true [markup.highlight] # See a complete list of available styles at https://xyproto.github.io/splash/docs/all.html style = "tango" +noClasses = false # Uncomment if you want your chosen highlight style used for code blocks without a specified language # guessSyntax = "true" @@ -158,8 +159,8 @@ algolia_docsearch = true # Enable Lunr.js offline search offlineSearch = false -# Enable syntax highlighting and copy buttons on code blocks with Prism -prism_syntax_highlighting = true +# Disable syntax highlighting and copy buttons on code blocks with Prism (using Chroma instead) +prism_syntax_highlighting = false # Mermaid diagrams configuration [params.mermaid] @@ -181,6 +182,8 @@ sidebar_search_disable = true navbar_logo = "/img/logo.png" # Set to true to disable the About link in the site footer footer_about_disable = true +# Set to true to show the light/dark mode toggle menu in the navbar +showLightDarkModeMenu = true # Adds a H2 section titled "Feedback" to the bottom of each doc. The responses are sent to Google Analytics as events. # This feature depends on [services.googleAnalytics] and will be disabled if "services.googleAnalytics.id" is not set. diff --git a/content/en/_index.html b/content/en/_index.html index f5770d405c3..46131013b09 100644 --- a/content/en/_index.html +++ b/content/en/_index.html @@ -8,11 +8,11 @@

}}"> - Get Started + Get Started - Github + Github