Skip to content

Commit 935fce7

Browse files
Merge pull request steam-bell-92#831 from Alvi24-hub/feature/info-tooltips
feature: add info tooltips (ⓘ) to show project instructions in modal
2 parents fc88a39 + a24b2df commit 935fce7

4 files changed

Lines changed: 616 additions & 5 deletions

File tree

web-app/css/styles.css

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2887,7 +2887,7 @@ body.sidebar-active .hero-controls {
28872887
}
28882888
}
28892889

2890-
..modal-close {
2890+
.modal-close {
28912891
position: absolute;
28922892
top: 1rem;
28932893
right: 1rem;
@@ -5317,3 +5317,143 @@ html[data-theme="dark"] body {
53175317
max-width: 100% !important;
53185318
overflow-x: hidden !important;
53195319
}
5320+
5321+
/* Modal Header*/
5322+
.modal-header {
5323+
display: flex;
5324+
justify-content: flex-end;
5325+
padding: 16px 20px 0;
5326+
}
5327+
5328+
/* Info Button */
5329+
.modal-info {
5330+
width: 36px;
5331+
height: 36px;
5332+
border-radius: 50%;
5333+
background: var(--accent-soft);
5334+
border: 1px solid var(--border-accent);
5335+
color: var(--accent);
5336+
font-size: 1.2rem;
5337+
cursor: pointer;
5338+
transition: all 0.2s ease;
5339+
display: flex;
5340+
align-items: center;
5341+
justify-content: center;
5342+
}
5343+
5344+
.modal-info:hover {
5345+
background: var(--accent);
5346+
color: white;
5347+
transform: scale(1.05);
5348+
}
5349+
5350+
/* Close Button */
5351+
.modal-close {
5352+
width: 36px;
5353+
height: 36px;
5354+
border-radius: 50%;
5355+
background: var(--bg-glass);
5356+
border: 1px solid var(--border);
5357+
color: var(--text-secondary);
5358+
font-size: 1.2rem;
5359+
cursor: pointer;
5360+
display: flex;
5361+
align-items: center;
5362+
justify-content: center;
5363+
}
5364+
5365+
.modal-close:hover {
5366+
background: rgba(239, 68, 68, 0.15);
5367+
color: var(--danger);
5368+
border-color: rgba(239, 68, 68, 0.3);
5369+
}
5370+
5371+
/* Info Modal Popup */
5372+
.info-modal-overlay {
5373+
position: fixed;
5374+
top: 0;
5375+
left: 0;
5376+
width: 100%;
5377+
height: 100%;
5378+
background: rgba(0, 0, 0, 0.7);
5379+
backdrop-filter: blur(8px);
5380+
z-index: 2002;
5381+
display: flex;
5382+
align-items: center;
5383+
justify-content: center;
5384+
visibility: hidden;
5385+
opacity: 0;
5386+
transition: all 0.2s ease;
5387+
}
5388+
5389+
.info-modal-overlay.active {
5390+
visibility: visible;
5391+
opacity: 1;
5392+
}
5393+
5394+
.info-modal-content {
5395+
background: var(--surface);
5396+
border: 1px solid var(--border);
5397+
border-radius: 24px;
5398+
padding: 32px;
5399+
max-width: 400px;
5400+
width: 90%;
5401+
text-align: center;
5402+
transform: scale(0.9);
5403+
transition: transform 0.2s ease;
5404+
}
5405+
5406+
.info-modal-overlay.active .info-modal-content {
5407+
transform: scale(1);
5408+
}
5409+
5410+
.info-modal-close {
5411+
float: right;
5412+
background: none;
5413+
border: none;
5414+
font-size: 24px;
5415+
cursor: pointer;
5416+
color: var(--text-secondary);
5417+
}
5418+
5419+
.info-modal-content h3 {
5420+
margin-top: 0;
5421+
margin-bottom: 20px;
5422+
}
5423+
5424+
.info-modal-content ul {
5425+
text-align: left;
5426+
margin: 20px 0;
5427+
padding-left: 20px;
5428+
}
5429+
5430+
.info-modal-content li {
5431+
margin: 10px 0;
5432+
color: var(--text-secondary);
5433+
line-height: 1.5;
5434+
}
5435+
5436+
.info-modal-btn {
5437+
background: var(--accent);
5438+
color: white;
5439+
border: none;
5440+
padding: 10px 24px;
5441+
border-radius: 30px;
5442+
cursor: pointer;
5443+
font-weight: bold;
5444+
margin-top: 10px;
5445+
}
5446+
5447+
.inline-info-btn {
5448+
background: none;
5449+
border: none;
5450+
font-size: 1.2rem;
5451+
cursor: pointer;
5452+
color: var(--accent);
5453+
margin-left: 10px;
5454+
transition: transform 0.2s ease;
5455+
}
5456+
5457+
.inline-info-btn:hover {
5458+
transform: scale(1.1);
5459+
}

web-app/index.html

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,9 @@ <h2 class="playground-title">🐍 Python Playground</h2>
598598
<!-- ── Project Modal ─────────────────────────────────────────── -->
599599
<div aria-hidden="true" aria-labelledby="modalDialogTitle" aria-modal="true" class="modal" id="projectModal" role="dialog">
600600
<div class="modal-content">
601-
<h2 class="modal-dialog-title visually-hidden" id="modalDialogTitle" tabindex="-1">Interactive project</h2>
602-
<button aria-label="Close project dialog" class="modal-close" id="modalClose" type="button">&times;</button>
601+
<div class="modal-header">
602+
<button aria-label="Close project dialog" class="modal-close" id="modalClose" type="button">&times;</button>
603+
</div>
603604
<div id="modalBody"></div>
604605
</div>
605606
</div>
@@ -708,6 +709,15 @@ <h3>Legal</h3>
708709
Press <kbd>Ctrl</kbd><span>+</span><kbd>K</kbd> to focus search
709710
</div>
710711

712+
<!-- Info Modal Popup -->
713+
<div id="infoModalOverlay" class="info-modal-overlay">
714+
<div class="info-modal-content">
715+
<button class="info-modal-close" id="infoModalClose">&times;</button>
716+
<h3 id="infoModalTitle">How to Play</h3>
717+
<ul id="infoModalList"></ul>
718+
<button class="info-modal-btn" id="infoModalGotIt">Got it</button>
719+
</div>
720+
</div>
711721

712722
</body>
713723
</html>

web-app/js/main.js

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,62 @@ function escapeHtml(str) {
2828
return d.innerHTML;
2929
}
3030

31+
// ============================================
32+
// INFO MODAL FUNCTIONS
33+
// ============================================
34+
35+
function showInfoModal(title, steps) {
36+
var overlay = document.getElementById('infoModalOverlay');
37+
var titleEl = document.getElementById('infoModalTitle');
38+
var listEl = document.getElementById('infoModalList');
39+
40+
if (!overlay || !titleEl || !listEl) return;
41+
42+
titleEl.textContent = title;
43+
listEl.innerHTML = steps.map(function(step) {
44+
return '<li>' + step + '</li>';
45+
}).join('');
46+
47+
overlay.classList.add('active');
48+
49+
function closeModal() {
50+
overlay.classList.remove('active');
51+
closeBtn.removeEventListener('click', closeModal);
52+
gotItBtn.removeEventListener('click', closeModal);
53+
overlay.removeEventListener('click', overlayClick);
54+
}
55+
56+
function overlayClick(e) {
57+
if (e.target === overlay) closeModal();
58+
}
59+
60+
var closeBtn = document.getElementById('infoModalClose');
61+
var gotItBtn = document.getElementById('infoModalGotIt');
62+
63+
closeBtn.addEventListener('click', closeModal);
64+
gotItBtn.addEventListener('click', closeModal);
65+
overlay.addEventListener('click', overlayClick);
66+
}
67+
68+
var currentProjectName = '';
69+
70+
function setupModalInfoButton(projectName) {
71+
currentProjectName = projectName;
72+
var infoBtn = document.getElementById('modalInfoBtn');
73+
if (!infoBtn) return;
74+
75+
// Remove old listener by cloning
76+
var newBtn = infoBtn.cloneNode(true);
77+
infoBtn.parentNode.replaceChild(newBtn, infoBtn);
78+
79+
newBtn.addEventListener('click', function() {
80+
if (typeof getProjectInstructions === 'function') {
81+
var info = getProjectInstructions(currentProjectName);
82+
showInfoModal(info.title, info.steps);
83+
}
84+
});
85+
}
86+
3187
/* ── DOMContentLoaded ──────────────────────────────────────── */
3288
document.addEventListener('DOMContentLoaded', function () {
3389
var html = document.documentElement;
@@ -797,7 +853,6 @@ document.addEventListener('DOMContentLoaded', function () {
797853
function openProjectSafe(name, trigger) {
798854
if (!modal || !modalBody) return;
799855
lastFocusedElement = trigger || document.activeElement;
800-
if (modalTitle) modalTitle.textContent = name || 'Interactive project';
801856
modal.classList.add('active');
802857
modal.setAttribute('aria-hidden', 'false');
803858
var scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
@@ -812,6 +867,50 @@ document.addEventListener('DOMContentLoaded', function () {
812867
modalBody.innerHTML = '<div style="padding:1rem;color:var(--text-secondary)">Project content unavailable.</div>';
813868
}
814869
if (typeof initializeProject === 'function') initializeProject(name);
870+
setupModalInfoButton(name);
871+
872+
// Inject info button next to the title (works for all projects)
873+
var projectContent = modalBody.querySelector('.project-content');
874+
if (projectContent) {
875+
// Try to find the title element (could be h2, or other heading)
876+
var firstHeading = projectContent.querySelector('h2, h3, .resume-analyzer-copy h2, .pet-title');
877+
878+
if (!firstHeading) {
879+
// If no heading found, look for any element with a title-like class
880+
firstHeading = projectContent.querySelector('[class*="title"], [class*="header"] h2');
881+
}
882+
883+
if (firstHeading && !projectContent.querySelector('.inline-info-btn')) {
884+
// Create info button
885+
var infoBtn = document.createElement('button');
886+
infoBtn.className = 'inline-info-btn';
887+
infoBtn.innerHTML = 'ⓘ';
888+
infoBtn.setAttribute('aria-label', 'How to use this project');
889+
890+
// Style the button
891+
infoBtn.style.marginLeft = '12px';
892+
infoBtn.style.background = 'none';
893+
infoBtn.style.border = 'none';
894+
infoBtn.style.fontSize = '1.3rem';
895+
infoBtn.style.cursor = 'pointer';
896+
infoBtn.style.color = 'var(--accent)';
897+
infoBtn.style.verticalAlign = 'middle';
898+
899+
infoBtn.addEventListener('click', function(e) {
900+
e.stopPropagation();
901+
if (typeof getProjectInstructions === 'function') {
902+
var info = getProjectInstructions(name);
903+
showInfoModal(info.title, info.steps);
904+
}
905+
});
906+
907+
// Make heading display inline if it's a block element
908+
if (firstHeading.style.display !== 'inline-block') {
909+
firstHeading.style.display = 'inline-block';
910+
}
911+
firstHeading.appendChild(infoBtn);
912+
}
913+
}
815914
});
816915

817916
removeTrap = trapFocus(modal);
@@ -820,7 +919,7 @@ document.addEventListener('DOMContentLoaded', function () {
820919
if (firstFocusable && typeof firstFocusable.focus === 'function') {
821920
firstFocusable.focus({ preventScroll: true });
822921
}
823-
}
922+
}
824923

825924
function closeProjectSafe() {
826925
if (!modal || !modal.classList.contains('active')) return;

0 commit comments

Comments
 (0)