diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml new file mode 100644 index 00000000..7c6b5ab6 --- /dev/null +++ b/.github/workflows/hugo.yml @@ -0,0 +1,83 @@ +# Sample workflow for building and deploying a Hugo site to GitHub Pages +name: Deploy Hugo site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +# Default to bash +defaults: + run: + shell: bash + +jobs: + # Build job + build: + runs-on: ubuntu-latest + env: + HUGO_VERSION: 0.148.2 + steps: + - name: Install Hugo CLI + run: | + wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ + && sudo dpkg -i ${{ runner.temp }}/hugo.deb + - name: Install Dart Sass + run: sudo snap install dart-sass + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + - name: Install Node.js dependencies + run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" + - name: Generate missing WebP images + run: | + sudo apt-get install -y -qq webp + find static/img -type f \( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' \) | while read src; do + webp="${src%.*}.webp" + if [ ! -f "$webp" ]; then + echo "Generating $webp" + cwebp -q 80 "$src" -o "$webp" + fi + done + - name: Build with Hugo + env: + HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache + HUGO_ENVIRONMENT: production + run: | + hugo \ + --minify + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./public + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 9d67e1b7..28e605a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,44 @@ -node_modules -.idea -public -yarn-error.log \ No newline at end of file +# Node modules and dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Hugo build outputs +public/ +resources/_gen/ +.hugo_build.lock + +# Development files +.DS_Store +.vscode/ +.idea/ +*.swp +*.swo + +# Build artifacts +dist/ +build/ + +# Environment files +.env +.env.local +.env.production + +# Image optimization cache +.image-cache/ + +# Logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Backup files +*.backup + +# Lighthouse reports +lighthouse-reports/ diff --git a/.gitmodules b/.gitmodules index b2b406bf..d7056c87 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ -[submodule "themes/meme"] - path = themes/meme - url = https://github.com/reuixiy/hugo-theme-meme.git [submodule "themes/matomo"] path = themes/matomo url = https://github.com/holehan/hugo-components-matomo.git [submodule "themes/hugo-notice"] path = themes/hugo-notice - url = https://github.com/martignoni/hugo-notice.git \ No newline at end of file + url = https://github.com/martignoni/hugo-notice.git diff --git a/.hugo_build.lock b/.hugo_build.lock new file mode 100644 index 00000000..e69de29b diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..67e7b880 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM hugomods/hugo:0.148.2 + +WORKDIR /src + +COPY . . + +# Install submodules +RUN git config --global --add safe.directory /workspace +RUN git submodule update --init --recursive + +RUN hugo --gc --minify \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..1209f6cd --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +.PHONY: help setup dev build serve clean deploy lint format + +# Default target +help: + @echo "Available commands:" + @echo " setup - Initial project setup (submodules + dependencies)" + @echo " dev - Start development mode (webpack watch + hugo server)" + @echo " build - Build production site" + @echo " serve - Start Hugo development server" + @echo " clean - Clean build artifacts" + @echo " lint - Lint JavaScript/Vue files" + @echo " format - Format code with Prettier" + +# Initial setup +setup: + git config --global --add safe.directory /workspace || true + git submodule update --init --recursive + yarn install + +# Development mode +dev: + @echo "Starting development mode..." + @echo "Run 'make serve' in another terminal for Hugo server" + yarn dev + +# Build production site +build: + yarn install + yarn build:production + hugo --gc --minify + yarn optimize + +# Start Hugo server +serve: + hugo server --disableFastRender --navigateToChanged + +# Clean build artifacts +clean: + rm -rf public/* + rm -f static/js/main.js + rm -f .hugo_build.lock + +# Lint code +lint: + yarn lint + +# Format code +format: + yarn format + +# Docker development +docker-dev: + docker-compose up -d --build + +# Docker stop +docker-stop: + docker-compose down diff --git a/README.md b/README.md index 95d004c5..4ae1191b 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,20 @@ hugo // Allow to use main.js in Hugo. Send static/js/main.js in public yarn run optimize // Optimize images of public/img ``` +### Local development + +```sh +git config --global --add safe.directory /workspace +git submodule update --init --recursive +hugo --gc --minify +hugo server +``` +or +```sh +docker-compose up -d --build +``` +and open `http://localhost:1313` + ### How to add filters If you need to add filters, follow the procedure below : @@ -129,11 +143,4 @@ export default { ``` -Follow by using `yarn run build` and your new category should be visible (clear web browser cache) - - - - - - - +Follow by using `yarn run build` and your new category should be visible (clear web browser cache) \ No newline at end of file diff --git a/assets/icons/brand-farcaster.svg b/assets/icons/brand-farcaster.svg new file mode 100644 index 00000000..1da05f4d --- /dev/null +++ b/assets/icons/brand-farcaster.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/brand-telegram.svg b/assets/icons/brand-telegram.svg new file mode 100644 index 00000000..610ad0ab --- /dev/null +++ b/assets/icons/brand-telegram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/brand-x.svg b/assets/icons/brand-x.svg new file mode 100644 index 00000000..0b7db395 --- /dev/null +++ b/assets/icons/brand-x.svg @@ -0,0 +1,32 @@ + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/brand-youtube.svg b/assets/icons/brand-youtube.svg new file mode 100644 index 00000000..58851c57 --- /dev/null +++ b/assets/icons/brand-youtube.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/assets/icons/gauge.svg b/assets/icons/gauge.svg new file mode 100644 index 00000000..fc10bb52 --- /dev/null +++ b/assets/icons/gauge.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/airdrop.svg b/assets/icons/glossary/airdrop.svg new file mode 100644 index 00000000..71323195 --- /dev/null +++ b/assets/icons/glossary/airdrop.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/amm.svg b/assets/icons/glossary/amm.svg new file mode 100644 index 00000000..32fd2608 --- /dev/null +++ b/assets/icons/glossary/amm.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/apr.svg b/assets/icons/glossary/apr.svg new file mode 100644 index 00000000..dc9a0734 --- /dev/null +++ b/assets/icons/glossary/apr.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/apy.svg b/assets/icons/glossary/apy.svg new file mode 100644 index 00000000..180c1007 --- /dev/null +++ b/assets/icons/glossary/apy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/arbitrage.svg b/assets/icons/glossary/arbitrage.svg new file mode 100644 index 00000000..1f6daff2 --- /dev/null +++ b/assets/icons/glossary/arbitrage.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/audit.svg b/assets/icons/glossary/audit.svg new file mode 100644 index 00000000..d00f57fc --- /dev/null +++ b/assets/icons/glossary/audit.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/auto-compounding.svg b/assets/icons/glossary/auto-compounding.svg new file mode 100644 index 00000000..75816fba --- /dev/null +++ b/assets/icons/glossary/auto-compounding.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/basis-trading.svg b/assets/icons/glossary/basis-trading.svg new file mode 100644 index 00000000..4c5ef5cc --- /dev/null +++ b/assets/icons/glossary/basis-trading.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/bid-ask-spread.svg b/assets/icons/glossary/bid-ask-spread.svg new file mode 100644 index 00000000..ef66834f --- /dev/null +++ b/assets/icons/glossary/bid-ask-spread.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/block.svg b/assets/icons/glossary/block.svg new file mode 100644 index 00000000..9c3e8c0e --- /dev/null +++ b/assets/icons/glossary/block.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/borrowing.svg b/assets/icons/glossary/borrowing.svg new file mode 100644 index 00000000..d00b6139 --- /dev/null +++ b/assets/icons/glossary/borrowing.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/glossary/bridge.svg b/assets/icons/glossary/bridge.svg new file mode 100644 index 00000000..37abe0d5 --- /dev/null +++ b/assets/icons/glossary/bridge.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/bug-bounty.svg b/assets/icons/glossary/bug-bounty.svg new file mode 100644 index 00000000..fe0cbf53 --- /dev/null +++ b/assets/icons/glossary/bug-bounty.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/glossary/carry-trade.svg b/assets/icons/glossary/carry-trade.svg new file mode 100644 index 00000000..2c32fbf7 --- /dev/null +++ b/assets/icons/glossary/carry-trade.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/cex.svg b/assets/icons/glossary/cex.svg new file mode 100644 index 00000000..bfafced3 --- /dev/null +++ b/assets/icons/glossary/cex.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/circulating-supply.svg b/assets/icons/glossary/circulating-supply.svg new file mode 100644 index 00000000..82f6acb3 --- /dev/null +++ b/assets/icons/glossary/circulating-supply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/collateral.svg b/assets/icons/glossary/collateral.svg new file mode 100644 index 00000000..522a0402 --- /dev/null +++ b/assets/icons/glossary/collateral.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/concentrated-liquidity.svg b/assets/icons/glossary/concentrated-liquidity.svg new file mode 100644 index 00000000..8d0d7408 --- /dev/null +++ b/assets/icons/glossary/concentrated-liquidity.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/consensus.svg b/assets/icons/glossary/consensus.svg new file mode 100644 index 00000000..624a724b --- /dev/null +++ b/assets/icons/glossary/consensus.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/constant-product.svg b/assets/icons/glossary/constant-product.svg new file mode 100644 index 00000000..43184564 --- /dev/null +++ b/assets/icons/glossary/constant-product.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/dao.svg b/assets/icons/glossary/dao.svg new file mode 100644 index 00000000..da30c2f5 --- /dev/null +++ b/assets/icons/glossary/dao.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/dapp.svg b/assets/icons/glossary/dapp.svg new file mode 100644 index 00000000..ced8532f --- /dev/null +++ b/assets/icons/glossary/dapp.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/dca.svg b/assets/icons/glossary/dca.svg new file mode 100644 index 00000000..6298c748 --- /dev/null +++ b/assets/icons/glossary/dca.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/glossary/defi.svg b/assets/icons/glossary/defi.svg new file mode 100644 index 00000000..9afd4f49 --- /dev/null +++ b/assets/icons/glossary/defi.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/glossary/delegation.svg b/assets/icons/glossary/delegation.svg new file mode 100644 index 00000000..ea201dc7 --- /dev/null +++ b/assets/icons/glossary/delegation.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/delta-neutral.svg b/assets/icons/glossary/delta-neutral.svg new file mode 100644 index 00000000..b78031d1 --- /dev/null +++ b/assets/icons/glossary/delta-neutral.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/derivatives.svg b/assets/icons/glossary/derivatives.svg new file mode 100644 index 00000000..dcb81711 --- /dev/null +++ b/assets/icons/glossary/derivatives.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/dex-aggregator.svg b/assets/icons/glossary/dex-aggregator.svg new file mode 100644 index 00000000..970c63be --- /dev/null +++ b/assets/icons/glossary/dex-aggregator.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/dex.svg b/assets/icons/glossary/dex.svg new file mode 100644 index 00000000..1dcc47d0 --- /dev/null +++ b/assets/icons/glossary/dex.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/erc-20.svg b/assets/icons/glossary/erc-20.svg new file mode 100644 index 00000000..f5c68dea --- /dev/null +++ b/assets/icons/glossary/erc-20.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/erc-721.svg b/assets/icons/glossary/erc-721.svg new file mode 100644 index 00000000..7f1efb91 --- /dev/null +++ b/assets/icons/glossary/erc-721.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/ethereum.svg b/assets/icons/glossary/ethereum.svg new file mode 100644 index 00000000..301a3618 --- /dev/null +++ b/assets/icons/glossary/ethereum.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/exit-scam.svg b/assets/icons/glossary/exit-scam.svg new file mode 100644 index 00000000..cb210828 --- /dev/null +++ b/assets/icons/glossary/exit-scam.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/glossary/exploit.svg b/assets/icons/glossary/exploit.svg new file mode 100644 index 00000000..6702ff4c --- /dev/null +++ b/assets/icons/glossary/exploit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/flash-loan-attack.svg b/assets/icons/glossary/flash-loan-attack.svg new file mode 100644 index 00000000..8b38a88a --- /dev/null +++ b/assets/icons/glossary/flash-loan-attack.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/glossary/flash-loan.svg b/assets/icons/glossary/flash-loan.svg new file mode 100644 index 00000000..bf94ee4a --- /dev/null +++ b/assets/icons/glossary/flash-loan.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/front-running.svg b/assets/icons/glossary/front-running.svg new file mode 100644 index 00000000..1df1cf8d --- /dev/null +++ b/assets/icons/glossary/front-running.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/funding-rate.svg b/assets/icons/glossary/funding-rate.svg new file mode 100644 index 00000000..46fd9e9e --- /dev/null +++ b/assets/icons/glossary/funding-rate.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/glossary/gas.svg b/assets/icons/glossary/gas.svg new file mode 100644 index 00000000..622c5d27 --- /dev/null +++ b/assets/icons/glossary/gas.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/governance-attack.svg b/assets/icons/glossary/governance-attack.svg new file mode 100644 index 00000000..8b2481eb --- /dev/null +++ b/assets/icons/glossary/governance-attack.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/governance-token.svg b/assets/icons/glossary/governance-token.svg new file mode 100644 index 00000000..9334eccd --- /dev/null +++ b/assets/icons/glossary/governance-token.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/grid-trading.svg b/assets/icons/glossary/grid-trading.svg new file mode 100644 index 00000000..96461fed --- /dev/null +++ b/assets/icons/glossary/grid-trading.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/honeypot.svg b/assets/icons/glossary/honeypot.svg new file mode 100644 index 00000000..14d1064c --- /dev/null +++ b/assets/icons/glossary/honeypot.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/impermanent-loss.svg b/assets/icons/glossary/impermanent-loss.svg new file mode 100644 index 00000000..f0adf738 --- /dev/null +++ b/assets/icons/glossary/impermanent-loss.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/kyc.svg b/assets/icons/glossary/kyc.svg new file mode 100644 index 00000000..8b2a3326 --- /dev/null +++ b/assets/icons/glossary/kyc.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/layer-2.svg b/assets/icons/glossary/layer-2.svg new file mode 100644 index 00000000..fb292de0 --- /dev/null +++ b/assets/icons/glossary/layer-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/lending.svg b/assets/icons/glossary/lending.svg new file mode 100644 index 00000000..ff01b69c --- /dev/null +++ b/assets/icons/glossary/lending.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/leverage.svg b/assets/icons/glossary/leverage.svg new file mode 100644 index 00000000..4db8b7f1 --- /dev/null +++ b/assets/icons/glossary/leverage.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/limit-order.svg b/assets/icons/glossary/limit-order.svg new file mode 100644 index 00000000..2602ede4 --- /dev/null +++ b/assets/icons/glossary/limit-order.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/liquidation.svg b/assets/icons/glossary/liquidation.svg new file mode 100644 index 00000000..a60d64e5 --- /dev/null +++ b/assets/icons/glossary/liquidation.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/liquidity-aggregation.svg b/assets/icons/glossary/liquidity-aggregation.svg new file mode 100644 index 00000000..78e1c366 --- /dev/null +++ b/assets/icons/glossary/liquidity-aggregation.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/glossary/liquidity-mining.svg b/assets/icons/glossary/liquidity-mining.svg new file mode 100644 index 00000000..5f1bc1db --- /dev/null +++ b/assets/icons/glossary/liquidity-mining.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/liquidity-pool.svg b/assets/icons/glossary/liquidity-pool.svg new file mode 100644 index 00000000..7e0027b5 --- /dev/null +++ b/assets/icons/glossary/liquidity-pool.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/liquidity-provider.svg b/assets/icons/glossary/liquidity-provider.svg new file mode 100644 index 00000000..b8102aeb --- /dev/null +++ b/assets/icons/glossary/liquidity-provider.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/lp-token.svg b/assets/icons/glossary/lp-token.svg new file mode 100644 index 00000000..e37a0d1b --- /dev/null +++ b/assets/icons/glossary/lp-token.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/ltv.svg b/assets/icons/glossary/ltv.svg new file mode 100644 index 00000000..cc549b00 --- /dev/null +++ b/assets/icons/glossary/ltv.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/margin-trading.svg b/assets/icons/glossary/margin-trading.svg new file mode 100644 index 00000000..f11c458c --- /dev/null +++ b/assets/icons/glossary/margin-trading.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/market-cap.svg b/assets/icons/glossary/market-cap.svg new file mode 100644 index 00000000..4c877b4a --- /dev/null +++ b/assets/icons/glossary/market-cap.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/market-depth.svg b/assets/icons/glossary/market-depth.svg new file mode 100644 index 00000000..028aa4a5 --- /dev/null +++ b/assets/icons/glossary/market-depth.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/market-order.svg b/assets/icons/glossary/market-order.svg new file mode 100644 index 00000000..e8572226 --- /dev/null +++ b/assets/icons/glossary/market-order.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/max-supply.svg b/assets/icons/glossary/max-supply.svg new file mode 100644 index 00000000..4a1bce64 --- /dev/null +++ b/assets/icons/glossary/max-supply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/mempool.svg b/assets/icons/glossary/mempool.svg new file mode 100644 index 00000000..7c72504e --- /dev/null +++ b/assets/icons/glossary/mempool.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/glossary/metamask.svg b/assets/icons/glossary/metamask.svg new file mode 100644 index 00000000..00290fb5 --- /dev/null +++ b/assets/icons/glossary/metamask.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/mev.svg b/assets/icons/glossary/mev.svg new file mode 100644 index 00000000..8367e8c9 --- /dev/null +++ b/assets/icons/glossary/mev.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/glossary/multisig.svg b/assets/icons/glossary/multisig.svg new file mode 100644 index 00000000..b52e72f2 --- /dev/null +++ b/assets/icons/glossary/multisig.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/glossary/nft.svg b/assets/icons/glossary/nft.svg new file mode 100644 index 00000000..81ead1de --- /dev/null +++ b/assets/icons/glossary/nft.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/options.svg b/assets/icons/glossary/options.svg new file mode 100644 index 00000000..c09c532c --- /dev/null +++ b/assets/icons/glossary/options.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/oracle.svg b/assets/icons/glossary/oracle.svg new file mode 100644 index 00000000..8146f2e1 --- /dev/null +++ b/assets/icons/glossary/oracle.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/glossary/order-book.svg b/assets/icons/glossary/order-book.svg new file mode 100644 index 00000000..25c07733 --- /dev/null +++ b/assets/icons/glossary/order-book.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/glossary/orderbook.svg b/assets/icons/glossary/orderbook.svg new file mode 100644 index 00000000..2adca5cc --- /dev/null +++ b/assets/icons/glossary/orderbook.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/perpetuals.svg b/assets/icons/glossary/perpetuals.svg new file mode 100644 index 00000000..2f762ccb --- /dev/null +++ b/assets/icons/glossary/perpetuals.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/glossary/phishing.svg b/assets/icons/glossary/phishing.svg new file mode 100644 index 00000000..8e9a348e --- /dev/null +++ b/assets/icons/glossary/phishing.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/price-impact.svg b/assets/icons/glossary/price-impact.svg new file mode 100644 index 00000000..bdaa6a3a --- /dev/null +++ b/assets/icons/glossary/price-impact.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/price-oracle.svg b/assets/icons/glossary/price-oracle.svg new file mode 100644 index 00000000..5c090c38 --- /dev/null +++ b/assets/icons/glossary/price-oracle.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/glossary/private-key.svg b/assets/icons/glossary/private-key.svg new file mode 100644 index 00000000..076338fa --- /dev/null +++ b/assets/icons/glossary/private-key.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/proposal.svg b/assets/icons/glossary/proposal.svg new file mode 100644 index 00000000..c99a759f --- /dev/null +++ b/assets/icons/glossary/proposal.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/quorum.svg b/assets/icons/glossary/quorum.svg new file mode 100644 index 00000000..4a784a08 --- /dev/null +++ b/assets/icons/glossary/quorum.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/rebase-token.svg b/assets/icons/glossary/rebase-token.svg new file mode 100644 index 00000000..4c458546 --- /dev/null +++ b/assets/icons/glossary/rebase-token.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/routing.svg b/assets/icons/glossary/routing.svg new file mode 100644 index 00000000..781cab88 --- /dev/null +++ b/assets/icons/glossary/routing.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/glossary/rug-pull.svg b/assets/icons/glossary/rug-pull.svg new file mode 100644 index 00000000..8e3a1d58 --- /dev/null +++ b/assets/icons/glossary/rug-pull.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/glossary/sandwich-attack.svg b/assets/icons/glossary/sandwich-attack.svg new file mode 100644 index 00000000..278d1e8c --- /dev/null +++ b/assets/icons/glossary/sandwich-attack.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/seed-phrase.svg b/assets/icons/glossary/seed-phrase.svg new file mode 100644 index 00000000..be3d5e4f --- /dev/null +++ b/assets/icons/glossary/seed-phrase.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/glossary/slashing.svg b/assets/icons/glossary/slashing.svg new file mode 100644 index 00000000..f2282eaa --- /dev/null +++ b/assets/icons/glossary/slashing.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/slippage.svg b/assets/icons/glossary/slippage.svg new file mode 100644 index 00000000..f51d11f5 --- /dev/null +++ b/assets/icons/glossary/slippage.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/smart-contract.svg b/assets/icons/glossary/smart-contract.svg new file mode 100644 index 00000000..1f8df23d --- /dev/null +++ b/assets/icons/glossary/smart-contract.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/glossary/snapshot.svg b/assets/icons/glossary/snapshot.svg new file mode 100644 index 00000000..33b96e13 --- /dev/null +++ b/assets/icons/glossary/snapshot.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/stablecoin.svg b/assets/icons/glossary/stablecoin.svg new file mode 100644 index 00000000..b6874487 --- /dev/null +++ b/assets/icons/glossary/stablecoin.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/staking.svg b/assets/icons/glossary/staking.svg new file mode 100644 index 00000000..7b165827 --- /dev/null +++ b/assets/icons/glossary/staking.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/stop-loss.svg b/assets/icons/glossary/stop-loss.svg new file mode 100644 index 00000000..60271206 --- /dev/null +++ b/assets/icons/glossary/stop-loss.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/swap.svg b/assets/icons/glossary/swap.svg new file mode 100644 index 00000000..691c0f44 --- /dev/null +++ b/assets/icons/glossary/swap.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/synthetic-asset.svg b/assets/icons/glossary/synthetic-asset.svg new file mode 100644 index 00000000..4ef40178 --- /dev/null +++ b/assets/icons/glossary/synthetic-asset.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/take-profit.svg b/assets/icons/glossary/take-profit.svg new file mode 100644 index 00000000..4658c7e2 --- /dev/null +++ b/assets/icons/glossary/take-profit.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/timelock.svg b/assets/icons/glossary/timelock.svg new file mode 100644 index 00000000..a71f4062 --- /dev/null +++ b/assets/icons/glossary/timelock.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/token-burn.svg b/assets/icons/glossary/token-burn.svg new file mode 100644 index 00000000..5f9b02a1 --- /dev/null +++ b/assets/icons/glossary/token-burn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/tokenomics.svg b/assets/icons/glossary/tokenomics.svg new file mode 100644 index 00000000..f6c763f4 --- /dev/null +++ b/assets/icons/glossary/tokenomics.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/total-supply.svg b/assets/icons/glossary/total-supply.svg new file mode 100644 index 00000000..2e4b14bf --- /dev/null +++ b/assets/icons/glossary/total-supply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/treasury.svg b/assets/icons/glossary/treasury.svg new file mode 100644 index 00000000..57572b47 --- /dev/null +++ b/assets/icons/glossary/treasury.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/tvl.svg b/assets/icons/glossary/tvl.svg new file mode 100644 index 00000000..7706b239 --- /dev/null +++ b/assets/icons/glossary/tvl.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/twap.svg b/assets/icons/glossary/twap.svg new file mode 100644 index 00000000..b555cc50 --- /dev/null +++ b/assets/icons/glossary/twap.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/utility-token.svg b/assets/icons/glossary/utility-token.svg new file mode 100644 index 00000000..01022a14 --- /dev/null +++ b/assets/icons/glossary/utility-token.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/validator.svg b/assets/icons/glossary/validator.svg new file mode 100644 index 00000000..2b2501fd --- /dev/null +++ b/assets/icons/glossary/validator.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/glossary/vault.svg b/assets/icons/glossary/vault.svg new file mode 100644 index 00000000..7f377795 --- /dev/null +++ b/assets/icons/glossary/vault.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/icons/glossary/vesting.svg b/assets/icons/glossary/vesting.svg new file mode 100644 index 00000000..2c5a60b1 --- /dev/null +++ b/assets/icons/glossary/vesting.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/volume.svg b/assets/icons/glossary/volume.svg new file mode 100644 index 00000000..6b5e7a9a --- /dev/null +++ b/assets/icons/glossary/volume.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/vote-escrow.svg b/assets/icons/glossary/vote-escrow.svg new file mode 100644 index 00000000..c5df1851 --- /dev/null +++ b/assets/icons/glossary/vote-escrow.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/glossary/wallet.svg b/assets/icons/glossary/wallet.svg new file mode 100644 index 00000000..ee814363 --- /dev/null +++ b/assets/icons/glossary/wallet.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/white-hat.svg b/assets/icons/glossary/white-hat.svg new file mode 100644 index 00000000..2b297e82 --- /dev/null +++ b/assets/icons/glossary/white-hat.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/whitelist.svg b/assets/icons/glossary/whitelist.svg new file mode 100644 index 00000000..1b6a13df --- /dev/null +++ b/assets/icons/glossary/whitelist.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/glossary/wrapped-token.svg b/assets/icons/glossary/wrapped-token.svg new file mode 100644 index 00000000..6bb736e4 --- /dev/null +++ b/assets/icons/glossary/wrapped-token.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/glossary/yield-farming.svg b/assets/icons/glossary/yield-farming.svg new file mode 100644 index 00000000..513d643c --- /dev/null +++ b/assets/icons/glossary/yield-farming.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/js/glossary-categories.js b/assets/js/glossary-categories.js new file mode 100644 index 00000000..6907f399 --- /dev/null +++ b/assets/js/glossary-categories.js @@ -0,0 +1,118 @@ +(function() { + function getCategoryColor(categoryId) { + var colors = { + 'yields': '#10B981', + 'rendements': '#10B981', + 'risks': '#EF4444', + 'risques': '#EF4444', + 'strategies': '#8B5CF6', + 'stratégies': '#8B5CF6', + 'protocols': '#3B82F6', + 'protocoles': '#3B82F6', + 'tokens': '#F59E0B', + 'technical': '#6B7280', + 'technique': '#6B7280', + 'trading': '#06B6D4', + 'governance': '#EC4899', + 'gouvernance': '#EC4899', + 'defi-protocols': '#3B82F6', + 'trading-amms': '#06B6D4', + 'governance-daos': '#EC4899', + 'technical-concepts': '#6366F1', + 'lending-borrowing': '#059669', + 'stablecoins': '#F97316', + 'yield-farming': '#10B981' + }; + return colors[categoryId] || '#6B7280'; + } + + function hexToRgb(hex) { + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; + } + + function shouldUseDarkText(hexColor) { + var rgb = hexToRgb(hexColor); + if (!rgb) return false; + var luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255; + return luminance > 0.5; + } + + function getCategoryIcon(categoryId) { + var icons = { + 'yields': '', + 'rendements': '', + 'risks': '', + 'risques': '', + 'strategies': '', + 'stratégies': '', + 'protocols': '', + 'protocoles': '', + 'tokens': '', + 'technical': '', + 'technique': '', + 'trading': '', + 'governance': '', + 'gouvernance': '' + }; + return icons[categoryId] || ''; + } + + function handleCategoryClick(categoryId) { + if (typeof window.handleCategoryFilter === 'function') { + window.handleCategoryFilter(categoryId); + } else { + var glossaryApp = document.getElementById('glossary-app'); + if (glossaryApp) { + glossaryApp.scrollIntoView({ behavior: 'smooth' }); + window.pendingCategoryFilter = categoryId; + } + } + } + + document.addEventListener('DOMContentLoaded', function() { + var categoryCards = document.querySelectorAll('.category-card[data-category-id]'); + categoryCards.forEach(function(card) { + var categoryId = card.getAttribute('data-category-id'); + var color = getCategoryColor(categoryId); + var icon = getCategoryIcon(categoryId); + var countSpan = card.querySelector('.category-count'); + var iconElement = card.querySelector('.category-icon'); + + card.style.borderLeftColor = color; + + if (countSpan) { + countSpan.style.backgroundColor = color; + countSpan.style.color = shouldUseDarkText(color) ? '#1a1a1a' : '#ffffff'; + } + + var rgb = hexToRgb(color); + if (rgb) { + card.style.setProperty('--category-color-rgb', rgb.r + ', ' + rgb.g + ', ' + rgb.b); + } + + card.style.setProperty('--category-color', color); + + if (iconElement) { + iconElement.innerHTML = icon; + iconElement.style.color = color; + } + + card.addEventListener('click', function(e) { + e.preventDefault(); + handleCategoryClick(categoryId); + }); + + card.addEventListener('keydown', function(e) { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleCategoryClick(categoryId); + } + }); + }); + }); +})(); diff --git a/assets/js/glossary.js b/assets/js/glossary.js new file mode 100644 index 00000000..d42d1808 --- /dev/null +++ b/assets/js/glossary.js @@ -0,0 +1,150 @@ +(function() { + 'use strict'; + + var termsList = document.getElementById('terms-list'); + var noResults = document.getElementById('no-results'); + var searchInput = document.getElementById('glossary-search'); + var pillContainer = document.getElementById('category-pills'); + var resultCount = document.getElementById('result-count'); + var alphabetContainer = document.getElementById('alphabet-container'); + + if (!termsList) return; + + var allCards = termsList.querySelectorAll('.compact-card'); + var letterSections = termsList.querySelectorAll('.letter-section'); + var activeCategory = ''; + var searchQuery = ''; + + function applyFilters() { + var visibleCount = 0; + var query = searchQuery.toLowerCase(); + + allCards.forEach(function(card) { + var matchesCategory = !activeCategory || card.getAttribute('data-category') === activeCategory; + var matchesSearch = true; + + if (query) { + var termId = (card.getAttribute('data-term-id') || '').toLowerCase(); + var termName = (card.querySelector('.compact-term-name') || {}).textContent || ''; + var definition = (card.querySelector('.compact-definition') || {}).textContent || ''; + var fullTerm = (card.querySelector('.compact-full-term') || {}).textContent || ''; + var searchable = (termId + ' ' + termName + ' ' + definition + ' ' + fullTerm).toLowerCase(); + matchesSearch = searchable.indexOf(query) !== -1; + } + + if (matchesCategory && matchesSearch) { + card.style.display = ''; + visibleCount++; + } else { + card.style.display = 'none'; + } + }); + + // Hide empty letter sections + letterSections.forEach(function(section) { + var visibleCards = section.querySelectorAll('.compact-card:not([style*="display: none"])'); + section.style.display = visibleCards.length > 0 ? '' : 'none'; + }); + + updateAlphabetNav(); + + // Update result count + if (resultCount) { + resultCount.textContent = visibleCount; + } + + // Show/hide no results + if (noResults) { + termsList.style.display = visibleCount > 0 ? '' : 'none'; + noResults.style.display = visibleCount > 0 ? 'none' : 'block'; + } + } + + function updateAlphabetNav() { + if (!alphabetContainer) return; + var buttons = alphabetContainer.querySelectorAll('.alphabet-link'); + buttons.forEach(function(btn) { + var letter = btn.getAttribute('data-letter') || btn.textContent.trim(); + var section = document.getElementById('letter-' + letter); + if (section) { + btn.style.display = section.style.display === 'none' ? 'none' : ''; + } + }); + } + + // Search input handler + if (searchInput) { + searchInput.addEventListener('input', function() { + searchQuery = this.value.trim(); + applyFilters(); + }); + } + + // Category pill handlers + if (pillContainer) { + pillContainer.addEventListener('click', function(e) { + var pill = e.target.closest('.category-pill'); + if (!pill) return; + pillContainer.querySelectorAll('.category-pill').forEach(function(p) { + p.classList.remove('active'); + }); + pill.classList.add('active'); + activeCategory = pill.getAttribute('data-category') || ''; + applyFilters(); + }); + } + + // Alphabet nav scroll tracking with IntersectionObserver + if (alphabetContainer && 'IntersectionObserver' in window) { + var observer = new IntersectionObserver(function(entries) { + entries.forEach(function(entry) { + if (entry.isIntersecting) { + var letter = entry.target.id.replace('letter-', ''); + alphabetContainer.querySelectorAll('.alphabet-link').forEach(function(btn) { + var btnLetter = btn.getAttribute('data-letter') || btn.textContent.trim(); + btn.classList.toggle('active', btnLetter === letter); + }); + } + }); + }, { rootMargin: '-20% 0px -70% 0px' }); + + letterSections.forEach(function(section) { + observer.observe(section); + }); + } + + // URL parameter support: ?category=X + var urlParams = new URLSearchParams(window.location.search); + var urlCategory = urlParams.get('category'); + if (urlCategory && pillContainer) { + var targetPill = pillContainer.querySelector('[data-category="' + urlCategory + '"]'); + if (targetPill) { + pillContainer.querySelectorAll('.category-pill').forEach(function(p) { + p.classList.remove('active'); + }); + targetPill.classList.add('active'); + activeCategory = urlCategory; + applyFilters(); + } + } + + // Backward compat: expose globally for category card shortcode + window.handleCategoryFilter = function(categoryId) { + activeCategory = categoryId || ''; + if (pillContainer) { + pillContainer.querySelectorAll('.category-pill').forEach(function(p) { + p.classList.remove('active'); + if ((p.getAttribute('data-category') || '') === activeCategory) { + p.classList.add('active'); + } + }); + } + applyFilters(); + }; + + // Handle pending category filter + if (window.pendingCategoryFilter) { + window.handleCategoryFilter(window.pendingCategoryFilter); + delete window.pendingCategoryFilter; + } +})(); diff --git a/assets/jsconfig.json b/assets/jsconfig.json new file mode 100644 index 00000000..f31b73a8 --- /dev/null +++ b/assets/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": [ + "*", + "../themes/hugo-theme-stack/assets/*" + ] + } + } +} \ No newline at end of file diff --git a/assets/scss/breadcrumbs.scss b/assets/scss/breadcrumbs.scss new file mode 100644 index 00000000..2a4384bf --- /dev/null +++ b/assets/scss/breadcrumbs.scss @@ -0,0 +1,57 @@ +/* Breadcrumb Navigation Styles */ +.breadcrumbs { + margin-bottom: 1.5rem; + font-size: 0.9rem; +} + +.breadcrumb-list { + display: flex; + flex-wrap: wrap; + align-items: center; + list-style: none; + margin: 0; + padding: 0; + color: var(--card-text-color-secondary); +} + +.breadcrumb-item { + display: flex; + align-items: center; + + &:not(:last-child)::after { + content: "›"; + margin: 0 0.5rem; + color: var(--card-text-color-tertiary); + } + + a { + color: var(--accent-color); + text-decoration: none; + + &:hover { + text-decoration: underline; + color: var(--accent-color-darker); + } + } + + &.active { + color: var(--card-text-color-main); + font-weight: 500; + } +} + +/* Responsive breadcrumbs */ +@media (max-width: 768px) { + .breadcrumb-list { + font-size: 0.8rem; + } + + .breadcrumb-item:not(:last-child):not(:first-child) { + display: none; + } + + .breadcrumb-item:nth-last-child(2)::before { + content: "... › "; + color: var(--card-text-color-tertiary); + } +} diff --git a/assets/scss/custom.scss b/assets/scss/custom.scss new file mode 100644 index 00000000..9955597d --- /dev/null +++ b/assets/scss/custom.scss @@ -0,0 +1,233 @@ +/* Custom styles for TokenBrice Blog */ + +/* Improved body text contrast (AAA compliant, 7:1 on white) */ +:root { + --body-text-color: #595959; + --error-color: #e74c3c; + --code-text-color: #546e7a; + --shadow-l3: 0px 14px 28px rgba(0, 0, 0, 0.06), + 0px 4px 10px rgba(0, 0, 0, 0.04), + 0px 0px 1px rgba(0, 0, 0, 0.04); +} + +/* Import existing custom styles */ +@import "glossary-links"; +@import "breadcrumbs"; + +/* Import new difficulty/expertise badges */ +@import "custom/difficulty-badges"; + +/* Import glossary categories card styles */ +@import "custom/glossary-categories"; + +/* PhotoSwipe v5 caption */ +.pswp__custom-caption { + font-size: 14px; + color: #fff; + background: rgba(0, 0, 0, 0.5); + padding: 4px 12px; + border-radius: 4px; + position: absolute; + left: 50%; + bottom: 20px; + transform: translateX(-50%); + max-width: 80%; + text-align: center; + pointer-events: none; + + &:empty { + display: none; + } +} + +/* WCAG AA fix: yellow tag background needs dark text (11.5:1 on #ffb900) */ +.article-list article:nth-child(5n+4) .article-category a { + color: #1a1a1a; +} + +/* Dark mode contrast fixes */ +[data-scheme="dark"] { + --card-text-color-tertiary: rgba(255, 255, 255, 0.65); + --code-text-color: #90a4ae; + --shadow-l1: 0px 4px 8px rgba(0, 0, 0, 0.12), + 0px 0px 2px rgba(0, 0, 0, 0.16), + 0px 0px 1px rgba(0, 0, 0, 0.12); + --shadow-l2: 0px 10px 20px rgba(0, 0, 0, 0.14), + 0px 2px 6px rgba(0, 0, 0, 0.12), + 0px 0px 1px rgba(0, 0, 0, 0.10); + --shadow-l3: 0px 14px 28px rgba(0, 0, 0, 0.18), + 0px 4px 10px rgba(0, 0, 0, 0.14), + 0px 0px 1px rgba(0, 0, 0, 0.10); +} + +/* Typography: heading line-heights and vertical rhythm */ +.article-content { + h1 { line-height: 1.2; margin: 2em 0 0.75em; } + h2 { line-height: 1.25; margin: 1.75em 0 0.6em; } + h3 { line-height: 1.3; margin: 1.5em 0 0.5em; } + h4, h5, h6 { line-height: 1.35; margin: 1.25em 0 0.5em; } + + > h1:first-child, + > h2:first-child, + > h3:first-child { + margin-top: 0.5em; + } +} + +/* Interaction polish: article card hover lift */ +.article-list article { + transition: box-shadow 0.2s ease, transform 0.2s ease; + + &:hover { + transform: translateY(-2px); + } +} + +/* Compact list: subtle background highlight on hover */ +.article-list--compact article > a { + transition: background-color 0.15s ease; + + &:hover { + background-color: var(--card-background-selected); + } +} + +/* Pagination hover/focus states */ +.pagination .page-link { + transition: background-color 0.15s ease, color 0.15s ease; + + &:hover:not(.current):not(.dots) { + background-color: var(--card-background-selected); + color: var(--card-text-color-main); + } + + &:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: -2px; + } +} + +/* Category tag: 0.5s → 0.2s (0.5s feels sluggish) */ +.article-category a, +.article-tags a { + transition: background-color 0.2s ease; +} + +/* Social icons: add hover transition */ +.menu-social a { + transition: transform 0.2s ease; + + &:hover { + transform: scale(1.1); + } + + svg { + transition: stroke 0.2s ease; + } +} + +/* Global focus-visible for keyboard navigation */ +a:focus-visible, +button:focus-visible, +select:focus-visible, +[role="button"]:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: 2px; +} + +:focus:not(:focus-visible) { + outline: none; +} + +/* Tag cloud links: add focus-visible */ +.tagCloud-tags a:focus-visible { + box-shadow: var(--shadow-l2); + outline: 2px solid var(--accent-color); + outline-offset: 2px; +} + +/* Dark mode toggle: ensure visible focus ring */ +#dark-mode-toggle:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: 2px; + border-radius: 4px; +} + +/* Spacing: normalize to 8px-based scale */ +.article-content th, +.article-content td { + padding: 8px 12px; +} + +.article-details { + gap: 16px; +} + +.article-time, +.article-translations { + gap: 16px; + + & > div { + gap: 16px; + } +} + +footer.site-footer::before { + margin-bottom: 24px; +} + +/* Inline code: slightly more visible padding */ +.article-content code { + padding: 2px 6px; + font-size: 0.88em; +} + +/* Blockquote: accent color left border */ +.article-content blockquote { + border-inline-start-color: var(--accent-color); +} + +/* Copy code button: keyboard accessible */ +.article-content .copyCodeButton:focus-visible { + opacity: 1; + outline: 2px solid var(--accent-color); + outline-offset: 2px; +} + +/* Selection color matches brand */ +::selection { + background-color: var(--accent-color); + color: var(--accent-color-text); +} + +/* Print: hide non-essential elements */ +@media print { + .left-sidebar, + .right-sidebar, + .pagination, + .article-footer .article-tags, + .related-content, + #toggle-menu { + display: none !important; + } + + .main-container { + max-width: 100% !important; + } + + .main-article { + box-shadow: none !important; + } +} + +/* Reduced motion support */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/assets/scss/custom/_custom.scss b/assets/scss/custom/_custom.scss deleted file mode 100644 index 7ddca361..00000000 --- a/assets/scss/custom/_custom.scss +++ /dev/null @@ -1,5 +0,0 @@ -@media(max-width: 1024px) { - ol.toc, ul.toc { - white-space: normal - } -} \ No newline at end of file diff --git a/assets/scss/custom/difficulty-badges.scss b/assets/scss/custom/difficulty-badges.scss new file mode 100644 index 00000000..988bf8ce --- /dev/null +++ b/assets/scss/custom/difficulty-badges.scss @@ -0,0 +1,292 @@ +/* Expertise Level / Difficulty System */ + +// Difficulty color palette +$difficulty-beginner: #22c55e; +$difficulty-beginner-dark: #16a34a; +$difficulty-intermediate: #fbbf24; +$difficulty-intermediate-dark: #ca8a04; +$difficulty-expert: #f87171; +$difficulty-expert-dark: #dc2626; + +/* CSS dots - cross-platform colored circles */ +.difficulty-dot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + vertical-align: middle; + margin-right: 0.25rem; + + &--beginner { + background-color: $difficulty-beginner; + } + + &--intermediate { + background-color: $difficulty-intermediate; + } + + &--expert { + background-color: $difficulty-expert; + } +} + +[data-scheme="dark"] { + .difficulty-dot { + &--beginner { + background-color: $difficulty-beginner-dark; + } + + &--intermediate { + background-color: $difficulty-intermediate-dark; + } + + &--expert { + background-color: $difficulty-expert-dark; + } + } +} + +/* Difficulty in article footer (for article lists) */ +.article-difficulty-footer { + display: flex; + align-items: center; + gap: 0.25rem; + + .difficulty-text { + font-size: inherit; + font-weight: inherit; + color: var(--card-text-color-secondary); + text-transform: capitalize; + } +} + +/* Inline difficulty badge for archives */ +.article-difficulty-inline { + color: var(--card-text-color-secondary); + font-size: inherit; + + .difficulty-text { + font-size: inherit; + font-weight: inherit; + margin-left: 0.25rem; + text-transform: capitalize; + color: var(--card-text-color-secondary); + } +} + +/* Sidebar expertise widget - matches archives widget styling */ +.widget.expertise { + .widget-expertise--list { + border-radius: var(--card-border-radius); + box-shadow: var(--shadow-l1); + background-color: var(--card-background); + } + + .expertise-level-item { + &:not(:last-of-type) { + border-bottom: 1.5px solid var(--card-separator-color); + } + + a { + display: block; + padding: 18px 25px; + text-decoration: none; + color: inherit; + transition: background-color 0.2s ease; + + &:hover { + background-color: var(--card-background-selected); + text-decoration: none; + } + } + + .expertise-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + + .level-name { + font-size: 1.4rem; + font-weight: bold; + color: var(--card-text-color-main); + } + + .count { + font-size: 1.4rem; + color: var(--card-text-color-tertiary); + font-weight: normal; + } + } + + .expertise-bar { + height: 4px; + background-color: var(--card-separator-color); + border-radius: 2px; + overflow: hidden; + + .bar-fill { + height: 100%; + border-radius: 2px; + transition: width 0.3s ease; + + &.beginner { + background-color: $difficulty-beginner; + } + + &.intermediate { + background-color: $difficulty-intermediate; + } + + &.expert { + background-color: $difficulty-expert; + } + } + } + } +} + +/* Dark mode adjustments for expertise widget */ +[data-scheme="dark"] { + .widget.expertise { + .expertise-bar { + .bar-fill { + &.beginner { + background-color: $difficulty-beginner-dark; + } + + &.intermediate { + background-color: $difficulty-intermediate-dark; + } + + &.expert { + background-color: $difficulty-expert-dark; + } + } + } + } +} + +/* Difficulty filter page nav tabs */ +.difficulty-nav { + display: flex; + gap: 0.5rem; + margin-bottom: 2rem; + flex-wrap: wrap; +} + +.difficulty-nav-item { + display: inline-flex; + align-items: center; + padding: 0.5rem 1rem; + border-radius: 2rem; + font-size: 1.4rem; + font-weight: 500; + text-decoration: none; + color: var(--card-text-color-secondary); + background-color: var(--card-background); + border: 1.5px solid var(--card-separator-color); + transition: all 0.2s ease; + + &:hover { + text-decoration: none; + background-color: var(--card-background-selected); + } + + &.active { + font-weight: 700; + color: var(--card-text-color-main); + } + + &.active.difficulty-nav--beginner { + border-color: $difficulty-beginner; + background-color: rgba($difficulty-beginner, 0.08); + } + + &.active.difficulty-nav--intermediate { + border-color: $difficulty-intermediate; + background-color: rgba($difficulty-intermediate, 0.08); + } + + &.active.difficulty-nav--expert { + border-color: $difficulty-expert; + background-color: rgba($difficulty-expert, 0.08); + } +} + +[data-scheme="dark"] { + .difficulty-nav-item { + &.active.difficulty-nav--beginner { + border-color: $difficulty-beginner-dark; + background-color: rgba($difficulty-beginner, 0.12); + } + + &.active.difficulty-nav--intermediate { + border-color: $difficulty-intermediate-dark; + background-color: rgba($difficulty-intermediate, 0.12); + } + + &.active.difficulty-nav--expert { + border-color: $difficulty-expert-dark; + background-color: rgba($difficulty-expert, 0.12); + } + } +} + +/* Difficulty pages styling */ +.difficulty-page-header { + margin-bottom: 2rem; + + .section-title { + color: var(--body-text-color); + margin-bottom: 1rem; + } + + .difficulty-page-description { + color: var(--card-text-color-secondary); + font-size: 1.75rem; + line-height: 1.5; + font-weight: normal; + + p { + margin: 0; + } + } +} + +/* Dark mode fixes for difficulty pages */ +[data-scheme="dark"] { + .difficulty-page-header { + .section-title { + color: var(--body-text-color); + } + + .difficulty-page-description { + color: var(--body-text-color); + opacity: 0.9; + } + } +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .difficulty-page-header { + .difficulty-page-description { + font-size: 1.5rem; + } + } + + .difficulty-nav-item { + font-size: 1.3rem; + padding: 0.4rem 0.8rem; + } +} + +/* Large screen adjustments */ +@media (min-width: 1024px) { + .difficulty-page-header { + .difficulty-page-description { + font-size: 2rem; + } + } +} diff --git a/assets/scss/custom/glossary-categories.scss b/assets/scss/custom/glossary-categories.scss new file mode 100644 index 00000000..31f6511e --- /dev/null +++ b/assets/scss/custom/glossary-categories.scss @@ -0,0 +1,103 @@ +/* Glossary Categories Overview Cards */ +.glossary-categories-overview { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 1.5rem; + margin: 2rem 0; +} + +.category-card { + background: var(--card-background); + border: 1px solid var(--card-separator-color); + border-left: 4px solid var(--card-separator-color); + border-radius: var(--card-border-radius); + padding: 1.5rem; + transition: all 0.2s ease; + box-shadow: var(--shadow-l1); +} + +.category-card.clickable-category { + cursor: pointer; + user-select: none; +} + +.category-card:hover { + box-shadow: var(--shadow-l2); + transform: translateY(-2px); + border-color: var(--accent-color); +} + +.category-card.clickable-category:hover { + border-left-width: 6px; +} + +.category-card:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: 2px; +} + +.category-header { + display: flex; + align-items: flex-start; + gap: 1rem; + margin-bottom: 1rem; +} + +.category-icon { + width: 48px; + height: 48px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + flex-shrink: 0; + transition: all 0.3s ease; + background: rgba(var(--category-color-rgb, 59, 130, 246), 0.08); +} + +.category-card:hover .category-icon { + background: rgba(var(--category-color-rgb, 59, 130, 246), 0.15); + transform: scale(1.05); +} + +.category-title-group { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.category-name { + margin: 0; + color: var(--card-text-color-main); + font-size: 1.2rem; + font-weight: 700; + line-height: 1.2; +} + +.category-description { + margin: 0; + color: var(--card-text-color-secondary); + line-height: 1.4; + font-size: 0.9rem; +} + +.category-count { + background: var(--category-color, var(--accent-color)); + color: white; + padding: 0.25rem 0.75rem; + border-radius: 12px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + align-self: flex-start; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +@media (max-width: 768px) { + .glossary-categories-overview { + grid-template-columns: 1fr; + } +} diff --git a/assets/scss/glossary-links.scss b/assets/scss/glossary-links.scss new file mode 100644 index 00000000..f3e8cbf0 --- /dev/null +++ b/assets/scss/glossary-links.scss @@ -0,0 +1,94 @@ +/* Glossary Term Links */ +.glossary-term-link { + color: var(--accent-color); + text-decoration: none; + border-bottom: 1px dotted var(--accent-color); + position: relative; + cursor: help; + transition: all 0.2s ease; + + &:hover { + color: var(--accent-color-darker); + border-bottom-color: var(--accent-color-darker); + text-decoration: none; + } + + /* Tooltip for definitions */ + &::after { + content: attr(data-definition); + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%); + background: var(--card-text-color-main); + color: var(--card-background); + padding: 0.5rem 0.75rem; + border-radius: 4px; + font-size: 0.8rem; + line-height: 1.3; + white-space: normal; + width: max-content; + max-width: clamp(200px, 80vw, 300px); + opacity: 0; + visibility: hidden; + transition: all 0.2s ease; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0,0,0,0.2); + } + + &::before { + content: ''; + position: absolute; + bottom: calc(100% - 5px); + left: 50%; + transform: translateX(-50%); + border: 5px solid transparent; + border-top-color: var(--card-text-color-main); + opacity: 0; + visibility: hidden; + transition: all 0.2s ease; + z-index: 1001; + } + + &:hover::after, + &:hover::before { + opacity: 1; + visibility: visible; + } +} + +.glossary-term-missing { + color: var(--error-color); + border-bottom: 1px dotted var(--error-color); + cursor: help; +} + +/* Mobile optimizations */ +@media (max-width: 768px) { + .glossary-term-link::after { + position: fixed; + bottom: 20px; + left: 10px; + right: 10px; + transform: none; + max-width: none; + width: auto; + } + + .glossary-term-link::before { + display: none; + } +} + +/* Dark mode adjustments */ +[data-scheme="dark"] { + .glossary-term-link::after { + background: var(--card-background); + color: var(--card-text-color-main); + border: 1px solid var(--card-separator-color); + } + + .glossary-term-link::before { + border-top-color: var(--card-background); + } +} diff --git a/assets/scss/glossary-term.scss b/assets/scss/glossary-term.scss new file mode 100644 index 00000000..72cda5c2 --- /dev/null +++ b/assets/scss/glossary-term.scss @@ -0,0 +1,251 @@ +/* Glossary Term Individual Page Styles */ + +.glossary-single-term { + background: var(--card-background); + border-radius: var(--card-border-radius); + box-shadow: var(--shadow-l1); + overflow: hidden; +} + +/* Breadcrumb */ +.glossary-breadcrumb { + padding: 1rem var(--card-padding, 1.5rem); + background: var(--card-background-selected); + border-bottom: 1px solid var(--card-separator-color); + font-size: 0.875rem; +} + +.glossary-breadcrumb ol { + list-style: none; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0; + padding: 0; +} + +.glossary-breadcrumb li { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--card-text-color-secondary); +} + +.glossary-breadcrumb li:not(:last-child)::after { + content: "\203A"; + color: var(--card-text-color-secondary); +} + +.glossary-breadcrumb a { + color: var(--accent-color); + text-decoration: none; +} + +.glossary-breadcrumb a:hover { + text-decoration: underline; +} + +/* Category badge */ +.term-category-badge { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.4rem 1rem; + border-radius: var(--tag-border-radius); + font-size: 0.8rem; + font-weight: 700; + letter-spacing: 0.5px; + margin-bottom: 1rem; +} + +/* Full term subtitle */ +.full-term-subtitle { + display: block; + font-size: 1.2rem; + font-weight: 400; + color: var(--card-text-color-secondary); + margin-top: 0.5rem; +} + +/* Content sections */ +.glossary-term-content section { + margin-bottom: 2rem; +} + +.glossary-term-content h2 { + font-size: 1.4rem; + font-weight: 700; + color: var(--card-text-color-main); + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 2px solid var(--accent-color); +} + +/* FAQ details/summary */ +.glossary-faq-detail { + margin-bottom: 1rem; + padding: 1rem; + background: linear-gradient(135deg, rgba(139, 92, 246, 0.05) 0%, rgba(139, 92, 246, 0.02) 100%); + border-radius: var(--card-border-radius); + border: 1px solid rgba(139, 92, 246, 0.15); +} + +.glossary-faq-detail summary { + cursor: pointer; + list-style: none; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.glossary-faq-detail summary::-webkit-details-marker { + display: none; +} + +.glossary-faq-detail summary::before { + content: "\25B6"; + font-size: 0.7rem; + color: #8B5CF6; + transition: transform 0.2s ease; + flex-shrink: 0; +} + +.glossary-faq-detail[open] summary::before { + transform: rotate(90deg); +} + +.glossary-faq-detail summary h3 { + margin: 0; + font-size: 1rem; + font-weight: 700; + color: var(--card-text-color-main); +} + +.glossary-faq-detail p { + margin: 1rem 0 0 1.2rem; + color: var(--card-text-color-secondary); + line-height: 1.6; +} + +/* Related terms list */ +.glossary-related-terms-list { + list-style: none; + padding: 0; + display: flex; + flex-wrap: wrap; + gap: 0.75rem; +} + +.glossary-related-terms-list li { + margin: 0; +} + +.glossary-related-terms-list a { + display: inline-block; + padding: 0.4rem 1rem; + background: var(--card-background-selected); + border: 1px solid var(--card-separator-color); + border-radius: var(--tag-border-radius); + color: var(--accent-color); + text-decoration: none; + font-weight: 600; + font-size: 0.9rem; + transition: all 0.2s ease; +} + +.glossary-related-terms-list a:hover { + background: var(--accent-color); + color: var(--accent-color-text); + border-color: var(--accent-color); +} + +/* ============================================= + PREV/NEXT NAVIGATION + ============================================= */ +.glossary-prev-next { + display: grid; + grid-template-columns: 1fr auto 1fr; + gap: 1rem; + margin-top: 2rem; + padding-top: 1.5rem; + border-top: 1px solid var(--card-separator-color); + align-items: center; +} + +.glossary-prev-next a { + text-decoration: none; + color: inherit; + padding: 0.75rem; + border-radius: var(--card-border-radius, 8px); + transition: background 0.2s ease; +} + +.glossary-prev-next a:hover { + background: var(--card-background-selected); +} + +.prev-next-left { + text-align: left; +} + +.prev-next-center { + text-align: center; +} + +.prev-next-center a { + color: var(--accent-color); + font-weight: 600; + font-size: 0.95rem; +} + +.prev-next-right { + text-align: right; +} + +.prev-next-label { + display: block; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--card-text-color-secondary); + margin-bottom: 0.25rem; +} + +.prev-next-term { + display: block; + font-weight: 700; + color: var(--accent-color); + font-size: 0.95rem; +} + +/* Dark mode */ +[data-scheme="dark"] .glossary-faq-detail { + background: rgba(139, 92, 246, 0.08); + border-color: rgba(139, 92, 246, 0.2); +} + +[data-scheme="dark"] .glossary-breadcrumb { + background: var(--card-background-selected); +} + +[data-scheme="dark"] .glossary-prev-next a:hover { + background: var(--card-background-selected); +} + +/* Mobile */ +@media (max-width: 768px) { + .glossary-prev-next { + grid-template-columns: 1fr; + text-align: center; + } + + .prev-next-left, + .prev-next-right { + text-align: center; + } + + .prev-next-center { + order: -1; + } +} diff --git a/assets/scss/glossary.scss b/assets/scss/glossary.scss new file mode 100644 index 00000000..eb79ea31 --- /dev/null +++ b/assets/scss/glossary.scss @@ -0,0 +1,939 @@ +/* Glossary section accent colors */ +$glossary-example-color: #3B82F6; +$glossary-risks-color: #F59E0B; +$glossary-risks-header-color: #D97706; +$glossary-faq-color: #8B5CF6; + +/* Glossary Page Header Padding Fix */ +.main-article .article-header { + padding-left: var(--card-padding, 1.5rem); + padding-right: var(--card-padding, 1.5rem); + margin-bottom: 2rem; +} + +.main-article .article-title { + margin-bottom: 1rem; +} + +.main-article .article-subtitle { + margin-bottom: 1.5rem; + line-height: 1.6; +} + +/* Glossary App Styles */ +.glossary-app { + margin-top: 2rem; +} + +/* ============================================= + GLOSSARY CONTROLS BAR (Search + Pills) + ============================================= */ +.glossary-controls { + position: sticky; + top: 0; + z-index: 20; + background: rgba(var(--body-background-rgb, 255,255,255), 0.85); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + padding: 1rem var(--card-padding, 1.5rem); + margin: 0 calc(-1 * var(--card-padding, 1.5rem)); + border-bottom: 1px solid var(--card-separator-color); +} + +/* Search Input */ +.glossary-search-wrap { + position: relative; + margin-bottom: 0.75rem; +} + +.glossary-search-wrap .search-icon { + position: absolute; + left: 0.875rem; + top: 50%; + transform: translateY(-50%); + color: var(--card-text-color-secondary); + pointer-events: none; +} + +.glossary-search { + width: 100%; + padding: 1rem 1.25rem 1rem 3rem; + border: 1.5px solid var(--card-separator-color); + border-radius: var(--tag-border-radius, 8px); + background: var(--card-background); + color: var(--card-text-color-main); + font-size: 1.3rem; + outline: none; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.glossary-search:focus { + border-color: var(--accent-color); + box-shadow: 0 0 0 3px rgba(var(--accent-color-rgb, 66,133,244), 0.15); +} + +.glossary-search::placeholder { + color: var(--card-text-color-secondary); + opacity: 0.7; +} + +/* Category Pills */ +.category-pills { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +.category-pill { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.55rem 1.15rem; + border-radius: 999px; + border: 1.5px solid var(--pill-color, var(--card-separator-color)); + background: transparent; + color: var(--card-text-color-main); + font-size: 1.1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + white-space: nowrap; +} + +.category-pill:hover { + background: rgba(var(--accent-color-rgb, 66,133,244), 0.08); +} + +.category-pill.active { + background: var(--pill-color, var(--accent-color)); + color: white; + border-color: var(--pill-color, var(--accent-color)); +} + +.pill-count { + font-size: 1rem; + opacity: 0.8; +} + +.glossary-result-count { + font-size: 1.2rem; + color: var(--card-text-color-secondary); + font-weight: 500; +} + +/* ============================================= + ALPHABET NAVIGATION + ============================================= */ +.alphabet-nav { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; + margin-bottom: 1.5rem; + padding: 0.75rem var(--card-padding, 1.5rem); + background: var(--card-background); + border-radius: var(--card-border-radius); + border: 1px solid var(--card-separator-color); + align-items: center; + justify-content: center; + position: sticky; + top: 120px; /* Below controls bar */ + z-index: 15; +} + +.alphabet-link { + background: var(--accent-color); + color: var(--accent-color-text); + border: none; + padding: 0.45rem 0.7rem; + border-radius: var(--tag-border-radius); + cursor: pointer; + font-size: 0.95rem; + font-weight: 600; + transition: all 0.2s ease; + min-width: 32px; + text-align: center; +} + +.alphabet-link:hover { + background: var(--accent-color-darker); + transform: translateY(-1px); + box-shadow: var(--shadow-l1); +} + +.alphabet-link.active { + transform: scale(1.15); + box-shadow: var(--shadow-l2); + background: var(--accent-color-darker, var(--accent-color)); +} + +/* ============================================= + LETTER SECTIONS + ============================================= */ +.letter-section { + margin-bottom: 2.5rem; +} + +.letter-heading { + font-size: 2.25rem; + font-weight: 900; + color: var(--accent-color); + margin-bottom: 1.25rem; + padding-bottom: 0.6rem; + border-bottom: 2px solid var(--accent-color); + background: var(--body-background); + z-index: 10; +} + +/* ============================================= + COMPACT TERM CARDS (Hub Page) + ============================================= */ +.compact-card { + display: flex; + align-items: flex-start; + gap: 1.25rem; + padding: 1.25rem 1.5rem; + margin-bottom: 1rem; + background: var(--card-background); + border: 1px solid var(--card-separator-color); + border-left: 3px solid var(--term-accent, var(--card-separator-color)); + border-radius: var(--card-border-radius); + text-decoration: none; + color: inherit; + transition: all 0.2s ease; + box-shadow: var(--shadow-l1); + position: relative; +} + +.compact-card:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-l2); + border-left-width: 5px; + border-color: var(--term-accent, var(--accent-color-lighter)); +} + +.compact-card-icon { + width: 48px; + height: 48px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 20px; + flex-shrink: 0; +} + +.compact-card-icon svg, +.term-icon svg { + width: 60%; + height: 60%; + fill: none; + stroke: white; + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; +} + +.term-category-badge svg { + width: 16px; + height: 16px; + fill: none; + stroke: white; + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; + vertical-align: middle; + display: inline-block; +} + +.compact-card-body { + flex: 1; + min-width: 0; +} + +.compact-card-header { + display: flex; + align-items: baseline; + gap: 0.6rem; + margin-bottom: 0.4rem; + flex-wrap: wrap; +} + +.compact-term-name { + font-size: 1.75rem; + font-weight: 700; + color: var(--card-text-color-main); +} + +.compact-full-term { + font-size: 1.2rem; + color: var(--card-text-color-secondary); + font-weight: 400; +} + +.compact-definition { + margin: 0; + font-size: 1.3rem; + line-height: 1.65; + color: var(--card-text-color-secondary); + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.compact-badge { + font-size: 0.85rem; + font-weight: 600; + color: white; + padding: 0.35rem 0.85rem; + border-radius: 999px; + white-space: nowrap; + flex-shrink: 0; + text-transform: uppercase; + letter-spacing: 0.3px; + align-self: center; +} + +/* ============================================= + FULL TERM CARDS (Individual Term Pages only) + ============================================= */ +.glossary-term-page .term-card { + background: var(--card-background); + border: 1px solid var(--card-separator-color); + border-radius: var(--card-border-radius); + padding: 0; + margin-bottom: 2rem; + transition: all 0.3s ease; + box-shadow: var(--shadow-l1); + position: relative; + border-left: 4px solid var(--card-separator-color); + overflow: hidden; +} + +.glossary-term-page .term-card:hover { + box-shadow: var(--shadow-l2); + transform: translateY(-2px); + border-color: var(--accent-color-lighter); +} + +.term-category-header { + padding: 0.75rem 1.5rem; + font-size: 0.85rem; + font-weight: 700; + letter-spacing: 1px; + text-align: left; + margin: 0; + border-radius: var(--card-border-radius) var(--card-border-radius) 0 0; +} + +.term-header { + display: flex; + align-items: center; + margin-bottom: 1rem; + gap: 0.75rem; + position: relative; + padding: var(--card-padding); + padding-bottom: 0; +} + +.term-icon { + width: 48px; + height: 48px; + border-radius: var(--card-border-radius); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 20px; + font-weight: bold; + flex-shrink: 0; + box-shadow: var(--shadow-l1); + margin-left: -4px; +} + +.term-title-group { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + gap: 0.25rem; + min-height: 48px; +} + +.term-title { + margin: 0; + flex: 1; +} + +.term-name { + font-size: 1.5rem; + font-weight: 800; + color: var(--card-text-color-main); + margin: 0; + line-height: 1.2; +} + +.full-term { + display: block; + font-size: 1rem; + font-weight: 400; + color: var(--card-text-color-secondary); + margin: 0; + line-height: 1.3; +} + +.term-category { + background: var(--accent-color); + color: var(--accent-color-text); + padding: 0.5rem 1rem; + border-radius: var(--tag-border-radius); + font-size: 0.8rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.term-definition { + font-size: 1.6rem; + line-height: 1.7; + color: var(--card-text-color-main); + margin-bottom: 1.5rem; + font-weight: 600; + padding: 1.5rem 2rem; + background: linear-gradient(135deg, + rgba(var(--accent-color-rgb), 0.08) 0%, + rgba(var(--accent-color-rgb), 0.03) 100%); + border: 1px solid rgba(var(--accent-color-rgb), 0.15); + border-radius: var(--card-border-radius); + border-left: 4px solid var(--accent-color); + position: relative; + box-shadow: 0 2px 12px rgba(var(--accent-color-rgb), 0.12); +} + +.term-definition::before { + content: '\201C'; + position: absolute; + top: -5px; + left: 15px; + font-size: 3rem; + color: var(--accent-color); + opacity: 0.3; + font-family: Georgia, serif; + font-weight: bold; + line-height: 1; +} + +.term-definition::after { + content: '\201D'; + position: absolute; + bottom: -15px; + right: 20px; + font-size: 3rem; + color: var(--accent-color); + opacity: 0.3; + font-family: Georgia, serif; + font-weight: bold; + line-height: 1; +} + +/* Example Section */ +.term-example { + margin-bottom: 1rem; + padding: 0.875rem; + background: linear-gradient(135deg, rgba($glossary-example-color, 0.05) 0%, rgba($glossary-example-color, 0.02) 100%); + border: 1px solid rgba($glossary-example-color, 0.2); + border-radius: var(--card-border-radius); + border-left: 4px solid $glossary-example-color; +} + +.example-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +.example-icon { + font-size: 1.2rem; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} + +.example-header strong { + color: $glossary-example-color; + font-size: 0.9rem; + font-weight: 600; +} + +/* Risks Section */ +.term-risks { + margin-bottom: 1rem; + padding: 0.875rem; + background: linear-gradient(135deg, rgba($glossary-risks-color, 0.08) 0%, rgba($glossary-risks-color, 0.03) 100%); + border: 1px solid rgba($glossary-risks-color, 0.25); + border-radius: var(--card-border-radius); + border-left: 4px solid $glossary-risks-color; +} + +.risks-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +.risks-icon { + font-size: 1.2rem; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} + +.risks-header strong { + color: $glossary-risks-header-color; + font-size: 0.9rem; + font-weight: 600; +} + +/* 2-Column Layout for Secondary Info */ +.term-secondary-info { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-bottom: 1rem; +} + +.term-secondary-info .term-risks { + margin-bottom: 0; +} + +.term-secondary-info .term-related { + margin-bottom: 0; +} + +/* Related Terms Section */ +.term-related { + margin-bottom: 1.5rem; + padding: 0.875rem; + background: var(--card-background-selected); + border-radius: var(--card-border-radius); + border-left: 4px solid var(--accent-color); +} + +.term-related strong { + color: var(--accent-color); + font-size: 1rem; + font-weight: 700; + display: block; + margin-bottom: 0.75rem; +} + +.term-risks ul { + margin: 0; + padding-left: 1.5rem; + color: var(--card-text-color-main); +} + +.term-risks li { + margin-bottom: 0.5rem; + line-height: 1.5; +} + +.related-link { + color: var(--accent-color); + text-decoration: none; + font-weight: 600; + transition: color 0.3s ease; +} + +.related-link:hover { + color: var(--accent-color-darker); + text-decoration: underline; +} + +/* FAQ Section */ +.term-faqs { + margin-top: 2rem; + padding: 1.5rem; + background: linear-gradient(135deg, rgba($glossary-faq-color, 0.05) 0%, rgba($glossary-faq-color, 0.02) 100%); + border-radius: var(--card-border-radius); +} + +.faq-header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1.5rem; + padding-bottom: 1rem; +} + +.faq-icon { + font-size: 1.5rem; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + color: $glossary-faq-color; + background: none; +} + +.faq-header h4 { + color: $glossary-faq-color; + font-size: 1.25rem; + font-weight: 700; + margin: 0; +} + +.faq-container { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.faq-item { + background: transparent; + border: none; + border-radius: var(--card-border-radius); + padding: 1.25rem; + transition: all 0.3s ease; +} + +.faq-item:hover { + background: rgba(var(--accent-color-rgb), 0.08); + transform: translateY(-1px); +} + +[data-scheme="dark"] .faq-item { + background: transparent; +} + +[data-scheme="dark"] .faq-item:hover { + background: rgba($glossary-faq-color, 0.08); +} + +/* Articles section */ +.term-articles { + margin-top: 2rem; + padding-top: 1.5rem; + border-top: 1px solid var(--card-separator-color); +} + +.term-articles h4 { + color: var(--accent-color); + font-size: 1.2rem; + font-weight: 700; + margin-bottom: 1rem; +} + +.faq-question { + font-weight: 700; + color: var(--card-text-color-main); + margin-bottom: 0.75rem; + font-size: 1rem; +} + +.faq-answer { + color: var(--card-text-color-secondary); + line-height: 1.6; + margin: 0; +} + +.article-item { + background: var(--card-background-selected); + border-radius: var(--card-border-radius); + padding: var(--card-padding); + margin-bottom: 1rem; + border-left: 3px solid var(--accent-color); +} + +.article-item h5 { + margin: 0 0 0.5rem 0; + font-size: 1rem; + font-weight: 700; +} + +.article-item h5 a { + color: var(--accent-color); + text-decoration: none; + transition: color 0.3s ease; +} + +.article-item h5 a:hover { + color: var(--accent-color-darker); + text-decoration: underline; +} + +.article-description { + color: var(--card-text-color-secondary); + font-size: 0.9rem; + line-height: 1.5; + margin: 0; +} + +.no-results { + text-align: center; + padding: 3rem var(--card-padding); + background: var(--card-background); + border-radius: var(--card-border-radius); + border: 1px solid var(--card-separator-color); +} + +.no-results h3 { + color: var(--card-text-color-main); + margin-bottom: 1rem; + font-size: 1.5rem; +} + +.no-results p { + color: var(--card-text-color-secondary); + font-size: 1rem; +} + +/* Related Articles Styling */ +.related-articles-list { + display: grid; + gap: 1rem; +} + +.related-articles-list article { + background: var(--card-background-selected); + border: 1px solid var(--card-separator-color); + border-radius: var(--card-border-radius); + overflow: hidden; + transition: all 0.3s ease; + box-shadow: var(--shadow-l1); +} + +.related-articles-list article:hover { + box-shadow: var(--shadow-l2); + transform: translateY(-2px); + border-color: var(--accent-color-lighter); +} + +.related-articles-list article a { + text-decoration: none; + color: inherit; + display: flex; + align-items: flex-start; + gap: 1rem; + padding: 1rem; + height: 100%; +} + +.related-articles-list .article-details { + flex: 1; + min-width: 0; +} + +.related-articles-list .article-title { + font-size: 1rem; + font-weight: 700; + color: var(--accent-color); + margin: 0 0 0.5rem 0; + line-height: 1.3; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.related-articles-list .article-preview { + font-size: 0.875rem; + color: var(--card-text-color-secondary); + line-height: 1.4; + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.related-articles-list .article-image { + flex-shrink: 0; + width: 80px; + height: 60px; + border-radius: var(--card-border-radius); + overflow: hidden; + background: var(--card-background); + display: flex; + align-items: center; + justify-content: center; +} + +.related-articles-list .article-image img { + width: 80%; + height: 80%; + object-fit: cover; + transition: transform 0.3s ease; + border-radius: var(--card-border-radius); +} + +.related-articles-list article:hover .article-image img { + transform: scale(1.05); +} + +/* ============================================= + RESPONSIVE DESIGN + ============================================= */ +@media (max-width: 768px) { + .glossary-controls { + padding: 0.75rem 1rem; + margin: 0 -1rem; + } + + .category-pills { + flex-wrap: nowrap; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + padding-bottom: 0.25rem; + } + + .category-pills::-webkit-scrollbar { + display: none; + } + + .compact-badge { + display: none; + } + + .compact-card { + padding: 1rem 1.15rem; + gap: 1rem; + } + + .compact-card-icon { + width: 40px; + height: 40px; + font-size: 17px; + } + + .compact-term-name { + font-size: 1.45rem; + } + + .compact-full-term { + font-size: 1.1rem; + } + + .compact-definition { + font-size: 1.15rem; + } + + .alphabet-nav { + gap: 0.25rem; + } + + .alphabet-link { + padding: 0.3rem 0.5rem; + font-size: 0.85rem; + min-width: 26px; + } + + .letter-heading { + font-size: 1.85rem; + } + + .term-header { + flex-direction: column; + align-items: flex-start; + } + + .term-name { + font-size: 1.3rem; + } + + .term-secondary-info { + grid-template-columns: 1fr; + } + + .related-articles-list .article-image { + width: 60px; + height: 45px; + } + + .related-articles-list .article-title { + font-size: 0.9rem; + } + + .related-articles-list .article-preview { + font-size: 0.8rem; + } +} + +/* ============================================= + DARK MODE + ============================================= */ +[data-scheme="dark"] .glossary-controls { + background: rgba(var(--body-background-rgb, 30,30,30), 0.85); +} + +[data-scheme="dark"] .glossary-search { + background: var(--card-background); + border-color: var(--card-separator-color); + color: var(--card-text-color-main); +} + +[data-scheme="dark"] .category-pill { + color: var(--card-text-color-main); +} + +[data-scheme="dark"] .category-pill.active { + color: white; +} + +[data-scheme="dark"] .compact-card { + background: var(--card-background); + border-color: var(--card-separator-color); +} + +[data-scheme="dark"] .compact-term-name { + color: var(--card-text-color-main); +} + +[data-scheme="dark"] .compact-definition { + color: var(--card-text-color-secondary); +} + +[data-scheme="dark"] .glossary-term-page .term-card { + background: var(--card-background); + border-color: var(--card-separator-color); + color: var(--card-text-color-main); +} + +[data-scheme="dark"] .term-definition, +[data-scheme="dark"] .term-example p, +[data-scheme="dark"] .term-related p, +[data-scheme="dark"] .faq-answer { + color: var(--card-text-color-main); +} + +[data-scheme="dark"] .term-risks li { + color: var(--card-text-color-main); +} + +[data-scheme="dark"] .letter-heading { + background: var(--body-background); + color: var(--accent-color); +} + +[data-scheme="dark"] .related-articles-list article { + background: var(--card-background-selected); + border-color: var(--card-separator-color); +} + +[data-scheme="dark"] .related-articles-list .article-title { + color: var(--accent-color); +} + +[data-scheme="dark"] .related-articles-list .article-preview { + color: var(--card-text-color-secondary); +} diff --git a/assets/ts/main.ts b/assets/ts/main.ts new file mode 100644 index 00000000..c2e06340 --- /dev/null +++ b/assets/ts/main.ts @@ -0,0 +1,111 @@ +/*! +* Hugo Theme Stack +* +* @author: Jimmy Cai +* @website: https://jimmycai.com +* @link: https://github.com/CaiJimmy/hugo-theme-stack +*/ +import StackGallery from "ts/gallery"; +import { getColor } from 'ts/color'; +import menu from 'ts/menu'; +import createElement from 'ts/createElement'; +import StackColorScheme from 'ts/colorScheme'; +import { setupScrollspy } from 'ts/scrollspy'; +import { setupSmoothAnchors } from "ts/smoothAnchors"; + +let Stack = { + init: () => { + /** + * Bind menu event + */ + menu(); + + const articleContent = document.querySelector('.article-content') as HTMLElement; + if (articleContent) { + new StackGallery(articleContent); + setupSmoothAnchors(); + setupScrollspy(); + } + + /** + * Add linear gradient background to tile style article + */ + const articleTile = document.querySelector('.article-list--tile'); + if (articleTile) { + let observer = new IntersectionObserver(async (entries, observer) => { + entries.forEach(entry => { + if (!entry.isIntersecting) return; + observer.unobserve(entry.target); + + const articles = entry.target.querySelectorAll('article.has-image'); + articles.forEach(async articles => { + const image = articles.querySelector('img'), + imageURL = image.src, + key = image.getAttribute('data-key'), + hash = image.getAttribute('data-hash'), + articleDetails: HTMLDivElement = articles.querySelector('.article-details'); + + const colors = await getColor(key, hash, imageURL); + + articleDetails.style.background = ` + linear-gradient(0deg, + rgba(${colors.DarkMuted.rgb[0]}, ${colors.DarkMuted.rgb[1]}, ${colors.DarkMuted.rgb[2]}, 0.5) 0%, + rgba(${colors.Vibrant.rgb[0]}, ${colors.Vibrant.rgb[1]}, ${colors.Vibrant.rgb[2]}, 0.75) 100%)`; + }) + }) + }); + + observer.observe(articleTile) + } + + + /** + * Add copy button to code block + */ + const highlights = document.querySelectorAll('.article-content div.highlight'); + const copyText = `Copy`, + copiedText = `Copied!`; + + highlights.forEach(highlight => { + const copyButton = document.createElement('button'); + copyButton.innerHTML = copyText; + copyButton.classList.add('copyCodeButton'); + highlight.appendChild(copyButton); + + const codeBlock = highlight.querySelector('code[data-lang]'); + if (!codeBlock) return; + + copyButton.addEventListener('click', () => { + navigator.clipboard.writeText(codeBlock.textContent) + .then(() => { + copyButton.textContent = copiedText; + + setTimeout(() => { + copyButton.textContent = copyText; + }, 1000); + }) + .catch(err => { + console.error('Clipboard copy failed:', err); + }); + }); + }); + + new StackColorScheme(document.getElementById('dark-mode-toggle')); + } +} + +window.addEventListener('load', () => { + setTimeout(function () { + Stack.init(); + }, 0); +}) + +declare global { + interface Window { + createElement: any; + Stack: any + } +} + +window.Stack = Stack; +window.createElement = createElement; \ No newline at end of file diff --git a/assets/ts/scrollspy.ts b/assets/ts/scrollspy.ts new file mode 100644 index 00000000..043ecddc --- /dev/null +++ b/assets/ts/scrollspy.ts @@ -0,0 +1,131 @@ +// Implements a scroll spy system for the ToC, displaying the current section with an indicator and scrolling to it when needed. + +// Inspired from https://gomakethings.com/debouncing-your-javascript-events/ +function debounced(func: Function) { + let timeout; + return () => { + if (timeout) { + window.cancelAnimationFrame(timeout); + } + + timeout = window.requestAnimationFrame(() => func()); + } +} + +const headersQuery = ".article-content h1[id], .article-content h2[id], .article-content h3[id], .article-content h4[id], .article-content h5[id], .article-content h6[id]"; +const tocQuery = "#TableOfContents"; +const navigationQuery = "#TableOfContents li"; +const activeClass = "active-class"; + +function scrollToTocElement(tocElement: HTMLElement, scrollableNavigation: HTMLElement) { + let textHeight = tocElement.querySelector("a").offsetHeight; + let scrollTop = tocElement.offsetTop - scrollableNavigation.offsetHeight / 2 + textHeight / 2 - scrollableNavigation.offsetTop; + if (scrollTop < 0) { + scrollTop = 0; + } + scrollableNavigation.scrollTo({ top: scrollTop, behavior: "smooth" }); +} + +type IdToElementMap = { [key: string]: HTMLElement }; + +function buildIdToNavigationElementMap(navigation: NodeListOf): IdToElementMap { + const sectionLinkRef: IdToElementMap = {}; + navigation.forEach((navigationElement: HTMLElement) => { + const link = navigationElement.querySelector("a"); + const href = link.getAttribute("href"); + if (href.startsWith("#")) { + sectionLinkRef[href.slice(1)] = navigationElement; + } + }); + + return sectionLinkRef; +} + +function computeOffsets(headers: NodeListOf) { + let sectionsOffsets = []; + headers.forEach((header: HTMLElement) => { sectionsOffsets.push({ id: header.id, offset: header.offsetTop }) }); + sectionsOffsets.sort((a, b) => a.offset - b.offset); + return sectionsOffsets; +} + +function setupScrollspy() { + let headers = document.querySelectorAll(headersQuery); + if (!headers) { + console.debug("No header matched query", headers); + return; + } + + let scrollableNavigation = document.querySelector(tocQuery) as HTMLElement | undefined; + if (!scrollableNavigation) { + console.debug("No toc matched query", tocQuery); + return; + } + + let navigation = document.querySelectorAll(navigationQuery); + if (!navigation) { + console.debug("No navigation matched query", navigationQuery); + return; + } + + let sectionsOffsets = computeOffsets(headers); + + // We need to avoid scrolling when the user is actively interacting with the ToC. Otherwise, if the user clicks on a link in the ToC, + // we would scroll their view, which is not optimal usability-wise. + let tocHovered: boolean = false; + scrollableNavigation.addEventListener("mouseenter", debounced(() => tocHovered = true)); + scrollableNavigation.addEventListener("mouseleave", debounced(() => tocHovered = false)); + + let activeSectionLink: Element; + + let idToNavigationElement: IdToElementMap = buildIdToNavigationElementMap(navigation); + + function scrollHandler() { + let scrollPosition = document.documentElement.scrollTop || document.body.scrollTop; + + let newActiveSection: HTMLElement | undefined; + + // Find the section that is currently active. + // It is possible for no section to be active, so newActiveSection may be undefined. + sectionsOffsets.forEach((section) => { + if (scrollPosition >= section.offset - 20) { + newActiveSection = document.getElementById(section.id); + } + }); + + // Find the link for the active section. Once again, there are a few edge cases: + // - No active section = no link => undefined + // - No active section but the link does not exist in toc (e.g. because it is outside of the applicable ToC levels) => undefined + let newActiveSectionLink: HTMLElement | undefined + if (newActiveSection) { + newActiveSectionLink = idToNavigationElement[newActiveSection.id]; + } + + if (newActiveSection && !newActiveSectionLink) { + // The active section does not have a link in the ToC, so we can't scroll to it. + console.debug("No link found for section", newActiveSection); + } else if (newActiveSectionLink !== activeSectionLink) { + if (activeSectionLink) + activeSectionLink.classList.remove(activeClass); + if (newActiveSectionLink) { + newActiveSectionLink.classList.add(activeClass); + if (!tocHovered) { + // Scroll so that newActiveSectionLink is in the middle of scrollableNavigation, except when it's from a manual click (hence the tocHovered check) + scrollToTocElement(newActiveSectionLink, scrollableNavigation); + } + } + activeSectionLink = newActiveSectionLink; + } + } + + window.addEventListener("scroll", debounced(scrollHandler)); + + // Resizing may cause the offset values to change: recompute them. + function resizeHandler() { + sectionsOffsets = computeOffsets(headers); + scrollHandler(); + } + + window.addEventListener("resize", debounced(resizeHandler)); +} + +export { setupScrollspy }; \ No newline at end of file diff --git a/build/image-optimizer.js b/build/image-optimizer.js deleted file mode 100644 index 7d47db72..00000000 --- a/build/image-optimizer.js +++ /dev/null @@ -1,83 +0,0 @@ -const hash = require('hash-files'); -const sharp = require('sharp'); -const async = require('async'); -const path = require('path'); -const _ = require('lodash'); -const fs = require('fs'); - -const SUPPORTED_FORMATS = ['.jpeg', '.jpg', '.png']; - -const generateFileHash = filePath => hash.sync({ files: [filePath], algorithm: 'sha1' }); -const isDirectory = filePath => fs.statSync(filePath).isDirectory(); - -const isFileSupported = (filePath) => { - const extension = path.extname(filePath).toLowerCase(); - return SUPPORTED_FORMATS.includes(extension); -}; - -const optimiseImage = (filePath, format, callback) => { - const tempPath = `${filePath}.tmp`; - if (format === 'jpeg') { - sharp(filePath) - .jpeg({ quality: 75}) - .toFormat(format) - .toFile(tempPath, (err) => { - if (err) { - return callback(err, null); - } - - fs.renameSync(tempPath, filePath); - - return callback(null, { filePath, hash: generateFileHash(tempPath) }); - }); - } else { - sharp(filePath) - .png({ quality: 75}) - .toFormat(format) - .toFile(tempPath, (err) => { - if (err) { - return callback(err, null); - } - - fs.renameSync(tempPath, filePath); - - return callback(null, { filePath, hash: generateFileHash(tempPath) }); - }); - } -}; - -const isJpeg = (filePath) => { - const extension = path.extname(filePath).toLowerCase(); - return extension === '.jpeg' || extension === '.jpg'; -}; - -const getImagePathsInDirectory = (dir, filelist = []) => { - let files = filelist; - fs.readdirSync(dir).forEach((file) => { - const filePath = path.join(dir, file); - if (isDirectory(filePath)) { - getImagePathsInDirectory(path.join(dir, file), filelist); - } else if (isFileSupported(filePath)) { - files.push(path.join(dir, file)); - } - }); - - return files; -}; - -module.exports = (options, callback) => { - const filePaths = getImagePathsInDirectory(options.root); - - const optimiseTasks = _(filePaths) - .map(filePath => cb => - optimiseImage(filePath, - isJpeg(filePath) ? sharp.format.jpeg : sharp.format.png, - cb)).value(); - - async.parallel(optimiseTasks, (err, result) => { - if (err) { - return callback(err, null); - } - }); - return callback(null, null); -}; \ No newline at end of file diff --git a/build/optimize.js b/build/optimize.js deleted file mode 100644 index b9547650..00000000 --- a/build/optimize.js +++ /dev/null @@ -1,8 +0,0 @@ -const imageOptimizer = require('./image-optimizer'); - -imageOptimizer({ - root: './public/img', // The root directory for your images. - blacklist: [] // The list of directories/files not to optimize. -}, (err, result) => { - console.log(err, result); -}); diff --git a/build/webpack.config.dev.js b/build/webpack.config.dev.js deleted file mode 100644 index 48538ff2..00000000 --- a/build/webpack.config.dev.js +++ /dev/null @@ -1,38 +0,0 @@ -const webpack = require('webpack'); -const path = require('path'); -const {VueLoaderPlugin} = require('vue-loader'); - - -module.exports = { - entry: './src/js/main.js', - mode: 'development', - output: { - path: path.resolve(__dirname, '../static/js'), - filename: 'main.js', - }, - module: { - rules: [ - { - test: /\.vue$/, - use: { - loader: 'vue-loader', - }, - }, - { - test: /\.css$/, - use: [ - 'vue-style-loader', - 'css-loader' - ], - }, - ], - }, - plugins: [ - new VueLoaderPlugin() - ], - resolve: { - alias: { - vue: 'vue/dist/vue.esm.js' - } - } -}; \ No newline at end of file diff --git a/build/webpack.config.production.js b/build/webpack.config.production.js deleted file mode 100644 index bc86e5c9..00000000 --- a/build/webpack.config.production.js +++ /dev/null @@ -1,55 +0,0 @@ -const webpack = require('webpack'); -const path = require('path'); -const {VueLoaderPlugin} = require('vue-loader'); -const UglifyJsPlugin = require('uglifyjs-webpack-plugin') - - -module.exports = { - entry: './src/js/main.js', - mode: 'development', - output: { - path: path.resolve(__dirname, '../static/js'), - filename: 'main.js', - }, - module: { - rules: [ - { - test: /\.vue$/, - use: { - loader: 'vue-loader', - }, - }, - { - test: /\.css$/, - use: [ - 'vue-style-loader', - 'css-loader' - ], - }, - ], - }, - plugins: [ - new VueLoaderPlugin(), - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: '"production"' - } - }), - ], - optimization: { - minimizer: [ - new UglifyJsPlugin({ - uglifyOptions: { - warnings: false, - compress: {}, - mangle: true, // Note `mangle.properties` is `false` by default. - }, - }), - ], - }, - resolve: { - alias: { - vue: 'vue/dist/vue.esm.js' - } - } -}; \ No newline at end of file diff --git a/config.toml b/config.toml deleted file mode 100644 index dccf84a7..00000000 --- a/config.toml +++ /dev/null @@ -1,1390 +0,0 @@ -########################################## -# Site Settings - -baseURL = "https://tokenbrice.xyz/" -title = "TokenBrice - Blog" -languageCode = "en" -hasCJKLanguage = false -# Copyright information (Markdown supported) -copyright = "[CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en)" - -# Name of theme -theme = ["hugo-notice", "meme", "matomo"] - -# `hugo new` automatically opened text editor -newContentEditor = "typora" - -# i18n -defaultContentLanguage = "en" -defaultContentLanguageInSubdir = false - -# Pluralize titles in lists? -pluralizeListTitles = false - -# The length of text in words to show in summary -summaryLength = 42 - -# Enable Emoji emoticons support? -enableEmoji = false - -# Text to display for footnote return links -footnoteReturnLinkContents = "↩" - -# Enable `.GitInfo` object for each page? -enableGitInfo = true - -# Enable generation of `robots.txt` file? -enableRobotsTXT = true - -# Number of items per page in paginated -# lists (valid for the `posts` homepage -# layout in MemE) -paginate = 7 - -# URL related -disablePathToLower = false -relativeURLs = true -uglyURLs = false - -#language -[languages] - [languages.en] - weight = 1 - languageName = "🇺🇸 EN" - [languages.fr] - title = "TokenBrice - Blog" - weight = 2 - languageName = "🇫🇷 FR" - homePoetry = ["Bienvenue explorateur,
J'espère que vous apprécierez votre séjour dans l'Ether
[— TokenBrice](/fr/about/),
Motivé par un but simple :
**Mettre en lumière le feu de l'Ether.**

