CSS 가상요소로 툴팁 말풍선 만들기

자바스크립트 도움 없이 CSS만으로도 간편하게 멋진 툴팁을 만들어서 표시할 수 있습니다.

툴팁을 표시하는 버튼 만들기

먼저 툴팁에 표시할 텍스트 내용을 이미지 태그에 추가해야 합니다.

사용자 정의 데이터는 "data-" 헤더를 붙여서 데이터 속성으로 사용합니다.

툴팁으로 표시할 텍스트 내용은 태그의 "data-tooltip" 속성으로 표시하기로 합니다.

<button class="tooltip-link" data-tooltip="제품의 인증관련 추가정보를 볼 수 있습니다."> > 추가정보 </button>

표시하는 툴팁은 CSS 가상 요소로 추가합니다.

추가 태그 없이 요소를 배치할 수 있기 때문에 가장 효율적인 방법입니다.

먼저 버튼을 하나 만듭니다. 이 예는 버튼에 툴팁 메시지를 표시하는 것을 기준으로 만듭니다.

.tooltip-link{
    position: relative;
    background-color: #aad;
    border-radius: 10px;
    border: 3px solid #eee;
    padding: 10px;
    box-sizing: border-box;
}

툴팁 만들기

툴팁은 가상요소를 이용해서 만듭니다.

위치는 정하기 나름이지만 버튼 오른쪽 상단 쪽으로 말풍선처럼 툴팁이 펼쳐지는 모습으로 보이도록 배치합니다.

툴팁의 내용은 버튼 태그의 "data-tooltip" 속성 값을 가져와 표시합니다.

툴팁은 "position: absolute;"로 자리를 차지하지 않으므로 "opacity: 0;"으로 투명하게 해서 보이지만 않게 합니다.

.tooltip-link::before {
    content: attr(data-tooltip); /*툴팁 표시 내용은 버튼의 "data-tooltip" 속성값으로 */
    position: absolute;
    background-color: rgba(0,0,0,0.5);
    color: #fff;
    padding: 10px 7px;
    border-radius: 10px;
    max-width: 300px;
    width: 250%; /* 버튼 너비 250% 크기만큼 툴팁 크기 설정 */
    left: 25%; /* 버튼 왼쪽에서 부터 버튼 너비 25% 이동한 위치에 툴팁 왼쪽이 오게 */
    bottom: 120%; /* 버튼 밑에서 부터 버튼 높이 120% 높이에 툴팁 밑부분이 오게 */
    opacity: 0; /* 기본 상태는 안보이게 */
    transition: all 0.5s linear; /* 나타났다 사라지는 애니메이션 */
}
.tooltip-link:hover::before {
    opacity: 1;
}

툴팁 표시 내용은 CSS attr() 함수를 사용해 요소의 "data-tooltip" 속성 값을 표시하는 것으로 합니다.

attr() 함수는 CSS가 적용된 요소의 속성값을 가져오는 역할을 합니다.

툴팁 위치는 버튼 크기를 기준으로 % 값으로 위치를 잡습니다. 픽셀 값으로 고정 위치를 잡으면 크기가 다른 버튼에 적용하려면 표시 위치를 다시 수정을 하거나, 해당 버튼용으로 클래스를 또 만들어야 합니다. 

버튼 영역의 약간 오른쪽 위에 표시되도록 "left: 25%; bottom: 120%;"로 위치를 잡습니다.

툴팁의 위치는 버튼의 왼쪽 밑에서 얼마나 멀어져있는지를 기준으로 잡아야 합니다. 버튼 크기가 달라질 수 있기 때문에 범용으로 사용할 수 있도록 버튼 크기를 기준으로 퍼센트 값으로 툴팁의 왼쪽과 밑이 버튼 왼쪽과 밑에서 얼마나 떨어져 있는지를 표시해야 합니다.

툴팁의 "left: 25%" 위치는 버튼 왼쪽 끝에서부터 버튼 너비의 25% 만큼 떨어진 위치가 툴팁의 왼쪽 끝이 됩니다.

마찬가지로 "bottom: 120%"는 버튼 밑에서부터 버튼 높이의 120% 만큼 떨어진 위치가 툴팁의 밑부분이 됩니다.

툴팁이 2줄, 또는 3줄 이상으로 높이가 높아질 수도 있기 때문에 툴팁의 Y좌표 기준은 툴팁의 밑부분 위치가 기준이 되어야 합니다. 그래야 줄 수가 늘어나도 위쪽으로 늘어나고 툴팁의 밑부분은 버튼을 기준으로 동일한 위치에 있게 됩니다.

툴팁 표시 위치

data-tooltip 속성 값이 없으면

문제가 생깁니다.

앞서 만든 툴팁은 그럴듯하게 잘 동작하지만, HTML 태그의 툴팁 표시용 내용을 담는 "data-tooltip" 속성이 없거나 속성 값이 없으면 다음과 같이 빈 툴팁 박스가 표시됩니다.

