diff --git a/docs/update.md b/docs/update.md
index deb992a..62c8133 100644
--- a/docs/update.md
+++ b/docs/update.md
@@ -91,3 +91,10 @@
- **커스텀 아이템 조회 강화**: 사용자 커스텀 아이템 목록에 파일명 검색, `50/200` 단위 페이지네이션, 다운로드 흐름 추가
- **회원 비밀번호 초기화 추가**: 관리자 페이지와 API에서 회원 비밀번호를 직접 재설정할 수 있도록 기능 추가
- **가변 티어 행 지원**: 티어표 에디터에서 `S~D` 고정 5단이 아니라 티어 행을 직접 추가/삭제할 수 있도록 보강
+
+## 2026-03-19 v0.1.14
+- **커스텀 아이템 카드 반응형 수정**: 관리자 아이템 관리 탭의 커스텀 아이템 카드에서 이미지 폭을 유동값으로 조정하고, 텍스트 영역에 `min-width: 0`과 강제 줄바꿈 기준을 추가해 카드 바깥 overflow를 방지
+
+## 2026-03-19 v0.1.15
+- **셀렉트 화살표 여백 정리**: 전역 `select` 스타일에 커스텀 화살표 위치와 오른쪽 여백을 추가해 텍스트와 화살표가 지나치게 붙지 않도록 조정
+- **티어표 다운로드 결과 개선**: `TierEditorView`의 이미지 저장을 Blob 다운로드 방식으로 바꾸고, 캡처 대상을 보드 영역만 포함하는 전용 export 뷰로 분리해 우측 아이템 영역과 편집용 버튼/입력 UI가 저장 이미지에 섞이지 않도록 수정
diff --git a/frontend/src/style.css b/frontend/src/style.css
index 527d4fb..dc26237 100644
--- a/frontend/src/style.css
+++ b/frontend/src/style.css
@@ -54,6 +54,21 @@ body {
margin: 0;
}
+select {
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ background-image:
+ linear-gradient(45deg, transparent 50%, rgba(255, 255, 255, 0.78) 50%),
+ linear-gradient(135deg, rgba(255, 255, 255, 0.78) 50%, transparent 50%);
+ background-position:
+ calc(100% - 18px) calc(50% - 2px),
+ calc(100% - 12px) calc(50% - 2px);
+ background-size: 6px 6px, 6px 6px;
+ background-repeat: no-repeat;
+ padding-right: 36px;
+}
+
h1,
h2 {
font-family: var(--heading);
diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue
index f126944..f62bc72 100644
--- a/frontend/src/views/AdminView.vue
+++ b/frontend/src/views/AdminView.vue
@@ -497,12 +497,10 @@ function fmt(ts) {
-
현재 목록은 사용자 커스텀 이미지 전용입니다. 여기서 보는 항목은 게임 기본 아이템 삭제와 연결되지 않아요.
-
조건에 맞는 커스텀 아이템이 없어요.
@@ -726,6 +724,7 @@ function fmt(ts) {
margin-top: 0;
}
.btn {
+ font-size: 12px;
margin-top: 12px;
padding: 10px 12px;
border-radius: 12px;
@@ -751,9 +750,6 @@ function fmt(ts) {
.btn--ghost {
background: rgba(255, 255, 255, 0.03);
}
-.btn--small {
- width: 100%;
-}
.detailHead {
display: flex;
gap: 12px;
@@ -866,7 +862,7 @@ function fmt(ts) {
.customItemGrid {
margin-top: 14px;
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+ grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.customItemCard {
@@ -874,27 +870,39 @@ function fmt(ts) {
border-radius: 16px;
background: rgba(255, 255, 255, 0.04);
overflow: hidden;
+ display: flex;
+ gap: 12px;
+ align-items: flex-start;
+ padding: 12px;
+ min-width: 0;
}
.customItemCard__image {
- width: min(100%, 150px);
+ width: clamp(88px, 22vw, 150px);
+ flex: 0 1 150px;
aspect-ratio: 1 / 1;
object-fit: cover;
display: block;
- margin: 12px auto 0;
border-radius: 12px;
background: rgba(0, 0, 0, 0.18);
}
.customItemCard__body {
- display: grid;
+ display: flex;
+ flex-direction: column;
gap: 6px;
- padding: 12px;
+ min-width: 0;
+ flex: 1 1 auto;
}
.customItemCard__title {
+ min-width: 0;
+ overflow-wrap: anywhere;
+ word-break: break-word;
font-weight: 900;
}
.customItemCard__meta {
opacity: 0.72;
font-size: 13px;
+ min-width: 0;
+ overflow-wrap: anywhere;
word-break: break-word;
}
.pager {
@@ -979,5 +987,12 @@ function fmt(ts) {
.userList {
grid-template-columns: 1fr;
}
+ .customItemCard {
+ align-items: stretch;
+ }
+ .customItemCard__image {
+ width: clamp(72px, 28vw, 120px);
+ flex-basis: 120px;
+ }
}
diff --git a/frontend/src/views/TierEditorView.vue b/frontend/src/views/TierEditorView.vue
index f503347..f2341da 100644
--- a/frontend/src/views/TierEditorView.vue
+++ b/frontend/src/views/TierEditorView.vue
@@ -30,10 +30,12 @@ const description = ref('')
const isPublic = ref(false)
const error = ref('')
const isSaving = ref(false)
+const isExporting = ref(false)
const ownerId = ref('')
const isDragActive = ref(false)
const boardEl = ref(null)
+const exportBoardEl = ref(null)
const groupListEl = ref(null)
const poolEl = ref(null)
const groupDropEls = ref({})
@@ -211,11 +213,25 @@ function onDropFiles(event) {
async function downloadImage() {
if (!boardEl.value) return
- const dataUrl = await htmlToImage.toPng(boardEl.value, { pixelRatio: 2, backgroundColor: '#0b1220' })
- const a = document.createElement('a')
- a.href = dataUrl
- a.download = `${(title.value || gameName.value || 'tierlist').trim()}.png`
- a.click()
+ isExporting.value = true
+ await nextTick()
+
+ try {
+ const targetEl = exportBoardEl.value || boardEl.value
+ const blob = await htmlToImage.toBlob(targetEl, { pixelRatio: 2, backgroundColor: '#0b1220' })
+ if (!blob) throw new Error('image_export_failed')
+
+ const url = URL.createObjectURL(blob)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = `${(title.value || gameName.value || 'tierlist').trim()}.png`
+ document.body.appendChild(a)
+ a.click()
+ a.remove()
+ URL.revokeObjectURL(url)
+ } finally {
+ isExporting.value = false
+ }
}
async function uploadPendingCustomItems() {
@@ -375,15 +391,22 @@ onUnmounted(() => {