01一行シェアバー
記事の末尾に丸いアイコンボタン4つとラベルが一行で並び、ホバー・フォーカスでボタンが少し浮き上がり、輪のリングが満ちます。押したボタンは押されたまま残り、選んだことを示します。
使いどころブログ記事の末尾、ニュースレターのアーカイブ下部
scale 링translateY 들림:focus-visible

<div class="stage">
<section class="ir" aria-label="記事のシェア">
<p class="ir__title">今週のデモ雑誌</p>
<span class="ir__row ir__row--full" aria-hidden="true"></span>
<span class="ir__row ir__row--wide" aria-hidden="true"></span>
<span class="ir__row ir__row--mid" aria-hidden="true"></span>
<div class="ir__share">
<p class="ir__label">この記事をシェア</p>
<div class="ir__btns" role="group" aria-label="シェア方法">
<button class="ir__btn" type="button" aria-pressed="false" aria-label="リンクをコピー">
<span class="ir__ring" aria-hidden="true"></span>
<span class="ir__ic"><svg width="22" height="22" 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></span>
</button>
<button class="ir__btn" type="button" aria-pressed="false" aria-label="メールで送る">
<span class="ir__ring" aria-hidden="true"></span>
<span class="ir__ic"><svg width="22" height="22" 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></span>
</button>
<button class="ir__btn" type="button" aria-pressed="false" aria-label="メッセージで送る">
<span class="ir__ring" aria-hidden="true"></span>
<span class="ir__ic"><svg width="22" height="22" 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></span>
</button>
<button class="ir__btn" type="button" aria-pressed="false" aria-label="QRコードを表示">
<span class="ir__ring" aria-hidden="true"></span>
<span class="ir__ic"><svg width="22" height="22" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M112,56v48a8,8,0,0,1-8,8H56a8,8,0,0,1-8-8V56a8,8,0,0,1,8-8h48A8,8,0,0,1,112,56Zm-8,88H56a8,8,0,0,0-8,8v48a8,8,0,0,0,8,8h48a8,8,0,0,0,8-8V152A8,8,0,0,0,104,144Zm96-96H152a8,8,0,0,0-8,8v48a8,8,0,0,0,8,8h48a8,8,0,0,0,8-8V56A8,8,0,0,0,200,48Z" opacity="0.2"/><path d="M104,40H56A16,16,0,0,0,40,56v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,104,40Zm0,64H56V56h48v48Zm0,32H56a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V152A16,16,0,0,0,104,136Zm0,64H56V152h48v48ZM200,40H152a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,64H152V56h48v48Zm-64,72V144a8,8,0,0,1,16,0v32a8,8,0,0,1-16,0Zm80-16a8,8,0,0,1-8,8H184v40a8,8,0,0,1-8,8H144a8,8,0,0,1,0-16h24V144a8,8,0,0,1,16,0v8h24A8,8,0,0,1,216,160Zm0,32v16a8,8,0,0,1-16,0V192a8,8,0,0,1,16,0Z"/></svg></span>
</button>
</div>
</div>
</section>
</div>@use "../../../../site/static/demo-tokens" as *;
// 편 277 공유 팔레트 — 아홉 개가 같은 값을 쓴다.
// 대비 실측(WCAG 2.1 상대 휘도): ink on paper 18.42:1 · slate on paper 7.15:1
// paper on ink 18.42:1 · sky on ink 11.09:1 · ink on sky 11.09:1
$sh-ink: #15131a; // 잉크 무대 / 밝은 면 위 글자
$sh-ink2: #26232e; // 잉크 위 카드·구분
$sh-paper: #ffffff;
$sh-cream: #fdf8f0; // 밝은 바탕
$sh-slate: #5b5566; // 밝은 면 위 보조 글자
$sh-mist: #bdb7c6; // 잉크 위 보조 글자
$sh-amber: $stage-yellow;
$sh-flame: $stage-orange;
$sh-sky: #c8dce4; // 잉크 위 강조 (밝은 하늘)
$sh-deep: #477690; // 밝은 면 위 강조
$duration: $dur-loop; // 버튼 넷이 차례로 들리는 한 바퀴
$easing: $ease-spring; // 들림·링 차오름
$color: $sh-deep; // 누른 버튼의 면 색
@include stage($sh-paper);
.ir {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: $sp-2;
width: clamp(272px, 84vw, 376px);
padding: $sp-4;
border-radius: $r-card;
background: $sh-cream;
box-shadow: $shadow-raised;
}
.ir__title {
margin: 0;
color: $sh-ink;
font-weight: 700; font-size: 15px; line-height: 1.3;
font-family: inherit;
}
// 본문 자리표시 줄 — 장식이라 aria-hidden
.ir__row {
display: block;
height: 8px;
border-radius: $r-pill;
background: #e3ddd2;
}
.ir__row--full { width: 100%; }
.ir__row--wide { width: 88%; }
.ir__row--mid { width: 62%; }
.ir__share {
display: flex;
flex-direction: column;
gap: $sp-2;
margin-top: $sp-2;
padding-top: $sp-3;
border-top: 1px solid #e3ddd2;
}
.ir__label {
margin: 0;
color: $sh-slate; // 크림 위 보조 글자 6.76:1
font-weight: 600; font-size: 12px; line-height: 1;
font-family: inherit;
}
.ir__btns {
display: flex;
gap: $sp-3;
}
.ir__btn {
position: relative;
box-sizing: border-box;
display: grid;
place-items: center;
width: 44px; // 손가락이 닿는 최소 크기
height: 44px;
padding: 0;
border: 0;
border-radius: 50%;
background: $sh-paper;
color: $sh-ink;
cursor: pointer;
box-shadow: $shadow-press;
transition: transform $dur-quick $easing, background-color $dur-quick $easing, color $dur-quick $easing;
}
// 링은 테두리가 아니라 겹쳐 둔 원 — 커져도 레이아웃을 다시 계산하지 않는다
.ir__ring {
position: absolute;
inset: 0;
border: 2px solid $color;
border-radius: 50%;
transform: scale(.72);
opacity: 0;
transition: transform $dur-quick $easing, opacity $dur-quick $easing;
}
.ir__ic { display: grid; place-items: center; }
.ir__ic svg { display: block; }
// 손으로 쓰는 경로 — .is-demo 를 뗀 뒤에도 상태가 전부 보인다
.ir__btn:hover,
.ir__btn:focus-visible {
transform: translateY(-4px);
}
.ir__btn:hover .ir__ring,
.ir__btn:focus-visible .ir__ring {
transform: scale(1);
opacity: 1;
}
.ir__btn:focus-visible {
outline: 2px solid $sh-ink;
outline-offset: 2px;
}
.ir__btn[aria-pressed="true"] {
background: $color; // 딥블루 면 위 흰 아이콘 6.94:1
color: $sh-paper;
}
.ir__btn[aria-pressed="true"] .ir__ring { opacity: 0; }
// ── 자동 재생 ───────────────────────────────────────────────
// 버튼 넷이 차례로 들린다. animation 단축을 쓰면 delay 가 0 으로 리셋되므로 낱개 속성으로,
// 지연 구간에 기본 상태가 한 프레임 번쩍이지 않게 fill-mode: backwards 를 같이 준다
.is-demo .ir__btn {
animation-name: irLift;
animation-duration: $duration;
animation-timing-function: $easing;
animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
.is-demo .ir__ring {
animation-name: irRing;
animation-duration: $duration;
animation-timing-function: $easing;
animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
.is-demo .ir__btn:nth-child(1), .is-demo .ir__btn:nth-child(1) .ir__ring { animation-delay: 0s; }
.is-demo .ir__btn:nth-child(2), .is-demo .ir__btn:nth-child(2) .ir__ring { animation-delay: .18s; }
.is-demo .ir__btn:nth-child(3), .is-demo .ir__btn:nth-child(3) .ir__ring { animation-delay: .36s; }
.is-demo .ir__btn:nth-child(4), .is-demo .ir__btn:nth-child(4) .ir__ring { animation-delay: .54s; }
// 들림 6% · 앉음 14% 의 비대칭 — 물러남이 더 빠르면 아무도 안 들린 장이 대표로 뽑힌다
@keyframes irLift {
0% { transform: translateY(0); }
6%, 22% { transform: translateY(-6px); }
36%, 100% { transform: translateY(0); }
}
@keyframes irRing {
0% { transform: scale(.72); opacity: 0; }
6%, 22% { transform: scale(1); opacity: 1; }
36%, 100% { transform: scale(.72); opacity: 0; }
}
@media (max-width: 340px), (max-height: 220px) {
.ir { gap: $sp-1; padding: $sp-3; }
.ir__title { font-size: 13px; }
.ir__row { height: 6px; }
.ir__share { margin-top: 0; padding-top: $sp-2; }
.ir__btns { gap: $sp-2; }
.ir__btn { width: 40px; height: 40px; }
.ir__ic svg { width: 18px; height: 18px; }
}
@media (prefers-reduced-motion: reduce) {
// .is-demo 쪽 선택자가 (0,2,0) 이라 !important 없이는 진다
.is-demo .ir__btn, .is-demo .ir__ring { animation: none !important; }
.ir__btn, .ir__ring { transition: none !important; }
// 움직임을 끊어도 호버·초점의 링은 그대로 보인다
.ir__btn:hover .ir__ring, .ir__btn:focus-visible .ir__ring { opacity: 1; transform: scale(1); }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffffff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.ir {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 8px;
width: clamp(272px, 84vw, 376px);
padding: 16px;
border-radius: 16px;
background: #fdf8f0;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.ir__title {
margin: 0;
color: #15131a;
font-weight: 700;
font-size: 15px;
line-height: 1.3;
font-family: inherit;
}
.ir__row {
display: block;
height: 8px;
border-radius: 999px;
background: #e3ddd2;
}
.ir__row--full {
width: 100%;
}
.ir__row--wide {
width: 88%;
}
.ir__row--mid {
width: 62%;
}
.ir__share {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 8px;
padding-top: 12px;
border-top: 1px solid #e3ddd2;
}
.ir__label {
margin: 0;
color: #5b5566;
font-weight: 600;
font-size: 12px;
line-height: 1;
font-family: inherit;
}
.ir__btns {
display: flex;
gap: 12px;
}
.ir__btn {
position: relative;
box-sizing: border-box;
display: grid;
place-items: center;
width: 44px;
height: 44px;
padding: 0;
border: 0;
border-radius: 50%;
background: #ffffff;
color: #15131a;
cursor: pointer;
box-shadow: 0 6px 12px rgba(23, 20, 26, 0.32);
transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), background-color 220ms cubic-bezier(0.2, 0.8, 0.2, 1), color 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ir__ring {
position: absolute;
inset: 0;
border: 2px solid #477690;
border-radius: 50%;
transform: scale(0.72);
opacity: 0;
transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ir__ic {
display: grid;
place-items: center;
}
.ir__ic svg {
display: block;
}
.ir__btn:hover,
.ir__btn:focus-visible {
transform: translateY(-4px);
}
.ir__btn:hover .ir__ring,
.ir__btn:focus-visible .ir__ring {
transform: scale(1);
opacity: 1;
}
.ir__btn:focus-visible {
outline: 2px solid #15131a;
outline-offset: 2px;
}
.ir__btn[aria-pressed=true] {
background: #477690;
color: #ffffff;
}
.ir__btn[aria-pressed=true] .ir__ring {
opacity: 0;
}
.is-demo .ir__btn {
animation-name: irLift;
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 .ir__ring {
animation-name: irRing;
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 .ir__btn:nth-child(1), .is-demo .ir__btn:nth-child(1) .ir__ring {
animation-delay: 0s;
}
.is-demo .ir__btn:nth-child(2), .is-demo .ir__btn:nth-child(2) .ir__ring {
animation-delay: 0.18s;
}
.is-demo .ir__btn:nth-child(3), .is-demo .ir__btn:nth-child(3) .ir__ring {
animation-delay: 0.36s;
}
.is-demo .ir__btn:nth-child(4), .is-demo .ir__btn:nth-child(4) .ir__ring {
animation-delay: 0.54s;
}
@keyframes irLift {
0% {
transform: translateY(0);
}
6%, 22% {
transform: translateY(-6px);
}
36%, 100% {
transform: translateY(0);
}
}
@keyframes irRing {
0% {
transform: scale(0.72);
opacity: 0;
}
6%, 22% {
transform: scale(1);
opacity: 1;
}
36%, 100% {
transform: scale(0.72);
opacity: 0;
}
}
@media (max-width: 340px), (max-height: 220px) {
.ir {
gap: 4px;
padding: 12px;
}
.ir__title {
font-size: 13px;
}
.ir__row {
height: 6px;
}
.ir__share {
margin-top: 0;
padding-top: 8px;
}
.ir__btns {
gap: 8px;
}
.ir__btn {
width: 40px;
height: 40px;
}
.ir__ic svg {
width: 18px;
height: 18px;
}
}
@media (prefers-reduced-motion: reduce) {
.is-demo .ir__btn, .is-demo .ir__ring {
animation: none !important;
}
.ir__btn, .ir__ring {
transition: none !important;
}
.ir__btn:hover .ir__ring, .ir__btn:focus-visible .ir__ring {
opacity: 1;
transform: scale(1);
}
}(function () {
var body = document.body;
function hand() { body.classList.remove('is-demo'); } // 사람이 손을 대면 자동 재생을 끈다
document.querySelectorAll('.ir__btn').forEach(function (b) {
b.addEventListener('pointerdown', hand);
b.addEventListener('focusin', hand);
b.addEventListener('click', function () {
hand();
var on = b.getAttribute('aria-pressed') === 'true'; // 누른 자국이 그대로 남는다
b.setAttribute('aria-pressed', on ? 'false' : 'true');
});
});
})();import { useState } from "react";
import type { CSSProperties, ReactNode } from "react";
import styles from "./ShareInlineRow.module.scss";
/* ambient 에 React 의 이벤트 제네릭이 없어 필요한 것만 로컬로 선언한다 */
type ClickEvt = { preventDefault(): void };
export type ShareInlineRowProps = {
/** 버튼 넷의 접근성 이름 */
labels?: string[];
/** 버튼 넷이 차례로 들리는 한 바퀴(ms) */
duration?: number;
easing?: string;
/** 누른 버튼의 면 색 */
color?: string;
/** 격자 미리보기용 자동 재생 */
autoPlay?: boolean;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
/* Phosphor Duotone (MIT) — 사슬 · 봉투 · 말풍선 · QR */
const ICONS: ReactNode[] = [
<svg width="22" height="22" 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="22" height="22" 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="22" height="22" 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="22" height="22" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M112,56v48a8,8,0,0,1-8,8H56a8,8,0,0,1-8-8V56a8,8,0,0,1,8-8h48A8,8,0,0,1,112,56Zm-8,88H56a8,8,0,0,0-8,8v48a8,8,0,0,0,8,8h48a8,8,0,0,0,8-8V152A8,8,0,0,0,104,144Zm96-96H152a8,8,0,0,0-8,8v48a8,8,0,0,0,8,8h48a8,8,0,0,0,8-8V56A8,8,0,0,0,200,48Z" opacity="0.2"/><path d="M104,40H56A16,16,0,0,0,40,56v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,104,40Zm0,64H56V56h48v48Zm0,32H56a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V152A16,16,0,0,0,104,136Zm0,64H56V152h48v48ZM200,40H152a16,16,0,0,0-16,16v48a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,64H152V56h48v48Zm-64,72V144a8,8,0,0,1,16,0v32a8,8,0,0,1-16,0Zm80-16a8,8,0,0,1-8,8H184v40a8,8,0,0,1-8,8H144a8,8,0,0,1,0-16h24V144a8,8,0,0,1,16,0v8h24A8,8,0,0,1,216,160Zm0,32v16a8,8,0,0,1-16,0V192a8,8,0,0,1,16,0Z"/></svg>,
];
export default function ShareInlineRow({
labels = ["링크 복사", "이메일로 보내기", "메시지로 보내기", "QR 코드 보기"],
duration = 2000,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#0f5f8a",
autoPlay = true,
}: ShareInlineRowProps) {
const [picked, setPicked] = useState<number[]>([]);
const [demo, setDemo] = useState(autoPlay);
/* 사람이 손을 대면 자동 재생을 끈다 */
function stopDemo() {
setDemo(false);
}
function applyPick(i: number, e: ClickEvt) {
e.preventDefault();
stopDemo();
setPicked((prev) => (prev.indexOf(i) >= 0 ? prev.filter((k) => k !== i) : prev.concat(i)));
}
const vars: Vars = { "--duration": duration + "ms", "--easing": easing, "--color": color };
return (
<div className={styles.stage + (demo ? " " + styles.isDemo : "")} style={vars}>
<section className={styles.inline} aria-label="글 공유">
<p className={styles.title}>이번 주 데모 잡지</p>
<span className={styles.row + " " + styles.rowFull} aria-hidden="true" />
<span className={styles.row + " " + styles.rowWide} aria-hidden="true" />
<span className={styles.row + " " + styles.rowMid} aria-hidden="true" />
<div className={styles.share}>
<p className={styles.label}>이 글 공유하기</p>
<div className={styles.btns} role="group" aria-label="공유 방법">
{labels.map((name, i) => (
<button
key={name}
className={styles.btn}
type="button"
aria-pressed={picked.indexOf(i) >= 0}
aria-label={name}
onClick={(e: ClickEvt) => applyPick(i, e)}
onPointerDown={stopDemo}
onFocus={stopDemo}
>
<span className={styles.ring} aria-hidden="true" />
<span className={styles.ic}>{ICONS[i]}</span>
</button>
))}
</div>
</div>
</section>
</div>
);
}01 一行シェアバー — 計測 累積面積 4.712% · 動いたフレーム 15/23GODRICH オリジナル · SCSS と React は zip に同梱 · シェアボタン デザイン パターン9選 — コピペで動く
このデモが属する記事
シェアボタン デザイン パターン9選 — コピペで動くシェアボタン デザインは、訪問者がワンタップで記事を外へ送り出す最後の扉の形そのものです。ここに集めた9つは見かけだけの作り物ではなく、押すと本当にコピーされるシェアUI を HTML・CSS・JavaScript で組んだものなので、その この記事のデモをすべて見る


