|
144 | 144 | cursor: not-allowed; |
145 | 145 | } |
146 | 146 |
|
| 147 | + /* ── Custom tooltip (JS-positioned, appended to body) ── */ |
| 148 | + .ui-tooltip { |
| 149 | + position: fixed; |
| 150 | + pointer-events: none; |
| 151 | + z-index: 99999; |
| 152 | + background: var(--color-surface); |
| 153 | + color: var(--color-text); |
| 154 | + padding: 6px 10px; |
| 155 | + border-radius: 6px; |
| 156 | + font-size: 0.72rem; |
| 157 | + font-family: var(--font-body); |
| 158 | + font-weight: 400; |
| 159 | + line-height: 1.4; |
| 160 | + white-space: nowrap; |
| 161 | + border: 1px solid var(--color-border); |
| 162 | + box-shadow: 0 4px 16px rgba(0,0,0,0.35); |
| 163 | + opacity: 0; |
| 164 | + transition: opacity 0.15s ease; |
| 165 | + } |
| 166 | + .ui-tooltip.visible { opacity: 1; } |
| 167 | + |
147 | 168 | #app-main { |
148 | 169 | display: flex; |
149 | 170 | position: relative; |
|
634 | 655 | .btn-icon { min-width: 34px; min-height: 34px; font-size: 0.85rem; } |
635 | 656 | /* Make dropdown arrow easier to see on mobile */ |
636 | 657 | .custom-select-arrow { font-size: 0.9rem; } |
| 658 | + /* Hide custom tooltips on touch devices */ |
| 659 | + .ui-tooltip { display: none !important; } |
637 | 660 | #quiz-panel { |
638 | 661 | position: absolute; |
639 | 662 | top: auto; bottom: 0; left: 0; right: 0; |
|
688 | 711 | <div class="domain-selector" hidden aria-label="Select knowledge domain"></div> |
689 | 712 | </div> |
690 | 713 | <div class="header-right"> |
691 | | - <button id="trophy-btn" class="btn-icon" aria-label="My areas of expertise" title="My areas of expertise" disabled> |
| 714 | + <button id="trophy-btn" class="btn-icon" aria-label="My areas of expertise" data-tooltip="My areas of expertise" disabled> |
692 | 715 | <i class="fa-solid fa-trophy"></i> |
693 | 716 | </button> |
694 | | - <button id="suggest-btn" class="btn-icon" aria-label="Suggest articles to learn" title="Suggest articles to maximally boost my knowledge" disabled> |
| 717 | + <button id="suggest-btn" class="btn-icon" aria-label="Suggest articles to learn" data-tooltip="Suggested learning" disabled> |
695 | 718 | <i class="fa-solid fa-graduation-cap"></i> |
696 | 719 | </button> |
697 | | - <button id="share-btn" class="btn-icon" aria-label="Share your knowledge map" title="Share your knowledge map"> |
| 720 | + <button id="share-btn" class="btn-icon" aria-label="Share your knowledge map" data-tooltip="Share"> |
698 | 721 | <i class="fa-solid fa-share-nodes"></i> |
699 | 722 | </button> |
700 | | - <button id="about-btn" class="btn-icon" aria-label="About this project" title="About this project"> |
| 723 | + <button id="about-btn" class="btn-icon" aria-label="About this project" data-tooltip="About"> |
701 | 724 | <i class="fa-solid fa-circle-info"></i> |
702 | 725 | </button> |
703 | 726 | </div> |
@@ -739,7 +762,7 @@ <h2>Map out (an approximation of) everything you know!</h2> |
739 | 762 | <section id="map-container" aria-label="Interactive Heatmap" tabindex="0"></section> |
740 | 763 |
|
741 | 764 | <!-- Quiz Panel Toggle --> |
742 | | - <button id="quiz-toggle" class="quiz-toggle-btn" hidden aria-label="Open quiz panel" title="Press to show/hide the questions"> |
| 765 | + <button id="quiz-toggle" class="quiz-toggle-btn" hidden aria-label="Open quiz panel" data-tooltip="Show/hide questions"> |
743 | 766 | <i class="fa-solid fa-chevron-left"></i> |
744 | 767 | </button> |
745 | 768 |
|
@@ -864,6 +887,70 @@ <h1>JavaScript Required</h1> |
864 | 887 | <!-- Feature Detection (T066) - runs before ES modules to detect unsupported browsers --> |
865 | 888 | <script src="feature-detection.js"></script> |
866 | 889 |
|
| 890 | + <!-- Global custom tooltip system for [data-tooltip] elements --> |
| 891 | + <script> |
| 892 | + (function() { |
| 893 | + var tip = document.createElement('div'); |
| 894 | + tip.className = 'ui-tooltip'; |
| 895 | + document.body.appendChild(tip); |
| 896 | + var showTimer = null; |
| 897 | + var DELAY = 200; |
| 898 | + |
| 899 | + function show(el) { |
| 900 | + var text = el.getAttribute('data-tooltip'); |
| 901 | + if (!text) return; |
| 902 | + tip.textContent = text; |
| 903 | + // Measure and position |
| 904 | + tip.style.opacity = '0'; |
| 905 | + tip.classList.add('visible'); |
| 906 | + var r = el.getBoundingClientRect(); |
| 907 | + var tw = tip.offsetWidth; |
| 908 | + var th = tip.offsetHeight; |
| 909 | + // Default: centered above the element |
| 910 | + var left = r.left + r.width / 2 - tw / 2; |
| 911 | + var top = r.top - th - 8; |
| 912 | + // If clipped at top, show below |
| 913 | + if (top < 4) top = r.bottom + 8; |
| 914 | + // Keep within horizontal viewport |
| 915 | + if (left < 4) left = 4; |
| 916 | + if (left + tw > window.innerWidth - 4) left = window.innerWidth - tw - 4; |
| 917 | + tip.style.left = left + 'px'; |
| 918 | + tip.style.top = top + 'px'; |
| 919 | + tip.style.opacity = ''; |
| 920 | + } |
| 921 | + |
| 922 | + function hide() { |
| 923 | + clearTimeout(showTimer); |
| 924 | + showTimer = null; |
| 925 | + tip.classList.remove('visible'); |
| 926 | + } |
| 927 | + |
| 928 | + function tooltipTarget(e) { |
| 929 | + var t = e.target; |
| 930 | + if (!t || !t.closest) return null; |
| 931 | + return t.closest('[data-tooltip]'); |
| 932 | + } |
| 933 | + |
| 934 | + document.addEventListener('pointerenter', function(e) { |
| 935 | + var el = tooltipTarget(e); |
| 936 | + if (!el) return; |
| 937 | + clearTimeout(showTimer); |
| 938 | + showTimer = setTimeout(function() { show(el); }, DELAY); |
| 939 | + }, true); |
| 940 | + |
| 941 | + document.addEventListener('pointerleave', function(e) { |
| 942 | + var el = tooltipTarget(e); |
| 943 | + if (!el) return; |
| 944 | + hide(); |
| 945 | + }, true); |
| 946 | + |
| 947 | + // Also hide on click (tooltip served its purpose) |
| 948 | + document.addEventListener('pointerdown', hide, true); |
| 949 | + // Hide on scroll |
| 950 | + document.addEventListener('scroll', hide, true); |
| 951 | + })(); |
| 952 | + </script> |
| 953 | + |
867 | 954 | <!-- Vite Entry Point --> |
868 | 955 | <script type="module" src="/src/app.js"></script> |
869 | 956 | </body> |
|
0 commit comments