diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba6e2d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +robots.txt +node_modules/ +yarn-error.log +yarn.lock diff --git a/assets/css/screen.css b/assets/css/screen.css index d32d20a..6047740 100644 --- a/assets/css/screen.css +++ b/assets/css/screen.css @@ -792,6 +792,84 @@ hr { } } +.anchor-link { + position: relative; + opacity: 0; + padding: 8px; + margin-left: 4px; + color: var(--ghost-accent-color); + border: none; + background: none; + cursor: pointer; + transition: opacity 0.3s ease; + min-width: 44px; + min-height: 44px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +/* Tooltip styles */ +.anchor-link::before { + content: attr(data-tooltip); + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%); + padding: 6px 10px; + background: var(--color-darker-gray); + color: white; + font-size: 14px; + white-space: nowrap; + border-radius: 4px; + opacity: 0; + visibility: hidden; + transition: opacity 0.2s ease, visibility 0.2s ease; +} + +/* Show tooltip on hover */ +.anchor-link:hover::before { + opacity: 1; + visibility: visible; +} + +/* When copied, change tooltip text */ +.anchor-link.copied::before { + content: attr(data-tooltip-copied); +} + +@media (hover: hover) { + .anchor-link { + opacity: 0; + } + + h1:hover .anchor-link, + h2:hover .anchor-link, + h3:hover .anchor-link, + h4:hover .anchor-link, + h5:hover .anchor-link, + h6:hover .anchor-link, + .anchor-link:focus { + opacity: 1; + } +} + +@media (hover: none) { + .anchor-link { + opacity: 0.8; + } +} + +.anchor-link.copied { + animation: pulse 0.4s ease; +} + +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.2); } + 100% { transform: scale(1); } +} + /* Custom CTA /* ---------------------------------------------------------- */ diff --git a/assets/js/main.js b/assets/js/main.js index 7a5235d..2162f5c 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -52,3 +52,53 @@ function initParallax() { (function () { pagination(true, initParallax); })(); + +(function (window, document) { + const TOOLTIP_TEXTS = { + default: 'Copy link to this section', + copied: 'Link copied!' + }; + + var addAnchors = () => { + var headings = document.querySelectorAll('.gh-content h1, .gh-content h2, .gh-content h3, .gh-content h4, .gh-content h5, .gh-content h6') + headings.forEach((heading) => { + heading.insertAdjacentHTML('beforeend', ` + + `) + + const anchor = heading.querySelector('.anchor-link'); + anchor.addEventListener('click', async (e) => { + e.preventDefault(); + const url = new URL(window.location.href); + url.hash = heading.id; + + try { + await navigator.clipboard.writeText(url.toString()); + showCopiedFeedback(anchor); + } catch (err) { + console.error('Failed to copy:', err); + anchor.setAttribute('aria-label', 'Failed to copy link'); + } + }); + }); + } + + function showCopiedFeedback(element) { + element.classList.add('copied'); + element.setAttribute('aria-label', TOOLTIP_TEXTS.copied); + element.setAttribute('data-tooltip', TOOLTIP_TEXTS.copied); + + setTimeout(() => { + element.classList.remove('copied'); + element.setAttribute('aria-label', TOOLTIP_TEXTS.default); + element.setAttribute('data-tooltip', TOOLTIP_TEXTS.default); + }, 2000); + } + + document.addEventListener('DOMContentLoaded', addAnchors) +})(window, document);