모양이 좋지 않을 뿐만 아니라 뭔가 에러가 발생한 것 같은 상태로 보입니다. 웹페이지에서 이런 화면이 연출되는 것은 보기 좋지 않습니다.

CSS 셀렉터를 추가해서 속성이 없거나 속성 값이 없으면 툴팁이 표시되지 않도록 처리를 해야 합니다.

중요합니다.

속성이 없는 것과 속성 값이 없는 것은 다른 상태이기 때문에 두 가지 상태 모두에 대해서 대응이 되도록 CSS 셀렉터를 작성해야 합니다.

CSS 선택자를 다음과 같이 수정합니다.

"[data-tooltip]" 은 속성이 태그에 존재하면 선택되는 CSS 선택자입니다.

"[data-tooltip=""]"은 속성 값이 없으면 선택되는 CSS 선택자입니다. 속성 값이 있을 때 선택해야 하므로 ":not([data-tooltip=""])"과 같이 부정 선택자로 "data-tooltip" 값이 있을 때만 선택되도록 변경합니다.

.tooltip-link[data-tooltip]:not([data-tooltip=""])::before

속성과 속성 값을 선택하는 CSS 선택자에 대한 내용은 다음 글을 참고하시기 바랍니다.

> 클래스와 속성 값을 문자열로 검색하는 5가지 CSS 선택자 사용법

CSS 셀렉터를 추가해서 속성과 속성 값이 모두 있을 때만 표시되도록 다음과 같이 가상 요소 클래스를 수정합니다.

그리고, 마우스 호버가 되었을 때도 동일하게 CSS 선택자가 동작해야 하기 때문에 마우스 호버 가상 클래스에도 동일하게 CSS 선택자를 적용해야 합니다.

이때 특별히 주의해야 하는 부분이 있는데 가상 클래스가 가상 요소보다 앞에 나와야 합니다.

.tooltip-link:hover[data-tooltip]:not([data-tooltip=""])::before

다음처럼 반대로 표시하면 마우스 호버가 동작하지 않습니다. CSS 선택자 우선순위가 정해지는 중요한 부분이므로 꼭 위와 같이 적용해야 합니다.

.tooltip-link::before:hover[data-tooltip]:not([data-tooltip=""])

툴팁 말풍선 방향 팁 추가

충분히 쓸만한 툴팁이지만 왠지 말풍선 같아 보이지 않고, 어느 버튼의 툴팁인지를 명확하게 구분되지가 않습니다.

말풍선 방향 팁이 있으면 어느 버튼을 마우스 호버 한 것인지 구분이 쉬워지므로 조금 더 사용자 친화적인 UI가 될 수 있습니다.

방향 팁도 가상 요소로 추가합니다. "::before" 가상 요소는 툴팁에 사용했으므로 "::after" 가상 요소로 방향 팁을 추가합니다.

추가한 방향 팁도 요소의 "data-tooltip" 속성과 속성 값 유무에 따라 말풍선처럼 동작해야 하므로 CSS 셀렉터를 동일하게 적용해야 합니다.

.tooltip-link[data-tooltip]:not([data-tooltip=""])::after {
    content: '';
    border-width: 6px;
    border-style: solid;
    border-color: transparent;
    border-top-color: rgba(0,0,0,0.5); /* 50% 투명한 검정색 6px 테두리선 */
    width: 0; /* 가상 요소의 크기가 0이기 때문에 테두리선이 삼각형 모양이 됨 */
    height: 0;
    position: absolute;
    left: 50%; /* 버튼 너비 50% 크기만큼 오른쪽으로 이동 */
    bottom: 100%; /* 버튼 높이만큼 위로 방향 팁 밑 부분 이동 */
    transform: translate(-50%, 0);
    opacity: 0;
    transition: all 0.5s linear;
}

최종 완성된 CSS는 다음과 같습니다.

button {
    margin: 60px;
}
.tooltip-link{
    position: relative;
    background-color: #aad;
    border-radius: 10px;
    border: 3px solid #eee;
    padding: 10px;
    box-sizing: border-box;
}
.tooltip-link[data-tooltip]:not([data-tooltip=""])::before {
    content: attr(data-tooltip);
    position: absolute;
    background-color: rgba(0,0,0,0.5);
    color: #fff;
    padding: 10px 7px;
    border-radius: 10px;
    max-width: 300px;
    width: 250%;
    left: 25%;
    bottom: 120%;
    opacity: 0;
    transition: all 0.5s linear;
}
.tooltip-link[data-tooltip]:not([data-tooltip=""])::after {
    content: '';
    border-width: 6px;
    border-style: solid;
    border-color: transparent;
    border-top-color: rgba(0,0,0,0.5);
    width: 0;
    height: 0;
    display: inline-block;
    position: absolute;
    left: 50%;
    bottom: 90%;
    transform: translate(-50%, 0);
    opacity: 0;
    transition: all 0.5s linear;
}
.tooltip-link:hover[data-tooltip]:not([data-tooltip=""])::before
, .tooltip-link:hover[data-tooltip]:not([data-tooltip=""])::after {
    opacity: 1;
}