[🇫🇷 DeFi France & 💸 BanklessFR](/fr/defifrance)
[👾 Do You Even DeFi? (EN)](/dyed/)
[🌌 Articles DeFi](/fr/categories/defi/)"] - siteDescription = "Blog personnel de TokenBrice - Finance décentralisée (DeFi), Ethereum, et développement de communautés adaptées aux réalités web3. Contenus en FR & EN." - -# URL structure -[permalinks] - categories = "/categories/:slug/" - tags = "/tags/:slug/" - -# Taxonomies -[taxonomies] - category = "categories" - tag = "tags" - -# Modified date -[frontmatter] - lastmod = ["lastmod", ":git", ":fileModTime", ":default"] - -# Markdown renderer -[markup] - defaultMarkdownHandler = "goldmark" - [markup.goldmark] - [markup.goldmark.extensions] - definitionList = true - footnote = true - linkify = true - strikethrough = true - table = true - taskList = true - typographer = true - [markup.goldmark.parser] - attribute = true - autoHeadingID = true - autoHeadingIDType = "github" - [markup.goldmark.renderer] - hardWraps = false - unsafe = true - xHTML = false - [markup.highlight] - codeFences = true - guessSyntax = false - lineNos = true - lineNumbersInTable = true - noClasses = false - [markup.tableOfContents] - startLevel = 2 - endLevel = 6 - ordered = true - -# Author’s information -[author] - # Name - name = "TokenBrice" - # Email - email = "me@tokenbrice.com" - # Motto or introduction - motto = "Helping communities to shape, record and share the story of Ethereum." - # Avatar - avatar = "/img/main/tokenbrice.jpg" - # Twitter - twitter = "TokenBrice" - -# Related content -[related] - threshold = 80 - includeNewer = true - toLower = true - [[related.indices]] - name = "categories" - weight = 100 - [[related.indices]] - name = "tags" - weight = 95 - [[related.indices]] - name = "date" - weight = 10 - pattern = "2006" - -# MIME type of Atom -[mediaTypes."application/atom+xml"] - suffixes = ["xml"] - -# Custom Atom template of MemE -[outputFormats.SectionsAtom] - mediaType = "application/atom+xml" - baseName = "atom" - -# Custom RSS template of MemE -[outputFormats.SectionsRSS] - mediaType = "application/rss+xml" - baseName = "rss" - -# Hugo’s output control -[outputs] - page = ["HTML"] - home = ["HTML", "SectionsAtom", "SectionsRSS"] - section = ["HTML"] - # Taxonomy - taxonomyTerm = ["HTML"] - # Taxonomy term - taxonomy = ["HTML"] - # Note: https://github.com/gohugoio/hugo/issues/4528#issuecomment-508488859 - -# Maximum number of items in the Atom & RSS feed -[services.rss] - limit = 25 - - - -########################################## -# Menu Settings - -# MemE supports the following four menus: -# 1. main Menu bar below the header -# (center layout) or inside -# the header (flex layout) -# 2. home Links at the bottom of the -# homepage (valid for the -# `poetry` and `footage` -# homepage layout) -# 3. socials Social bar (minimal footer -# in about page) -# 4. links Link bar (minimal footer -# in about page) - -# The configuration instructions in the -# menu are as follows: -# url URL -# name Text (won’t display if it -# is left empty("") or does -# not exist) -# weight Position -# pre Type of link (internal or -# external) -# post Icon (won’t display if it -# is left empty("") or does -# not exist) -# identifier Icon’s class name -# (there are two speacial -# values for header layout -# flex: `theme-switcher`, -# `lang-switcher`) - -# sectionPagesMenu = "home" - -[menu] - ## Menu bar - # [[menu.main]] - # url = "/" - # name = "Home" - # weight = 1 - # pre = "internal" - # post = "home" - [[menu.main]] - url = "/posts/" - name = "Articles" - weight = 2 - pre = "internal" - post = "archive" - [[menu.main]] - url = "/money-markets-risk/" - name = "Risk Analysis" - weight = 3 - pre = "internal" - post = "chess-knight" - [[menu.main]] - url = "/learndefi/" - name = "Discovery" - weight = 4 - pre = "internal" - post = "ethereum" - [[menu.main]] - url = "/about/" - name = "About" - weight = 5 - pre = "internal" - post = "user-circle" - - [[menu.main]] - weight = 6 - identifier = "theme-switcher" - [[menu.main]] - weight = 7 - identifier = "lang-switcher" - - ## Socials - [[menu.socials]] - url = "https://github.com/TokenBrice" - name = "GitHub" - weight = 1 - pre = "external" - [[menu.socials]] - url = "https://t.me/TokenBrice" - name = "Telegram" - weight = 2 - pre = "external" - [[menu.socials]] - url = "https://twitter.com/TokenBrice" - name = "Twitter" - weight = 3 - pre = "external" - - ## Home Menu -# [[menu.home]] -# url = "/categories/defi/" -# name = "DeFi Posts" -# weight = 1 -# pre = "internal" -# post = "ethereum" -# [[menu.home]] -# url = "/categories/blockchain/" -# name = " Blockchain" -# weight = 2 -# pre = "internal" -# post = "chain" - - -## FR Menu bar -# [[languages.fr.menu.main]] -# url = "/" -# name = "Home" -# weight = 1 -# pre = "internal" -# post = "home" -[[languages.fr.menu.main]] - url = "/fr/posts/" - name = "Articles" - weight = 2 - pre = "internal" - post = "news" -[[languages.fr.menu.main]] - url = "/fr/marche-monetaire-risque/" - name = "Risques" - weight = 3 - pre = "internal" - post = "chess-knight" -[[languages.fr.menu.main]] - url = "/fr/defi-decouverte-finance-decentralisee/" - name = "Découverte" - weight = 4 - pre = "internal" - post = "ethereum" -[[languages.fr.menu.main]] - url = "/fr/about/" - name = "À Propos" - weight = 5 - pre = "internal" - post = "user-circle" -[[languages.fr.menu.main]] - weight = 6 - identifier = "theme-switcher" -[[languages.fr.menu.main]] - weight = 7 - identifier = "lang-switcher" - - # [[languages.fr.menu.home]] - # url = "/fr/categories/defi/" - # name = "Articles DeFi" - # weight = 1 - # pre = "internal" - # post = "ethereum" - # [[languages.fr.menu.home]] - # url = "/fr/categories/blockchain/" - # name = " Blockchain" - # weight = 2 - # pre = "internal" - # post = "chain" - - - -########################################## -# Theme Settings - -[params] - ###################################### - # Header - - enableHeader = true - - displayHeaderInHome = true - # Note: invalid for header layout flex - - headerBackground = "linear-gradient(90deg, #f795331a 0, #f370551a 15%, #ef4e7b1a 30%, #a166ab1a 44%, #5073b81a 58%, #1098ad1a 72%, #07b39b1a 86%, #6dba821a 100%)" - # Note: https://developer.mozilla.org/en-US/docs/Web/CSS/background - - headerLayout = "center" - # Note: center or flex - - headerFallbackWidth = "32em" - # Note: by default, MemE uses main-inner’s - # width for `headerWidth`, but in - # some pages (e.g. homepage layout - # poetry, footage), there is no - # main-inner, so you need to give - # a fallback value for this - # situation. - - # Hide menu bar and display a toggle - # on narrow screen? - enableNavToggle = true - - navHeight = "10em" - # Note: menu bar height when it’s - # toggled - - - ###################################### - # Brand Bar (inside header) - - # Use SVG? - siteBrandSVG = true - # If true, put your SVG in the `SVG.toml` - # file and configure the following options. - # Unit: px - siteBrandSVGWidth = 300 - siteBrandSVGHeight = 100 - siteBrandSVGFill = "#cfcbd4" - # If false, your site title will be - # used as the default and displayed as - # text. The font size and font color - # can be configured below. The font - # family can be configured in typography - # section at the bottom of this file. - # Unit: em - siteBrandFontSize = 1 - siteBrandFontColor = "var(--color-contrast-high)" - - - ###################################### - # Menu Bar - - enableMenu = true - # Note: invalid for header layout flex - - displayMenuInHome = true - # Note: invalid for header layout flex - - # Highlight current menu item by section? - activeInSection = true - # Note: if true, the menu item in the - # post page will be highlighted - # also if that post’s section is - # the `url` of that menu item - - - ###################################### - # Homepage Layout - - # For homepage layout, MemE has the - # following four different types: - # 1. poetry A few lines of verse - # 2. footage With fullscreen background videos - # 3. posts List of post’s summary - # 4. page Normal page/post - - homeLayout = "posts" - - ## Poetry - # Verse (Markdown supported) - # {{< relref path=\"/content/about/_index.md\" >}} - homePoetry = ["Welcome traveler,

