05Expanding icon share button
A single share button stands alone until pressed, then four platform buttons fan out in a staggered row; press again and they fold back. An expansion attribute reports the open state.
Best forA one-page portfolio with a narrow header, a mobile hero
aria-expandedclip-path: inset()translateX
<div class="stage">
<section class="ex" aria-label="Expand share options">
<div class="ex__bar">
<button class="ex__toggle" type="button" aria-expanded="false" aria-controls="ex-list">
<span class="ex__ic"><svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M152,192V144c-61.4,0-104.61,37.19-121.07,54.72a4,4,0,0,1-6.9-3.18C31.51,130.45,99.19,80,152,80V32l80,80Z" opacity="0.2"/><path d="M237.66,106.35l-80-80A8,8,0,0,0,144,32V72.35c-25.94,2.22-54.59,14.92-78.16,34.91-28.38,24.08-46.05,55.11-49.76,87.37a12,12,0,0,0,20.68,9.58h0c11-11.71,50.14-48.74,107.24-52V192a8,8,0,0,0,13.66,5.65l80-80A8,8,0,0,0,237.66,106.35ZM160,172.69V144a8,8,0,0,0-8-8c-28.08,0-55.43,7.33-81.29,21.8a196.17,196.17,0,0,0-36.57,26.52c5.8-23.84,20.42-46.51,42.05-64.86C99.41,99.77,127.75,88,152,88a8,8,0,0,0,8-8V51.32L220.69,112Z"/></svg></span>
<span class="ex__toggleLabel">Share</span>
</button>
<div class="ex__list" id="ex-list">
<a class="ex__item" href="#" aria-label="Copy link"><svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M218.34,119.6,183.6,154.34a46.58,46.58,0,0,1-44.31,12.26c-.31.34-.62.67-.95,1L103.6,202.34A46.63,46.63,0,1,1,37.66,136.4L72.4,101.66A46.6,46.6,0,0,1,116.71,89.4c.31-.34.62-.67,1-1L152.4,53.66a46.63,46.63,0,0,1,65.94,65.94Z" opacity="0.2"/><path d="M240,88.23a54.43,54.43,0,0,1-16,37L189.25,160a54.27,54.27,0,0,1-38.63,16h-.05A54.63,54.63,0,0,1,96,119.84a8,8,0,0,1,16,.45A38.62,38.62,0,0,0,150.58,160h0a38.39,38.39,0,0,0,27.31-11.31l34.75-34.75a38.63,38.63,0,0,0-54.63-54.63l-11,11A8,8,0,0,1,135.7,59l11-11A54.65,54.65,0,0,1,224,48,54.86,54.86,0,0,1,240,88.23ZM109,185.66l-11,11A38.41,38.41,0,0,1,70.6,208h0a38.63,38.63,0,0,1-27.29-65.94L78,107.31A38.63,38.63,0,0,1,144,135.71a8,8,0,0,0,7.78,8.22H152a8,8,0,0,0,8-7.78A54.86,54.86,0,0,0,144,96a54.65,54.65,0,0,0-77.27,0L32,130.75A54.62,54.62,0,0,0,70.56,224h0a54.28,54.28,0,0,0,38.64-16l11-11A8,8,0,0,0,109,185.66Z"/></svg></a>
<a class="ex__item" href="#" aria-label="Send by mail"><svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M224,56l-96,88L32,56Z" opacity="0.2"/><path d="M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM203.43,64,128,133.15,52.57,64ZM216,192H40V74.19l82.59,75.71a8,8,0,0,0,10.82,0L216,74.19V192Z"/></svg></a>
<a class="ex__item" href="#" aria-label="Send in a message"><svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M224,128A96,96,0,0,1,79.93,211.11h0L42.54,223.58a8,8,0,0,1-10.12-10.12l12.47-37.39h0A96,96,0,1,1,224,128Z" opacity="0.2"/><path d="M128,24A104,104,0,0,0,36.18,176.88L24.83,210.93a16,16,0,0,0,20.24,20.24l34.05-11.35A104,104,0,1,0,128,24Zm0,192a87.87,87.87,0,0,1-44.06-11.81,8,8,0,0,0-4-1.08,7.85,7.85,0,0,0-2.53.42L40,216,52.47,178.6a8,8,0,0,0-.66-6.54A88,88,0,1,1,128,216Zm12-88a12,12,0,1,1-12-12A12,12,0,0,1,140,128Zm-44,0a12,12,0,1,1-12-12A12,12,0,0,1,96,128Zm88,0a12,12,0,1,1-12-12A12,12,0,0,1,184,128Z"/></svg></a>
<a class="ex__item" href="#" aria-label="Open in a new window"><svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M184,80V208a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V80a8,8,0,0,1,8-8H176A8,8,0,0,1,184,80Z" opacity="0.2"/><path d="M224,104a8,8,0,0,1-16,0V59.32l-66.33,66.34a8,8,0,0,1-11.32-11.32L196.68,48H152a8,8,0,0,1,0-16h64a8,8,0,0,1,8,8Zm-40,24a8,8,0,0,0-8,8v72H48V80h72a8,8,0,0,0,0-16H48A16,16,0,0,0,32,80V208a16,16,0,0,0,16,16H176a16,16,0,0,0,16-16V136A8,8,0,0,0,184,128Z"/></svg></a>
</div>
</div>
<p class="ex__hint">In tight spots, keep just one button</p>
</section>
</div>@use "../../../../site/static/demo-tokens" as *;
// 편 277 공유 팔레트 (01 과 같은 값)
$sh-ink: #15131a;
$sh-ink2: #26232e;
$sh-paper: #ffffff;
$sh-mist: #bdb7c6; // 잉크 위 보조 글자 9.43:1
$sh-sky: #c8dce4; // 잉크 위 강조 11.09:1
$duration: $dur-loop; // 펼침 → 머무름 → 접힘 한 바퀴
$easing: $ease-spring;
$color: $sh-sky; // 펼침 버튼 면 색
@include stage($sh-ink);
.ex {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
gap: $sp-4;
width: clamp(288px, 88vw, 400px);
padding: $sp-6 $sp-4;
border-radius: $r-card;
background: $sh-ink2;
box-shadow: $shadow-raised;
}
.ex__bar {
display: flex;
align-items: center;
gap: $sp-2;
}
.ex__toggle {
box-sizing: border-box;
display: flex;
align-items: center;
gap: $sp-2;
height: 44px;
padding: 0 $sp-4;
border: 0;
border-radius: $r-pill;
background: $color;
color: $sh-ink; // 하늘색 면 위 잉크 글자 11.09:1
cursor: pointer;
transition: transform $dur-quick $easing;
}
.ex__toggle:hover { transform: translateY(-2px); }
.ex__toggle:focus-visible {
outline: 2px solid $sh-paper;
outline-offset: 2px;
}
.ex__ic { display: grid; place-items: center; }
.ex__ic svg { display: block; }
.ex__toggleLabel {
font-weight: 700; font-size: 13px; line-height: 1;
font-family: inherit;
}
.ex__list {
display: flex;
gap: $sp-2;
}
// 접힘은 폭을 0 으로 줄이는 게 아니라 왼쪽 끝에서 잘라 두는 것이다 —
// 자리를 미리 잡아 두니 펼쳐도 옆 글자가 밀리지 않는다
.ex__item {
box-sizing: border-box;
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: $sh-ink;
color: $sh-mist;
text-decoration: none;
transform: translateX(-16px);
clip-path: inset(0 100% 0 0 round 50%);
transition: clip-path $dur-base $easing, transform $dur-base $easing, color $dur-base $easing;
}
.ex__item svg { display: block; }
.ex__item:hover, .ex__item:focus-visible { color: $color; }
.ex__item:focus-visible {
outline: 2px solid $sh-paper;
outline-offset: 2px;
}
// 손으로 쓰는 경로 — .is-demo 를 뗀 뒤에도 펼침 상태가 그대로 보인다
.ex.is-open .ex__item {
transform: translateX(0);
clip-path: inset(0 0 0 0 round 50%);
}
.ex.is-open .ex__item:nth-child(1) { transition-delay: 0ms; }
.ex.is-open .ex__item:nth-child(2) { transition-delay: 60ms; }
.ex.is-open .ex__item:nth-child(3) { transition-delay: 120ms; }
.ex.is-open .ex__item:nth-child(4) { transition-delay: 180ms; }
.ex__hint {
margin: 0;
color: $sh-mist;
font-weight: 500; font-size: 12px; line-height: 1;
font-family: inherit;
}
// ── 자동 재생 ───────────────────────────────────────────────
// 넷이 시차로 펼쳐졌다가 접힌다. animation 단축은 delay 를 0 으로 리셋하므로 낱개 속성으로,
// 지연 구간에 접힌 기본 상태가 번쩍이지 않게 fill-mode: backwards 를 같이 준다
.is-demo .ex__item {
animation-name: exFan;
animation-duration: $duration;
animation-timing-function: $easing;
animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
.is-demo .ex__item:nth-child(1) { animation-delay: 0s; }
.is-demo .ex__item:nth-child(2) { animation-delay: .1s; }
.is-demo .ex__item:nth-child(3) { animation-delay: .2s; }
.is-demo .ex__item:nth-child(4) { animation-delay: .3s; }
.is-demo .ex__toggle {
animation-name: exPress;
animation-duration: $duration;
animation-timing-function: $easing;
animation-iteration-count: infinite;
}
// 펼침 12% · 접힘 18%. 접힘은 84% 에 끝난다 — 24프레임의 마지막 장이 95.8% 지점이라
// 복귀를 그 뒤에 두면 어느 프레임에도 안 잡힌다
@keyframes exFan {
0% { clip-path: inset(0 100% 0 0 round 50%); transform: translateX(-16px); color: $sh-mist; }
12%, 62% { clip-path: inset(0 0 0 0 round 50%); transform: translateX(0); color: $color; }
84%, 100% { clip-path: inset(0 100% 0 0 round 50%); transform: translateX(-16px); color: $sh-mist; }
}
@keyframes exPress {
0%, 2% { transform: scale(1); }
6% { transform: scale(.92); }
12%, 100% { transform: scale(1); }
}
@media (max-width: 340px), (max-height: 220px) {
.ex { gap: $sp-2; padding: $sp-3; }
.ex__toggle { height: 40px; padding: 0 $sp-3; }
.ex__toggleLabel { font-size: 12px; }
.ex__item { width: 40px; height: 40px; }
.ex__ic svg, .ex__item svg { width: 18px; height: 18px; }
.ex__hint { font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) {
// .is-demo 쪽 선택자가 (0,2,0) 이라 !important 없이는 진다
.is-demo .ex__item, .is-demo .ex__toggle { animation: none !important; }
.ex__item, .ex__toggle { transition: none !important; }
// 움직임을 끊어도 펼친 상태는 그대로 남는다
.ex.is-open .ex__item { clip-path: inset(0 0 0 0 round 50%); transform: translateX(0); }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #15131a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.ex {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
width: clamp(288px, 88vw, 400px);
padding: 24px 16px;
border-radius: 16px;
background: #26232e;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.ex__bar {
display: flex;
align-items: center;
gap: 8px;
}
.ex__toggle {
box-sizing: border-box;
display: flex;
align-items: center;
gap: 8px;
height: 44px;
padding: 0 16px;
border: 0;
border-radius: 999px;
background: #c8dce4;
color: #15131a;
cursor: pointer;
transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ex__toggle:hover {
transform: translateY(-2px);
}
.ex__toggle:focus-visible {
outline: 2px solid #ffffff;
outline-offset: 2px;
}
.ex__ic {
display: grid;
place-items: center;
}
.ex__ic svg {
display: block;
}
.ex__toggleLabel {
font-weight: 700;
font-size: 13px;
line-height: 1;
font-family: inherit;
}
.ex__list {
display: flex;
gap: 8px;
}
.ex__item {
box-sizing: border-box;
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: #15131a;
color: #bdb7c6;
text-decoration: none;
transform: translateX(-16px);
clip-path: inset(0 100% 0 0 round 50%);
transition: clip-path 300ms cubic-bezier(0.2, 0.8, 0.2, 1), transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1), color 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ex__item svg {
display: block;
}
.ex__item:hover, .ex__item:focus-visible {
color: #c8dce4;
}
.ex__item:focus-visible {
outline: 2px solid #ffffff;
outline-offset: 2px;
}
.ex.is-open .ex__item {
transform: translateX(0);
clip-path: inset(0 0 0 0 round 50%);
}
.ex.is-open .ex__item:nth-child(1) {
transition-delay: 0ms;
}
.ex.is-open .ex__item:nth-child(2) {
transition-delay: 60ms;
}
.ex.is-open .ex__item:nth-child(3) {
transition-delay: 120ms;
}
.ex.is-open .ex__item:nth-child(4) {
transition-delay: 180ms;
}
.ex__hint {
margin: 0;
color: #bdb7c6;
font-weight: 500;
font-size: 12px;
line-height: 1;
font-family: inherit;
}
.is-demo .ex__item {
animation-name: exFan;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
.is-demo .ex__item:nth-child(1) {
animation-delay: 0s;
}
.is-demo .ex__item:nth-child(2) {
animation-delay: 0.1s;
}
.is-demo .ex__item:nth-child(3) {
animation-delay: 0.2s;
}
.is-demo .ex__item:nth-child(4) {
animation-delay: 0.3s;
}
.is-demo .ex__toggle {
animation-name: exPress;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
}
@keyframes exFan {
0% {
clip-path: inset(0 100% 0 0 round 50%);
transform: translateX(-16px);
color: #bdb7c6;
}
12%, 62% {
clip-path: inset(0 0 0 0 round 50%);
transform: translateX(0);
color: #c8dce4;
}
84%, 100% {
clip-path: inset(0 100% 0 0 round 50%);
transform: translateX(-16px);
color: #bdb7c6;
}
}
@keyframes exPress {
0%, 2% {
transform: scale(1);
}
6% {
transform: scale(0.92);
}
12%, 100% {
transform: scale(1);
}
}
@media (max-width: 340px), (max-height: 220px) {
.ex {
gap: 8px;
padding: 12px;
}
.ex__toggle {
height: 40px;
padding: 0 12px;
}
.ex__toggleLabel {
font-size: 12px;
}
.ex__item {
width: 40px;
height: 40px;
}
.ex__ic svg, .ex__item svg {
width: 18px;
height: 18px;
}
.ex__hint {
font-size: 11px;
}
}
@media (prefers-reduced-motion: reduce) {
.is-demo .ex__item, .is-demo .ex__toggle {
animation: none !important;
}
.ex__item, .ex__toggle {
transition: none !important;
}
.ex.is-open .ex__item {
clip-path: inset(0 0 0 0 round 50%);
transform: translateX(0);
}
}(function () {
var body = document.body;
var toggle = document.querySelector('.ex__toggle');
var root = document.querySelector('.ex');
var items = Array.prototype.slice.call(document.querySelectorAll('.ex__item'));
function hand() { body.classList.remove('is-demo'); } // 사람이 손을 대면 자동 재생을 끈다
function setOpen(open) {
root.classList.toggle('is-open', open);
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
items.forEach(function (a) { a.tabIndex = open ? 0 : -1; }); // 접힌 목록은 Tab 순서에서 뺀다
}
toggle.addEventListener('click', function () {
hand();
setOpen(toggle.getAttribute('aria-expanded') !== 'true');
});
toggle.addEventListener('pointerdown', hand);
toggle.addEventListener('focusin', hand);
root.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') return;
hand();
setOpen(false);
toggle.focus({ preventScroll: true });
});
items.forEach(function (a) {
a.addEventListener('click', function (e) { e.preventDefault(); hand(); });
});
setOpen(false);
})();import { useRef, useState } from "react";
import type { CSSProperties, ReactNode } from "react";
import styles from "./ShareExpandButton.module.scss";
/* ambient 에 React 의 이벤트 제네릭이 없어 필요한 것만 로컬로 선언한다 */
type ClickEvt = { preventDefault(): void };
type KeyEvt = { key: string };
type FocusBox = { focus(opts: { preventScroll: boolean }): void };
export type ShareExpandButtonProps = {
/** 펼쳐지는 버튼 넷의 접근성 이름 */
labels?: string[];
/** 펼침 → 머무름 → 접힘 한 바퀴(ms) */
duration?: number;
easing?: string;
/** 펼침 버튼 면 색 */
color?: string;
autoPlay?: boolean;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
/* Phosphor Duotone (MIT) — 사슬 · 봉투 · 말풍선 · 새 창 */
const ICONS: ReactNode[] = [
<svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M218.34,119.6,183.6,154.34a46.58,46.58,0,0,1-44.31,12.26c-.31.34-.62.67-.95,1L103.6,202.34A46.63,46.63,0,1,1,37.66,136.4L72.4,101.66A46.6,46.6,0,0,1,116.71,89.4c.31-.34.62-.67,1-1L152.4,53.66a46.63,46.63,0,0,1,65.94,65.94Z" opacity="0.2"/><path d="M240,88.23a54.43,54.43,0,0,1-16,37L189.25,160a54.27,54.27,0,0,1-38.63,16h-.05A54.63,54.63,0,0,1,96,119.84a8,8,0,0,1,16,.45A38.62,38.62,0,0,0,150.58,160h0a38.39,38.39,0,0,0,27.31-11.31l34.75-34.75a38.63,38.63,0,0,0-54.63-54.63l-11,11A8,8,0,0,1,135.7,59l11-11A54.65,54.65,0,0,1,224,48,54.86,54.86,0,0,1,240,88.23ZM109,185.66l-11,11A38.41,38.41,0,0,1,70.6,208h0a38.63,38.63,0,0,1-27.29-65.94L78,107.31A38.63,38.63,0,0,1,144,135.71a8,8,0,0,0,7.78,8.22H152a8,8,0,0,0,8-7.78A54.86,54.86,0,0,0,144,96a54.65,54.65,0,0,0-77.27,0L32,130.75A54.62,54.62,0,0,0,70.56,224h0a54.28,54.28,0,0,0,38.64-16l11-11A8,8,0,0,0,109,185.66Z"/></svg>,
<svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M224,56l-96,88L32,56Z" opacity="0.2"/><path d="M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM203.43,64,128,133.15,52.57,64ZM216,192H40V74.19l82.59,75.71a8,8,0,0,0,10.82,0L216,74.19V192Z"/></svg>,
<svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M224,128A96,96,0,0,1,79.93,211.11h0L42.54,223.58a8,8,0,0,1-10.12-10.12l12.47-37.39h0A96,96,0,1,1,224,128Z" opacity="0.2"/><path d="M128,24A104,104,0,0,0,36.18,176.88L24.83,210.93a16,16,0,0,0,20.24,20.24l34.05-11.35A104,104,0,1,0,128,24Zm0,192a87.87,87.87,0,0,1-44.06-11.81,8,8,0,0,0-4-1.08,7.85,7.85,0,0,0-2.53.42L40,216,52.47,178.6a8,8,0,0,0-.66-6.54A88,88,0,1,1,128,216Zm12-88a12,12,0,1,1-12-12A12,12,0,0,1,140,128Zm-44,0a12,12,0,1,1-12-12A12,12,0,0,1,96,128Zm88,0a12,12,0,1,1-12-12A12,12,0,0,1,184,128Z"/></svg>,
<svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M184,80V208a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V80a8,8,0,0,1,8-8H176A8,8,0,0,1,184,80Z" opacity="0.2"/><path d="M224,104a8,8,0,0,1-16,0V59.32l-66.33,66.34a8,8,0,0,1-11.32-11.32L196.68,48H152a8,8,0,0,1,0-16h64a8,8,0,0,1,8,8Zm-40,24a8,8,0,0,0-8,8v72H48V80h72a8,8,0,0,0,0-16H48A16,16,0,0,0,32,80V208a16,16,0,0,0,16,16H176a16,16,0,0,0,16-16V136A8,8,0,0,0,184,128Z"/></svg>,
];
export default function ShareExpandButton({
labels = ["링크 복사", "메일로 보내기", "메시지로 보내기", "새 창으로 열기"],
duration = 2000,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#7fd4f5",
autoPlay = true,
}: ShareExpandButtonProps) {
const [demo, setDemo] = useState(autoPlay);
const [open, setOpen] = useState(false);
const toggleRef = useRef<FocusBox>(null) as { current: FocusBox | null };
/* 사람이 손을 대면 자동 재생을 끈다 */
function stopDemo() {
setDemo(false);
}
function applyToggle() {
stopDemo();
setOpen((prev) => !prev);
}
function applyKey(e: KeyEvt) {
if (e.key !== "Escape") return;
stopDemo();
setOpen(false);
if (toggleRef.current) toggleRef.current.focus({ preventScroll: true });
}
const vars: Vars = { "--duration": duration + "ms", "--easing": easing, "--color": color };
return (
<div className={styles.stage + (demo ? " " + styles.isDemo : "")} style={vars}>
<section
className={styles.expand + (open ? " " + styles.isOpen : "")}
aria-label="공유 펼치기"
onKeyDown={applyKey}
>
<div className={styles.bar}>
<button
className={styles.toggle}
type="button"
aria-expanded={open}
onClick={applyToggle}
onPointerDown={stopDemo}
onFocus={stopDemo}
ref={toggleRef as unknown as never}
>
<span className={styles.ic}><svg width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M152,192V144c-61.4,0-104.61,37.19-121.07,54.72a4,4,0,0,1-6.9-3.18C31.51,130.45,99.19,80,152,80V32l80,80Z" opacity="0.2"/><path d="M237.66,106.35l-80-80A8,8,0,0,0,144,32V72.35c-25.94,2.22-54.59,14.92-78.16,34.91-28.38,24.08-46.05,55.11-49.76,87.37a12,12,0,0,0,20.68,9.58h0c11-11.71,50.14-48.74,107.24-52V192a8,8,0,0,0,13.66,5.65l80-80A8,8,0,0,0,237.66,106.35ZM160,172.69V144a8,8,0,0,0-8-8c-28.08,0-55.43,7.33-81.29,21.8a196.17,196.17,0,0,0-36.57,26.52c5.8-23.84,20.42-46.51,42.05-64.86C99.41,99.77,127.75,88,152,88a8,8,0,0,0,8-8V51.32L220.69,112Z"/></svg></span>
<span className={styles.toggleLabel}>공유</span>
</button>
<div className={styles.list}>
{labels.map((name, i) => (
<a
key={name}
className={styles.item}
href="#"
aria-label={name}
tabIndex={open ? 0 : -1}
onClick={(e: ClickEvt) => {
e.preventDefault();
stopDemo();
}}
>
{ICONS[i]}
</a>
))}
</div>
</div>
<p className={styles.hint}>좁은 자리에서는 버튼 하나만 둡니다</p>
</section>
</div>
);
}05 Expanding icon share button — Measured area 5.952% · moving frames 14/23Original by GODRICH · SCSS and React ship in the zip · Social Share Buttons: 9 Copy-Paste UI Patterns
The post this demo lives in
Social Share Buttons: 9 Copy-Paste UI PatternsSocial share buttons are the last door a page leaves through, and the one tap a fake demo gives away. The nine here real See all demos in this post


