Skip to content

Commit 51b6c6e

Browse files
committed
add tooltip functionality and styles
1 parent 561482e commit 51b6c6e

4 files changed

Lines changed: 189 additions & 1 deletion

File tree

_sass/modules/layout.scss

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,28 @@
241241
}
242242

243243
.generated-content {
244+
.tooltip-preview {
245+
&__title {
246+
display: inline-block;
247+
padding: 4px 8px;
248+
margin-bottom: 6px;
249+
border-radius: 4px;
250+
background-color: $color--gray-dark;
251+
color: $color--white;
252+
font-size: 0.75rem;
253+
line-height: 1.3;
254+
box-shadow: 0 4px 12px rgba($color--black, 0.2);
255+
}
256+
257+
&__link {
258+
text-decoration: underline;
259+
}
260+
}
261+
262+
a[title] {
263+
text-decoration: underline;
264+
}
265+
244266
h1 {
245267
font-weight: 700;
246268
font-size: 1.25rem; /* 20px */

_sass/modules/tooltips.scss

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
.tooltip-static-wrapper {
2+
position: relative;
3+
display: inline-block;
4+
isolation: isolate;
5+
}
6+
7+
.tooltip-static {
8+
position: absolute;
9+
bottom: calc(100% + 8px);
10+
left: 50%;
11+
transform: translate(-50%, 2px);
12+
display: inline-flex;
13+
align-items: center;
14+
justify-content: center;
15+
padding: 6px 12px;
16+
border-radius: 5px;
17+
border: 1px solid rgba($color--white, 0.1);
18+
background-color: rgba($color--gray-dark, 0.87);
19+
color: mix($color--white, $color--light, 72%);
20+
font-size: 0.78rem;
21+
font-weight: 500;
22+
line-height: 1.25;
23+
box-shadow: 0 6px 18px rgba($color--black, 0.12);
24+
text-align: center;
25+
max-width: 240px;
26+
min-width: 140px;
27+
white-space: normal;
28+
overflow-wrap: anywhere;
29+
letter-spacing: 0.01em;
30+
opacity: 0;
31+
visibility: hidden;
32+
pointer-events: none;
33+
transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease;
34+
35+
@include breakpoint($tablet) {
36+
font-size: 0.82rem;
37+
}
38+
39+
&::after {
40+
content: '';
41+
position: absolute;
42+
left: 50%;
43+
bottom: -6px;
44+
width: 12px;
45+
height: 12px;
46+
background: rgba($color--gray-dark, 0.87);
47+
clip-path: polygon(50% 100%, 0 0, 100% 0);
48+
transform: translateX(-50%);
49+
filter: drop-shadow(0 2px 6px rgba($color--black, 0.1));
50+
}
51+
}
52+
53+
.tooltip-static-wrapper:hover .tooltip-static,
54+
.tooltip-static-wrapper:focus-within .tooltip-static,
55+
.tooltip-static-wrapper.is-visible .tooltip-static {
56+
opacity: 1;
57+
visibility: visible;
58+
transform: translate(-50%, 0);
59+
}

css/style.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
@import 'modules/navigation';
1616
@import 'modules/slack';
1717
@import 'modules/social';
18+
@import 'modules/tooltips';
1819

1920
@import 'pages/homepage';
2021
@import 'pages/post';

js/main.js

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,111 @@ document.addEventListener('DOMContentLoaded', function(event) {
3434
})
3535
});
3636

37-
});
37+
var tooltipLinks = document.querySelectorAll('.generated-content a[title], .tooltip-enabled a[title], a.tooltip-hint');
38+
39+
var openTooltipWrapper = null;
40+
41+
function showTooltip(wrapper) {
42+
if (!wrapper) {
43+
return;
44+
}
45+
46+
if (openTooltipWrapper && openTooltipWrapper !== wrapper) {
47+
hideTooltip(openTooltipWrapper);
48+
}
49+
50+
wrapper.classList.add('is-visible');
51+
openTooltipWrapper = wrapper;
52+
}
53+
54+
function hideTooltip(wrapper) {
55+
if (!wrapper) {
56+
return;
57+
}
58+
59+
wrapper.classList.remove('is-visible');
60+
61+
if (openTooltipWrapper === wrapper) {
62+
openTooltipWrapper = null;
63+
}
64+
}
65+
66+
forEach(tooltipLinks, function(index, link) {
67+
var tooltipText = link.getAttribute('title');
68+
69+
if (!tooltipText || !tooltipText.trim()) {
70+
return;
71+
}
72+
73+
tooltipText = tooltipText.trim();
74+
75+
var wrapper = link.parentElement;
76+
77+
if (!wrapper || !wrapper.classList.contains('tooltip-static-wrapper')) {
78+
wrapper = document.createElement('span');
79+
wrapper.className = 'tooltip-static-wrapper';
80+
link.insertAdjacentElement('beforebegin', wrapper);
81+
wrapper.appendChild(link);
82+
}
83+
84+
var tooltipSpan = wrapper.querySelector('.tooltip-static');
3885

86+
if (!tooltipSpan) {
87+
tooltipSpan = document.createElement('span');
88+
tooltipSpan.className = 'tooltip-static';
89+
wrapper.insertBefore(tooltipSpan, wrapper.firstChild);
90+
}
91+
92+
tooltipSpan.textContent = tooltipText;
93+
tooltipSpan.setAttribute('role', 'tooltip');
94+
95+
var tooltipId = tooltipSpan.id;
96+
97+
if (!tooltipId) {
98+
tooltipId = 'tooltip-' + index + '-' + Math.random().toString(36).slice(2, 8);
99+
tooltipSpan.id = tooltipId;
100+
}
101+
102+
link.setAttribute('data-tooltip', tooltipText);
103+
link.removeAttribute('title');
104+
link.setAttribute('aria-describedby', tooltipSpan.id);
105+
link.classList.add('tooltip-trigger');
106+
107+
link.addEventListener('mouseenter', function() {
108+
showTooltip(wrapper);
109+
});
110+
111+
link.addEventListener('mouseleave', function() {
112+
hideTooltip(wrapper);
113+
});
114+
115+
link.addEventListener('focus', function() {
116+
showTooltip(wrapper);
117+
});
118+
119+
link.addEventListener('blur', function() {
120+
hideTooltip(wrapper);
121+
});
122+
123+
link.addEventListener('touchstart', function(e) {
124+
e.preventDefault();
125+
126+
if (wrapper.classList.contains('is-visible')) {
127+
hideTooltip(wrapper);
128+
} else {
129+
showTooltip(wrapper);
130+
}
131+
}, { passive: false });
132+
});
133+
134+
document.addEventListener('touchstart', function(event) {
135+
if (!openTooltipWrapper) {
136+
return;
137+
}
138+
139+
if (!openTooltipWrapper.contains(event.target)) {
140+
hideTooltip(openTooltipWrapper);
141+
}
142+
});
143+
144+
});

0 commit comments

Comments
 (0)