블로그 위젯 디자인 9종 — 공유·후기·지도 임베드 카드
블로그 위젯 디자인은 본문 옆이나 아래에 따로 붙어 공유·후기·문의 같은 한 가지 일을 대신하는 작은 카드다. 실제 유튜브·X·구글맵 API 를 불러오지 않고도 그 모양만 CSS 로 흉내 내면, 코드를 몰라도 복붙 몇 줄로 진짜처럼 보이는 카드를 붙일 수 있다.
자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip
- 01 소셜 공유 버튼 로우
- 02 영상 임베드 재생 버튼 오버레이
- 03 X 포스트 임베드 카드 목업
- 04 후기 임베드 위젯
- 05 지도·위치 임베드 카드
- 06 팟캐스트 플레이어 임베드 위젯
- 07 깃허브 저장소 통계 카드
- 08 뉴스레터 구독 임베드 위젯
- 09 라이브 채팅 버블 런처
아홉 개는 인기순이 아니라 방문자가 글을 읽는 동안 실제로 맞닥뜨리는 순서로 놓았다. 글 아래 공유 줄(01)을 지나 눈을 붙잡는 영상 카드(02)를 보고, 신뢰를 굳히는 X 포스트(03)와 후기(04)를 거쳐 찾아오게 만드는 위치 카드(05)에 닿는다. 그다음 팟캐스트(06)와 깃허브(07)로 더 볼거리를 주고, 떠나기 전 뉴스레터(08)로 붙잡고, 마지막까지 화면에 남아 있는 채팅 버블(09)로 끝난다.
01소셜 공유 버튼 로우
핑크·민트·라일락·하늘색 원형 버튼 네 개가 scale 로 한 번씩 튀어 오르며 순서대로 나타난다. 글 하단 공유 줄이나 상품 상세의 SNS 공유 자리에 쓴다.
.btn { animation: pop $duration $easing infinite; }
.pink { background: $subject-pink; animation-delay: 0s; }
.mint { background: $subject-mint; animation-delay: .15s; }
.lilac { background: $subject-lilac; animation-delay: .3s; }
.sky { background: $subject-sky; animation-delay: .45s; }
@keyframes pop {
0%, 40%, 100% { transform: scale(1); }
15% { transform: scale(1.16); }
25% { transform: scale(.94); }
}
링크 버튼은 navigator.clipboard.writeText() 로 실제 복사를 시도하고, 좋아요 버튼은 aria-pressed 를 토글해 눌림 상태를 남긴다.
02영상 임베드 재생 버튼 오버레이
어두운 썸네일 위 재생 버튼 둘레로 흰 링이 레이더 핑처럼 커지며 옅어진다. 실제 유튜브 iframe 을 불러오는 게 아니라, 로딩 전 자리표시자로 쓰는 CSS 카드다.
.ring {
border: 2px solid rgba(255, 255, 255, .5);
animation: ping $duration $easing infinite;
}
@keyframes ping {
0% { opacity: .55; transform: scale(.8); }
70%, 100% { opacity: 0; transform: scale(1.35); }
}
누르면 아이콘이 재생↔일시정지로 바뀌고, requestAnimationFrame 으로 진행바가 실제로 채워진다(진짜 영상 파일은 없다).
03X 포스트 임베드 카드 목업
하트 아이콘이 숨쉬듯 scale·opacity 를 오르내려 누를 수 있다는 걸 알린다. 진짜 X API 는 한 줄도 부르지 않는 순수 CSS 카드라, 후기 페이지에 SNS 반응 캡처 대신 걸어둔다.
.heart { animation: breathe $duration $easing infinite; }
@keyframes breathe {
0%, 100% { transform: scale(1); opacity: .5; }
50% { transform: scale(1.45); opacity: 1; }
}
.likeActive .heart { animation: bump 220ms $ease-pop; }
좋아요·리포스트 버튼을 누르면 카운트가 실제로 오르내리고 색이 바뀐다 — 화면에 보이는 숫자는 전부 이 카드 안에서만 도는 값이다.
04후기 임베드 위젯
별 다섯 개가 왼쪽부터 순서대로 튀어 오르며 채워진다. 후기 모음 페이지의 카드형 리뷰나 랜딩의 고객 후기 섹션에 쓴다.
.star { animation: star $duration $easing infinite; }
@keyframes star {
0% { transform: scale(0); opacity: 0; }
12% { transform: scale(1.3); opacity: 1; }
20%, 100% { transform: scale(1); opacity: 1; }
}
도움이 됐어요 버튼은 aria-pressed 로 눌림 상태를 남기고, 누를 때마다 카운트가 실제로 오른다.
05지도·위치 임베드 카드
지도 위 핀이 위에서 떨어지듯 나타나 살짝 튕기고 자리를 잡는다. 매장·사무실 소개 페이지의 위치 카드, 문의 페이지의 오시는 길에 쓴다.
.pin { animation: drop $duration $easing infinite; }
@keyframes drop {
0% { transform: translate(-50%, -220%); opacity: 0; }
35% { transform: translate(-50%, -100%); opacity: 1; }
46% { transform: translate(-50%, -108%); }
56%, 100% { transform: translate(-50%, -100%); }
}
길찾기 버튼을 누르면 거리·소요시간 패널이 aria-expanded 와 함께 실제로 열린다 — 진짜 지도 타일은 없고, 도로 두 줄만 CSS 로 그렸다.
06팟캐스트 플레이어 임베드 위젯
여덟 개 파형 막대가 서로 다른 배수로 어긋나며 오르내려 진짜 재생 중처럼 보인다. 블로그 글 안 팟캐스트 회차 카드, 사이드바의 최신 에피소드 위젯에 쓴다.
.wave span { animation: bar #{$duration * 3} ease-out infinite alternate; }
.wave.is-paused span { animation-play-state: paused; }
@keyframes bar {
from { transform: scaleY(.3); }
to { transform: scaleY(1); }
}
재생 버튼을 누르면 파형이 실제로 멈췄다가 다시 움직인다 — animation-play-state 하나로 재생 상태와 애니메이션을 묶었다.
07깃허브 저장소 통계 카드
별 아이콘이 눌러보라는 듯 좌우로 짧게 흔들린다. 실제 GitHub API 를 호출하지 않는 순수 CSS 카드로, 오픈소스 프로젝트 소개 글의 저장소 카드나 포트폴리오의 코드 링크에 쓴다.
.icStar { animation: wiggle $duration $easing infinite; transform-origin: 50% 90%; }
@keyframes wiggle {
0% { transform: rotate(0) scale(1); }
20% { transform: rotate(-16deg) scale(1.15); }
40% { transform: rotate(12deg) scale(1.05); }
60% { transform: rotate(-10deg) scale(1.15); }
100% { transform: rotate(0) scale(1); }
}
Star 버튼을 누르면 카운트가 실제로 오르내리고 별이 채워진 색으로 바뀐다.
08뉴스레터 구독 임베드 위젯
편지 아이콘이 떠올랐다 가라앉기를 반복한다. 글 하단 구독 유도 카드, 사이드바의 뉴스레터 가입 폼에 쓴다.
.icon { animation: float $duration $easing infinite; }
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-6px); }
}
@media (prefers-reduced-motion: reduce) {
.icon { animation: none; }
}
실제로 이메일 형식을 정규식으로 검사해 통과하면 아이콘이 종이비행기 모양으로 바뀌며 완료 문구가 뜬다.
09라이브 채팅 버블 런처
런처 둘레 흰 링이 레이더처럼 퍼지고 배지 숫자가 숨쉬듯 커졌다 작아진다. 사이트 우하단 고정 문의 버튼, 결제 페이지의 실시간 상담 진입점에 쓴다.
.ring { animation: ping $duration $easing infinite; }
.badge { animation: bounce $duration $ease-pop infinite; }
@keyframes ping {
0% { opacity: .5; transform: scale(.85); }
70%, 100% { opacity: 0; transform: scale(1.25); }
}
실제로 누르면 채팅 패널이 열리고, .focus({ preventScroll: true }) 덕분에 입력창에 포커스가 가도 부모 페이지가 강제로 스크롤되지 않는다.
어디서 깨지는가 — 320px 에서 카드가 화면을 넘긴다
X 포스트 카드(03)와 후기 카드(04)를 처음 만들었을 때는 카드 폭을 280px 로 두고 문단·여백 값도 넉넉하게 잡았다. 블로그설정.json 의 폰 데모 폭 320px 로 Playwright 렌더를 돌려보니 document.body.scrollHeight 가 200px 예산을 14px·2px 씩 넘겼다 — document.documentElement 만 재면 이 초과분을 놓친다(스테이지 CSS 가 overflow: hidden 을 걸어 스크롤바 자체가 안 생기기 때문). 카드 폭을 256px 로 줄이고 문단·버튼 사이 여백을 토큰 한 단계씩($sp-4→$sp-3, $sp-3→$sp-2) 낮추자 정확히 200px 에 맞았다. 지도 카드(05)는 지도 영역 높이를 120px 에서 88px 로 줄인 게 더 컸다. 반경·간격 규칙은 border-radius 에 $r-* 토큰만, gap·padding·margin 의 px 값에는 4px 배수만 쓰게 강제돼 있어 재생 아이콘을 1~2px 옮기려던 margin-left 도 전부 transform: translateX() 로 바꿔야 했다. 아홉 개 zip 을 열 때 요구하는 암호는 xc2tmqdq 인데, 소문자·숫자만 섞인 짧은 문자열이라 대문자 없이 그대로 옮겨 붙이면 된다.
접근성
아홉 개 전부 prefers-reduced-motion: reduce 에서 반복 애니메이션이 꺼지고 정지된 모양만 남는다 — 02번 재생 링과 09번 채팅 버블 링은 opacity: 0 으로 고정해 링이 멈춘 채로 떠 있지 않다. 버튼마다 aria-label·aria-pressed·aria-expanded 를 붙여 스크린리더가 지금 상태(좋아요를 눌렀는지, 패널이 열렸는지)를 읽을 수 있고, 채팅 버블(09)의 입력창 포커스는 { preventScroll: true } 로 걸어 화면이 갑자기 움직이지 않는다. 운영체제에서 이 값을 어디서 켜는지는 MDN 의 prefers-reduced-motion 문서 를 참고한다.
FAQ
이 카드들로 진짜 유튜브·X·지도가 열리나요?
아니다. 9개 전부 실제 API·iframe 을 부르지 않는 CSS·바닐라 JS 목업이다. 02번 영상 카드는 재생 버튼을 누르면 진행바만 채워지고, 03번 X 카드는 좋아요·리포스트 숫자가 이 카드 안에서만 오르내린다. 진짜 임베드가 필요하면 각 서비스의 공식 embed 코드를 그 자리에 넣고, 이 카드의 CSS 는 로딩 전 자리표시자로 쓰면 된다. 더 많은 임베드 관련 패턴은 사이트 검색 에서 "임베드"로 찾아볼 수 있다.
색과 문구를 우리 브랜드에 맞게 바꿀 수 있나요?
된다. 대부분의 데모는 SCSS 맨 위 $color 변수만 바꾸면 애니메이션 색이 따라가고, $duration·$easing 은 속도와 리듬을 조절한다. 텍스트(회사명·주소·후기 문구)는 index.html 안 문자열을 직접 고치면 되고, React 버전은 각 컴포넌트의 props(title·address·quote 등)로 같은 걸 한다.
320px 초과 여부는 어떻게 직접 확인하나요?
브라우저 개발자 도구에서 뷰포트를 320px 로 좁힌 뒤 콘솔에 document.documentElement.scrollWidth·document.body.scrollWidth 를 각각 찍어 window.innerWidth 와 비교하면 된다. documentElement 만 재면 이 글의 함정 섹션처럼 overflow: hidden 뒤에 숨은 초과분을 놓친다. 이 사이트가 데모를 어떻게 만들고 검증하는지는 소개 페이지 에 정리돼 있다.