I hope you'll appreciate your stay in the Ether
[— TokenBrice](/about/),
Driven by one simple goal:
**To shine a light on the Etherial fire.**

[🌌 DeFi Stories](categories/defi/)
[🇫🇷 DeFi France](defifrance)
[👾 Do You Even DeFi?](/dyed/)"] - # Padding of links at the bottom - # Unit: em - homeLinksPadding = 1 - - ## Footage - homePoster = "" - homeVideoWebm = "" - homeVideoMp4 = "" - homeLogo = "" - homeLogoLink = "" - homeTitle = "" - homeMotto = "" - homeDescription = "" - homeKeywords = "" - homeLinksDelimiter = "" - - - ###################################### - # Site Info - - siteLogo = "/img/main/logo.png" - # Note: used for JSON-LD, Open Graph - - siteDescription = "Decentralised Finance (DeFi), Ethereum & building communties in the web3 world. Personal blog of TokenBrice. Content, in EN & FR." - # Note: used for HTML head meta, JSON-LD, - # Open Graph, Atom, RSS - - siteCreatedTime = "2020-05-02T20:17:43+00:00" - # ATTENTION: keep this format, change - # the number - - siteTwitter = "TokenBrice" - # Note: used for Twitter Cards - - - ###################################### - # Category By - - # MemE supports category by: - # 1. sections - # 2. categories - # The sections is based on the site’s - # `content` directory structure; the - # categories is based on the post’s - # Front Matter. First of all, category - # means tree structure (nested, with - # sub-category). To achieve it, Hexo - # uses the second method, while Hugo - # uses the first method. Due to the - # different design philosophy, the - # difference between Hexo and Hugo has - # been made. Therefore, this option - # was designed here to be user friendly - # to those who come from Hexo. But note - # that the second method cannot be - # implemented perfectly in Hugo. I - # recommended you adapt to Hugo’s design - # philosophy if you want to keep the - # tree organization structure of your - # posts. - - categoryBy = "categories" - # ATTENTION: If you set it to `sections`, - # be sure to delete `categories` - # in `taxonomies` at the top - # of this file. Otherwise, - # the categories page will - # be invalid. Also, you need - # to create a new - # `content/categories/_index.md` - # file by yourself. - - - ###################################### - # Categories Page - - # Enable tree structure layout? - enableTree = true - # ATTENTION: This option cannot be - # disabled if you set - # `categoryBy` to `sections` - - # Display title of posts? - displayPosts = true - - # Display count of posts under each - # category? - displayPostsCount = true - - - ###################################### - # Tags Page - - enableTagCloud = true - - fontUnit = "em" - largestFontSize = 2.5 - smallestFontSize = 1 - - - ###################################### - # List Page - - listWidth = 50 - # Note: you can leave it empty("") to - # fallback to the default value: 42 - # Unit: em - - displayListTitle = true - - listDateFormat = "January 2" - # ATTENTION: be sure to follow the - # specified format - # https://gohugo.io/functions/format/ - - listDatePosition = "right" - # Note: left or right - - # Separate the list by month? - groupByMonth = true - - # Enable Chinese zodiac? - chineseZodiac = true - - # Translate the year? (valid for title) - i18nYear = true - # Translate the month? (valid for title) - i18nMonth = true - - - ###################################### - # Taxonomy List Page - - # Note: for categories page, you can - # set `enableTree` to false to - # get a normal taxonomy list - # page; for tags page, you can - # set `enableTagCloud` to false - # to get a normal taxonomy list - # page. - - # Display count of posts in each - # taxonomy term? - displayTaxonomyTermCount = true - - - ###################################### - # Atom & RSS - - # Include full content? - includeContent = true - # Note: If false, only the summary of - # the post will be included. The - # summary, Hugo automatically - # takes the first 70 words (you - # can customize this via - # `summaryLength`) of your content - # as its summary by default. - # Alternatively, you may use the - # (with no whitespace) - # summary divider to split summary - # manually. Or use the `summary` - # variable in post’s Front - # Matter to assign it manually. - # The priority of them: assign - # manually > split manually > - # split automatically. Additionally, - # `description` in post’s Front - # Matter has a higher priority - # than assign manually in the - # custom Atom & RSS template of - # MemE. - - - ###################################### - # Dark Mode - - enableDarkMode = true - - defaultTheme = "light" - # Note: light or dark - - hideThemeToggle = false - hideThemeToggleInHome = false - # Note: If dark mode is enabled and - # theme toggle is hidden, your - # readers may still read your - # blog in dark mode if the - # reader’s system is set to dark - # mode. Similarly, if dark mode - # is enabled and is set to the - # default theme, your readers - # may still read your blog in - # light mode, even if you have - # hidden the theme toggle. - - - ###################################### - # Web App - - # Note: go to https://realfavicongenerator.net/ - # to generate related icons and - # files, unzip after downloading, - # and keep only android-chrome-512x512.png, - # apple-touch-icon.png, mstile-150x150.png, - # safari-pinned-tab.svg, favicon.ico, - # site.webmanifest these files, - # delete the rest. Then move - # these files to the ~/blog/static/icons/ - # directory, move favicon.ico, - # site.webmanifest to the ~/blog/static/ - # directory, and finally rename - # site.webmanifest to manifest.json, - # and check and modify related - # content (the path of the icons). - - themeColor = "#fff" - themeColorDark = "#16171d" - safariMaskColor = "#2a6df4" - msApplicationTileColor = "#fff" - - - ###################################### - # HTML Head Meta, SEO & Social Discovery - - jsonLD = true - openGraph = true - twitterCards = true - # ATTENTION: If you set twitterCards - # to `true`, be sure to - # enable openGraph also. - # Otherwise, the Twitter - # Cards info will be - # incomplete. - - autoDetectImages = true - # Note: It is recommended to enable it. - # Otherwise, you must manually - # specify `images` in post’s - # Front Matter or links you - # shared on social networks or - # APPs will not be able to - # display a summary view with a - # large image. - - - ###################################### - # Service Worker - - # Note: render only in production - # environment - - enableServiceWorker = false - # Note: need third-party support, see - # https://io-oi.me/tech/pwa-via-workbox/ - - - ###################################### - # KaTeX (chemical equation supported) - - enableKaTeX = false - # Note: *global settings* - # `katex` in post’s Front Matter - # has a higher priority than here - - - ###################################### - # MathJax (chemical equation supported) - - enableMathJax = false - # Note: *global settings* - # `mathjax` in post’s Front Matter - # has a higher priority than here - - disableMathJaxMenu = false - - - ###################################### - # Mermaid (https://github.com/mermaid-js/mermaid) - - enableMermaid = false - # Note: *global settings* - # `mermaid` in post’s Front Matter - # has a higher priority than here - - - ###################################### - # Comments - - # Note: render only in production - # environment - - enableComments = false - # Note: *global settings* - # `comments` in post’s Front Matter - # has a higher priority than here - - ## Disqus - enableDisqus = false - disqusShortname = "" - - ## Valine - enableValine = false - valineAppId = "" - valineAppKey = "" - valinePlaceholder = "Just go go" - valineNotify = false - valineVerify = false - valinePath = "" - valineAvatar = "mm" - valineMeta = ["nick", "mail", "link"] - valinePageSize = 10 - valineLang = "en" - valineVisitor = false - valineHighlight = true - avatarForce = false - valineRecordIP = false - valineServerURLs = "" - # Note: https://valine.js.org/ - - ## Utterances - enableUtterances = false - utterancesRepo = "" - utterancesIssueTerm = "pathname" - utterancesTheme = "github-light" - utterancesThemeDark = "photon-dark" - utterancesLabel = "" - # Note: https://utteranc.es/ - - - ###################################### - # Google Analytics - - # Note: render only in production - # environment - - enableGoogleAnalytics = false - - trackingCodeType = "gtag" - # Note: gtag or analytics - - trackingID = "" - - - ###################################### - # Google Site Verification - - googleSiteVerification = "" - - - ###################################### - # Google AdSense - - # Note: render only in production - # environment - - googleAdClient = "" - - ## Auto Ads - enableGoogleAutoAds = false - - ## Ad Units - enableGoogleAdUnits = false - googleAdSlot = "" - - - ###################################### - # Post Settings - - # The color change duration of the - # hyperlink (in seconds) - duration = 0.5 - - # Primary color of light mode - primaryColorLight = "220, 90%, 56%" - # Primary color of dark mode - primaryColorDark = "201, 65%, 62%" - # ATTENTION: only HSL color values ​​are - # supported - - # The content width of the post - postWidth = 50 - # Note: you can leave it empty("") to - # fallback to the default value: 42 - # Unit: em - - # Is the post original? - original = true - # Note: will affect the author and - # copyright information of the - # post - # *global settings* - # `original` in post’s Front Matter - # has a higher priority than here - - - ###################################### - # Post Description - - displayPostDescription = true - - - ###################################### - # Post Meta Info - - enablePostMeta = true - # Note: *global settings* - # `meta` in post’s Front Matter - # has a higher priority than here - - enablePostMetaInHome = true - # Note: valid for the `posts` and - # `page` homepage layout only - - postMetaDateFormat = "2006.1.2" - # ATTENTION: be sure to follow the - # specified format - # https://gohugo.io/functions/format/ - - displayPublishedDate = true - publishedDateIcon = "calendar-alt" - - displayModifiedDate = true - modifiedDateIcon = "calendar-check" - - displayExpiredDate = true - expiredDateIcon = "calendar-times" - - displayCategory = true - categoryIcon = "folder" - categoryDelimiter = "/" - - displayWordCount = true - wordCountIcon = "pencil-alt" - - displayReadingTime = true - readingTimeIcon = "clock" - - displayBusuanziPagePV = false - busuanziPagePVIcon = "eye" - # Note: render only in production - # environment - # Homepage is not supported yet - # https://busuanzi.ibruce.info/ - - - ###################################### - # Markdown Related - - # Open external links in a new tab? - hrefTargetBlank = true - - - ###################################### - # Table of Contents - - enableTOC = false - # Note: *global settings* - # `toc` in post’s Front Matter - # has a higher priority than here - - displayTOCTitle = true - - displayTOCNum = true - # Note: *global settings* - # `tocNum` in post’s Front Matter - # has a higher priority than here - - linkHeadingsToTOC = true - - - ###################################### - # Headings Anchor - - enableHeadingsAnchor = true - - # Level range of headings - headingsOpt = "1-6" - # Note: regex format, default value - # is 1-6, which is 1|2|3|4|5|6, - # which is h1 to h6. - - anchorSymbol = "#" - # Note: the anchorIcon has a higher - # priority than anchorSybol - # Default: § - - anchorIcon = "link" - # Note: you can leave it empty("") to - # fallback to the anchorSymbol - - enableAnchorLink = true - - enableAnchorAutoHide = true - - - ###################################### - # Caption - - enableCaption = true - - captionPrefix = "◎ " - - - ###################################### - # Image Hosting - - # Note: render only in production - # environment - # - # only support absolute URLs - # relative to root, e.g. - # /images/meme.jpg - # (~/blog/static/images/meme.jpg) - - enableImageHost = false - - imageHostURL = "https://example.com/" - - # Replace the image link in the HTML - # head meta also? - headAlso = false - - - ###################################### - # Video Hosting - - # Note: render only in production - # environment - # - # only support absolute URLs - # relative to root, e.g. - # /videos/meme.mp4 - # - # and the HTML code you add must - # start with `