09눌러 확대되는 미니 지도
구석의 작은 지도에 현재 보는 영역이 사각형으로 움직이고, 누르면 그 지도가 오버레이로 확대되어 크게 열립니다.
쓰임큰 지도를 둘러볼 때 전체 현황을 함께 보여줄 때
transform-originoverlayaria-expanded

<main class="stage">
<div class="mm is-demo">
<div class="mm__map">
<span class="mm__block mm__block--a" aria-hidden="true"></span>
<span class="mm__block mm__block--b" aria-hidden="true"></span>
<span class="mm__block mm__block--c" aria-hidden="true"></span>
<span class="mm__you" aria-hidden="true"><svg viewBox="0 0 256 256" width="18" height="18"><g fill="currentColor"><path d="M234.35 129L152 152l-23 82.35a8 8 0 0 1-15.21.27l-65.28-176a8 8 0 0 1 10.12-10.16l176 65.28a8 8 0 0 1-.28 15.26" opacity=".2"/><path d="M237.33 106.21L61.41 41l-.16-.05a16 16 0 0 0-20.35 20.3a1 1 0 0 0 .05.16l65.26 175.92A15.77 15.77 0 0 0 121.28 248h.3a15.77 15.77 0 0 0 15-11.29l.06-.2l21.84-78l78-21.84l.2-.06a16 16 0 0 0 .62-30.38Zm-87.49 38.09a8 8 0 0 0-5.54 5.54l-23 82.16l-.06-.17L56 56l175.82 65.22l.16.06Z"/></g></svg></span>
<div class="mm__panel" id="mm-panel">
<span class="mm__panel-view" aria-hidden="true"></span>
<p class="mm__panel-title">
<span class="mm__panel-ico" aria-hidden="true"><svg viewBox="0 0 256 256" width="12" height="12"><g fill="currentColor"><path d="M96 40v168l-56 24V64Zm120 24l-56-24v168l56 24Z" opacity=".2"/><path d="M228.92 49.69a8 8 0 0 0-6.86-1.45l-61.13 15.28l-61.35-30.68a8 8 0 0 0-5.52-.6l-64 16A8 8 0 0 0 24 56v144a8 8 0 0 0 9.94 7.76l61.13-15.28l61.35 30.68A8 8 0 0 0 160 224a8 8 0 0 0 1.94-.24l64-16A8 8 0 0 0 232 200V56a8 8 0 0 0-3.08-6.31M104 52.94l48 24v126.12l-48-24Zm-64 9.31l48-12v127.5l-48 12Zm176 131.5l-48 12V78.25l48-12Z"/></g></svg></span>
<span class="mm__panel-txt">전체 지도</span>
</p>
</div>
<button class="mm__mini" type="button" id="mm-btn" aria-expanded="false" aria-controls="mm-panel" aria-label="미니 지도 펼치기">
<span class="mm__view" aria-hidden="true"></span>
<span class="mm__ico" aria-hidden="true"><svg viewBox="0 0 256 256" width="12" height="12"><g fill="currentColor"><path d="M208 48v160H48V48Z" opacity=".2"/><path d="M216 48v48a8 8 0 0 1-16 0V67.31l-42.34 42.35a8 8 0 0 1-11.32-11.32L188.69 56H160a8 8 0 0 1 0-16h48a8 8 0 0 1 8 8M98.34 146.34L56 188.69V160a8 8 0 0 0-16 0v48a8 8 0 0 0 8 8h48a8 8 0 0 0 0-16H67.31l42.35-42.34a8 8 0 0 0-11.32-11.32M208 152a8 8 0 0 0-8 8v28.69l-42.34-42.35a8 8 0 0 0-11.32 11.32L188.69 200H160a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8M67.31 56H96a8 8 0 0 0 0-16H48a8 8 0 0 0-8 8v48a8 8 0 0 0 16 0V67.31l42.34 42.35a8 8 0 0 0 11.32-11.32Z"/></g></svg></span>
</button>
</div>
<p class="mm__hint">미니 지도를 눌러 펼치고 Esc 로 접어요</p>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-base;
$easing: $ease-spring;
$color: $subject-blue;
@include stage($stage-ink);
// 가짜 지도 캔버스 — 외부 지도 API·이미지 금지. CSS 그라디언트만 (249 아홉 항목 공통 규격)
$map-land: #efe9dc;
$map-road: #fffdf7;
$map-block: #e2dac9;
$map-park: #c9e0bd;
$map-water: #bcd9ea;
.mm {
box-sizing: border-box;
width: min(480px, 88vw);
display: flex;
flex-direction: column;
gap: $sp-2;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
// 지도 배경은 움직이지 않는다 — 움직이는 건 영역 사각형과 확대 오버레이뿐
.mm__map {
position: relative;
overflow: hidden; // 오버레이를 지도 상자 안에 가둔다 (밖으로 삐져나오면 scrollHeight 에 잡힌다)
border-radius: $r-card;
width: 100%;
height: clamp(104px, 34vw, 160px);
box-shadow: $shadow-raised;
background:
radial-gradient(circle at 28% 28%, $map-park 0 40px, transparent 40px),
linear-gradient(122deg, transparent 0 56%, $map-water 56% 63%, transparent 63%),
repeating-linear-gradient(90deg, $map-road 0 7px, transparent 7px 31px),
repeating-linear-gradient(0deg, $map-road 0 7px, transparent 7px 27px),
$map-land;
}
.mm__block {
position: absolute;
display: block;
background: $map-block;
border-radius: $r-xs;
}
.mm__block--a { left: 8%; top: 62%; width: 16%; height: 24%; }
.mm__block--b { left: 44%; top: 16%; width: 18%; height: 24%; }
.mm__block--c { left: 62%; top: 56%; width: 14%; height: 20%; }
.mm__you {
position: absolute;
left: 18%;
top: 40%;
display: block;
transform: translate(-50%, -50%);
color: $color;
filter: drop-shadow(0 2px 3px rgba(23, 20, 26, .3));
}
.mm__you svg { display: block; }
// ── 미니 지도 (구석의 작은 버튼) ────────────────────────
.mm__mini {
position: absolute;
right: $sp-2;
bottom: $sp-2;
z-index: 2;
box-sizing: border-box;
width: 30%;
height: 40%;
padding: 0;
border: 2px solid rgba(255, 255, 255, .85);
border-radius: $r-sm;
cursor: pointer;
overflow: hidden;
box-shadow: $shadow-press;
background:
repeating-linear-gradient(90deg, $map-road 0 3px, transparent 3px 12px),
repeating-linear-gradient(0deg, $map-road 0 3px, transparent 3px 11px),
$map-land;
transition: border-color $duration $easing;
}
.mm__mini:focus-visible { outline: 2px solid $color; outline-offset: 2px; }
.mm.is-open .mm__mini { border-color: $color; }
.mm__ico {
position: absolute;
right: 2px;
bottom: 2px;
display: inline-flex;
padding: $sp-1;
border-radius: $r-xs;
background: rgba(255, 255, 255, .9);
color: rgba(23, 20, 26, .78);
transition: transform $duration $easing;
}
.mm__ico svg { display: block; }
.mm.is-open .mm__ico { transform: rotate(180deg); }
// "지금 보는 영역" 사각형
.mm__view, .mm__panel-view {
position: absolute;
box-sizing: border-box;
border: 2px solid $color;
border-radius: $r-xs;
background: rgba(47, 109, 246, .16);
}
.mm__view { left: 8%; top: 12%; width: 34%; height: 34%; }
.mm__panel-view { left: 10%; top: 16%; width: 30%; height: 32%; }
// ── 확대 오버레이 — 미니 지도 자리(오른쪽 아래)에서 자란다 ──
.mm__panel {
position: absolute;
right: $sp-2;
bottom: $sp-2;
z-index: 1;
box-sizing: border-box;
width: 66%;
height: 78%;
padding: $sp-2;
border-radius: $r-card;
overflow: hidden;
box-shadow: $shadow-raised;
background:
radial-gradient(circle at 72% 70%, $map-park 0 26px, transparent 26px),
repeating-linear-gradient(90deg, $map-road 0 5px, transparent 5px 22px),
repeating-linear-gradient(0deg, $map-road 0 5px, transparent 5px 20px),
$map-land;
transform-origin: 100% 100%; // 규칙8: 미니맵 자리 모서리 확대
transform: scale(.3);
opacity: 0;
pointer-events: none;
transition: transform $duration $easing, opacity $duration $easing;
}
.mm.is-open .mm__panel { transform: scale(1); opacity: 1; pointer-events: auto; }
.mm__panel-title {
position: relative;
display: inline-flex;
align-items: center;
gap: $sp-1;
margin: 0;
padding: $sp-1 $sp-2;
border-radius: $r-pill;
background: rgba(255, 255, 255, .92);
color: rgba(23, 20, 26, .9);
font-size: 10px;
font-weight: 700;
line-height: 14px;
white-space: nowrap;
}
.mm__panel-ico { display: inline-flex; color: $color; }
.mm__panel-ico svg { display: block; }
.mm__hint {
margin: 0;
font-size: 10px;
line-height: 14px;
font-weight: 600;
letter-spacing: .02em;
color: rgba(255, 255, 255, .58);
}
// ── 자동 루프: 영역 사각형이 왕복하고 미니 지도가 열렸다 닫힌다 (0% 와 100% 가 같은 상태) ──
.mm.is-demo .mm__view, .mm.is-demo .mm__panel-view {
animation-name: mm-pan;
animation-duration: $dur-loop;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
}
@keyframes mm-pan {
0% { transform: translate(0, 0); }
50% { transform: translate(108%, 68%); }
100% { transform: translate(0, 0); }
}
.mm.is-demo .mm__panel {
animation-name: mm-open;
animation-duration: $dur-loop;
animation-timing-function: $easing;
animation-iteration-count: infinite;
}
@keyframes mm-open {
0% { opacity: 0; transform: scale(.3); }
24% { opacity: 0; transform: scale(.3); }
40% { opacity: 1; transform: scale(1); }
66% { opacity: 1; transform: scale(1); }
84% { opacity: 0; transform: scale(.3); }
100% { opacity: 0; transform: scale(.3); }
}
.mm.is-demo .mm__ico {
animation-name: mm-flip;
animation-duration: $dur-loop;
animation-timing-function: $easing;
animation-iteration-count: infinite;
}
@keyframes mm-flip {
0% { transform: rotate(0deg); }
24% { transform: rotate(0deg); }
40% { transform: rotate(180deg); }
66% { transform: rotate(180deg); }
84% { transform: rotate(0deg); }
100% { transform: rotate(0deg); }
}
// 320×200 — 무대 패딩(25.6px)을 빼면 174px. 지도 108.8 + 힌트 14, 간격은 0 으로 내린다
@media (max-width: 340px) {
.mm { gap: 0; }
.mm__hint { margin-top: $sp-1; }
}
@media (prefers-reduced-motion: reduce) {
.mm.is-demo .mm__view,
.mm.is-demo .mm__panel-view,
.mm.is-demo .mm__panel,
.mm.is-demo .mm__ico { animation: none !important; }
.mm__panel, .mm__ico, .mm__mini { transition: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.mm {
box-sizing: border-box;
width: min(480px, 88vw);
display: flex;
flex-direction: column;
gap: 8px;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
.mm__map {
position: relative;
overflow: hidden;
border-radius: 16px;
width: 100%;
height: clamp(104px, 34vw, 160px);
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
background: radial-gradient(circle at 28% 28%, #c9e0bd 0 40px, transparent 40px), linear-gradient(122deg, transparent 0 56%, #bcd9ea 56% 63%, transparent 63%), repeating-linear-gradient(90deg, #fffdf7 0 7px, transparent 7px 31px), repeating-linear-gradient(0deg, #fffdf7 0 7px, transparent 7px 27px), #efe9dc;
}
.mm__block {
position: absolute;
display: block;
background: #e2dac9;
border-radius: 4px;
}
.mm__block--a {
left: 8%;
top: 62%;
width: 16%;
height: 24%;
}
.mm__block--b {
left: 44%;
top: 16%;
width: 18%;
height: 24%;
}
.mm__block--c {
left: 62%;
top: 56%;
width: 14%;
height: 20%;
}
.mm__you {
position: absolute;
left: 18%;
top: 40%;
display: block;
transform: translate(-50%, -50%);
color: #2f6df6;
filter: drop-shadow(0 2px 3px rgba(23, 20, 26, 0.3));
}
.mm__you svg {
display: block;
}
.mm__mini {
position: absolute;
right: 8px;
bottom: 8px;
z-index: 2;
box-sizing: border-box;
width: 30%;
height: 40%;
padding: 0;
border: 2px solid rgba(255, 255, 255, 0.85);
border-radius: 8px;
cursor: pointer;
overflow: hidden;
box-shadow: 0 6px 12px rgba(23, 20, 26, 0.32);
background: repeating-linear-gradient(90deg, #fffdf7 0 3px, transparent 3px 12px), repeating-linear-gradient(0deg, #fffdf7 0 3px, transparent 3px 11px), #efe9dc;
transition: border-color 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.mm__mini:focus-visible {
outline: 2px solid #2f6df6;
outline-offset: 2px;
}
.mm.is-open .mm__mini {
border-color: #2f6df6;
}
.mm__ico {
position: absolute;
right: 2px;
bottom: 2px;
display: inline-flex;
padding: 4px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.9);
color: rgba(23, 20, 26, 0.78);
transition: transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.mm__ico svg {
display: block;
}
.mm.is-open .mm__ico {
transform: rotate(180deg);
}
.mm__view, .mm__panel-view {
position: absolute;
box-sizing: border-box;
border: 2px solid #2f6df6;
border-radius: 4px;
background: rgba(47, 109, 246, 0.16);
}
.mm__view {
left: 8%;
top: 12%;
width: 34%;
height: 34%;
}
.mm__panel-view {
left: 10%;
top: 16%;
width: 30%;
height: 32%;
}
.mm__panel {
position: absolute;
right: 8px;
bottom: 8px;
z-index: 1;
box-sizing: border-box;
width: 66%;
height: 78%;
padding: 8px;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
background: radial-gradient(circle at 72% 70%, #c9e0bd 0 26px, transparent 26px), repeating-linear-gradient(90deg, #fffdf7 0 5px, transparent 5px 22px), repeating-linear-gradient(0deg, #fffdf7 0 5px, transparent 5px 20px), #efe9dc;
transform-origin: 100% 100%;
transform: scale(0.3);
opacity: 0;
pointer-events: none;
transition: transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.mm.is-open .mm__panel {
transform: scale(1);
opacity: 1;
pointer-events: auto;
}
.mm__panel-title {
position: relative;
display: inline-flex;
align-items: center;
gap: 4px;
margin: 0;
padding: 4px 8px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.92);
color: rgba(23, 20, 26, 0.9);
font-size: 10px;
font-weight: 700;
line-height: 14px;
white-space: nowrap;
}
.mm__panel-ico {
display: inline-flex;
color: #2f6df6;
}
.mm__panel-ico svg {
display: block;
}
.mm__hint {
margin: 0;
font-size: 10px;
line-height: 14px;
font-weight: 600;
letter-spacing: 0.02em;
color: rgba(255, 255, 255, 0.58);
}
.mm.is-demo .mm__view, .mm.is-demo .mm__panel-view {
animation-name: mm-pan;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
}
@keyframes mm-pan {
0% {
transform: translate(0, 0);
}
50% {
transform: translate(108%, 68%);
}
100% {
transform: translate(0, 0);
}
}
.mm.is-demo .mm__panel {
animation-name: mm-open;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
}
@keyframes mm-open {
0% {
opacity: 0;
transform: scale(0.3);
}
24% {
opacity: 0;
transform: scale(0.3);
}
40% {
opacity: 1;
transform: scale(1);
}
66% {
opacity: 1;
transform: scale(1);
}
84% {
opacity: 0;
transform: scale(0.3);
}
100% {
opacity: 0;
transform: scale(0.3);
}
}
.mm.is-demo .mm__ico {
animation-name: mm-flip;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
}
@keyframes mm-flip {
0% {
transform: rotate(0deg);
}
24% {
transform: rotate(0deg);
}
40% {
transform: rotate(180deg);
}
66% {
transform: rotate(180deg);
}
84% {
transform: rotate(0deg);
}
100% {
transform: rotate(0deg);
}
}
@media (max-width: 340px) {
.mm {
gap: 0;
}
.mm__hint {
margin-top: 4px;
}
}
@media (prefers-reduced-motion: reduce) {
.mm.is-demo .mm__view,
.mm.is-demo .mm__panel-view,
.mm.is-demo .mm__panel,
.mm.is-demo .mm__ico {
animation: none !important;
}
.mm__panel, .mm__ico, .mm__mini {
transition: none;
}
}// 실제 동작: 미니 지도를 누르면(마우스 클릭 · Enter/Space) aria-expanded 가 켜지며 오버레이가 구석에서 자라고, Escape 로 접힌다.
var root = document.querySelector('.mm');
var btn = document.getElementById('mm-btn');
function toggle(on) {
root.classList.remove('is-demo'); // 사람이 조작하면 자동 루프를 끈다 (237 선례)
root.classList.toggle('is-open', on);
btn.setAttribute('aria-expanded', String(on));
}
btn.addEventListener('click', function () { toggle(!root.classList.contains('is-open')); });
btn.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && root.classList.contains('is-open')) {
toggle(false);
btn.focus({ preventScroll: true });
}
});import { useEffect, useRef, useState } from "react";
import type { CSSProperties } from "react";
import styles from "./MiniMapExpand.module.scss";
export type MiniMapExpandProps = {
/** 확대 오버레이 제목 */
panelTitle?: string;
/** 미니 지도 버튼의 스크린리더 설명 */
buttonLabel?: string;
/** 지도 아래 안내 문구 */
hint?: string;
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
type KeyEvt = { key: string; preventDefault: () => void };
export default function MiniMapExpand({
panelTitle = "Whole map",
buttonLabel = "Expand the mini map",
hint = "Tap the mini map to expand, Esc to collapse",
duration = 300,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#2f6df6",
}: MiniMapExpandProps) {
const [demo, setDemo] = useState(true);
const [open, setOpen] = useState(false);
const btnRef = useRef<HTMLButtonElement>(null);
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
function toggle(on: boolean) {
setDemo(false); // 사람이 조작하면 자동 루프를 끈다
setOpen(on);
}
useEffect(() => {
function onKey(e: KeyboardEvent) {
if (e.key === "Escape" && open) {
toggle(false);
btnRef.current?.focus({ preventScroll: true });
}
}
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [open]);
return (
<div className={`${styles.mm} ${demo ? styles.isDemo : ""} ${open ? styles.isOpen : ""}`} style={vars}>
<div className={styles.map}>
<span className={`${styles.block} ${styles.blockA}`} aria-hidden="true" />
<span className={`${styles.block} ${styles.blockB}`} aria-hidden="true" />
<span className={`${styles.block} ${styles.blockC}`} aria-hidden="true" />
<span className={styles.you} aria-hidden="true">
<svg viewBox="0 0 256 256" width="18" height="18"><g fill="currentColor"><path d="M234.35 129L152 152l-23 82.35a8 8 0 0 1-15.21.27l-65.28-176a8 8 0 0 1 10.12-10.16l176 65.28a8 8 0 0 1-.28 15.26" opacity=".2" /><path d="M237.33 106.21L61.41 41l-.16-.05a16 16 0 0 0-20.35 20.3a1 1 0 0 0 .05.16l65.26 175.92A15.77 15.77 0 0 0 121.28 248h.3a15.77 15.77 0 0 0 15-11.29l.06-.2l21.84-78l78-21.84l.2-.06a16 16 0 0 0 .62-30.38Zm-87.49 38.09a8 8 0 0 0-5.54 5.54l-23 82.16l-.06-.17L56 56l175.82 65.22l.16.06Z" /></g></svg>
</span>
<div className={styles.panel} id="mm-panel">
<span className={styles.panelView} aria-hidden="true" />
<p className={styles.panelTitle}>
<span className={styles.panelIco} aria-hidden="true">
<svg viewBox="0 0 256 256" width="12" height="12"><g fill="currentColor"><path d="M96 40v168l-56 24V64Zm120 24l-56-24v168l56 24Z" opacity=".2" /><path d="M228.92 49.69a8 8 0 0 0-6.86-1.45l-61.13 15.28l-61.35-30.68a8 8 0 0 0-5.52-.6l-64 16A8 8 0 0 0 24 56v144a8 8 0 0 0 9.94 7.76l61.13-15.28l61.35 30.68A8 8 0 0 0 160 224a8 8 0 0 0 1.94-.24l64-16A8 8 0 0 0 232 200V56a8 8 0 0 0-3.08-6.31M104 52.94l48 24v126.12l-48-24Zm-64 9.31l48-12v127.5l-48 12Zm176 131.5l-48 12V78.25l48-12Z" /></g></svg>
</span>
<span>{panelTitle}</span>
</p>
</div>
<button
ref={btnRef}
type="button"
className={styles.mini}
aria-expanded={open}
aria-controls="mm-panel"
aria-label={buttonLabel}
onClick={() => toggle(!open)}
onKeyDown={(e: KeyEvt) => {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggle(!open); }
}}
>
<span className={styles.view} aria-hidden="true" />
<span className={styles.ico} aria-hidden="true">
<svg viewBox="0 0 256 256" width="12" height="12"><g fill="currentColor"><path d="M208 48v160H48V48Z" opacity=".2" /><path d="M216 48v48a8 8 0 0 1-16 0V67.31l-42.34 42.35a8 8 0 0 1-11.32-11.32L188.69 56H160a8 8 0 0 1 0-16h48a8 8 0 0 1 8 8M98.34 146.34L56 188.69V160a8 8 0 0 0-16 0v48a8 8 0 0 0 8 8h48a8 8 0 0 0 0-16H67.31l42.35-42.34a8 8 0 0 0-11.32-11.32M208 152a8 8 0 0 0-8 8v28.69l-42.34-42.35a8 8 0 0 0-11.32 11.32L188.69 200H160a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8M67.31 56H96a8 8 0 0 0 0-16H48a8 8 0 0 0-8 8v48a8 8 0 0 0 16 0V67.31l42.34 42.35a8 8 0 0 0 11.32-11.32Z" /></g></svg>
</span>
</button>
</div>
<p className={styles.hint}>{hint}</p>
</div>
);
}09 눌러 확대되는 미니 지도 — 측정 누적면적 20.717% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 지도 ui 디자인 9종 — 누르면 핀이 떨어지는 위치 선택 화면
이 데모가 속한 글
지도 ui 디자인 9종 — 누르면 핀이 떨어지는 위치 선택 화면지도 ui 디자인은 배달·예약 화면에서 "어디로 보낼까"를 지도 위에서 고르게 만드는 화면 설계입니다. 아홉 개 전부 지도 API 없이 CSS 격자로 그린 가짜 지도 위에서 돌아가고, 첫 번째는 누르면 핀이 떨어지는 이 글의 데모 전부 보기


