Files
dbx-main/app/static/cafe24.css
T
king 08ebe7b53d feat(cafe24): 상단 공통 홍보 숨기기 체크박스 (PC·모바일 동시)
요청은 스킨 detail.html 의 `const numbers = [...]` 를 고치는 것이었지만, 카페24
Admin API 로는 스킨 HTML 파일을 읽거나 쓸 수 없다. 확인 결과 테마는 조회만
가능하고(GET /admin/themes) 스킨 파일 엔드포인트가 없다. 쓸 수 있는 것은 테마
페이지와 스크립트 태그뿐이다.

그래서 같은 결과를 상품 상세설명 안의 CSS 로 낸다. 상세설명은 이미 우리가 쓸 수
있는 영역이고, 상품별로 켜고 끌 수 있으며, 상태가 그 상품 소스에 그대로 보인다.
새 권한이나 재인증도 필요하지 않다.

    <style id="cf24-hide-common-promo">.edb-img-tag-w{display:none !important}</style>

id 로 우리 블록만 찾으므로 사람이 쓴 <style> 은 건드리지 않는다. 넣기/빼기는
멱등이고 소스 정리(format_html)를 거쳐도 상태가 유지된다.

PC·모바일 모두 반영한다. 미분리 상품은 같은 HTML 이 양쪽에 들어가고, 분리 상품은
모바일 본문을 건드리지 않되 이 블록만 모바일에도 맞춘다 — 양쪽에 걸지 않으면 한쪽에
홍보가 그대로 남는다. PC/모바일 상태가 다르면 화면에 불일치를 알린다.

"변경 없음" 판정에 모바일 변경도 포함시켰다. PC 는 그대로인데 모바일 숨김만
바뀌는 경우가 있어서, 예전 조건이면 아무 일도 하지 않고 끝났다.

스킨의 numbers 목록과는 독립이며 충돌하지 않는다(스킨은 요소 제거, 이쪽은 CSS 숨김).
이미 목록에 있는 상품은 그대로 두면 된다.

검증: 유닛테스트 49개 통과(신규 4개 — 추가/제거 왕복, 멱등, 포맷 통과 후 인식,
사람이 쓴 style 보존).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:44:43 +09:00

586 lines
14 KiB
CSS

/* 카페24 상품관리 모듈 전용 스타일.
전역(erp.css) 은 건드리지 않는다. 클래스 접두사: cf24-
색상은 erp.css 의 :root 토큰을 재사용한다. */
/* .erp-page 는 flex column + overflow-y:auto 다. 자식의 flex-shrink 기본값이
1 이라 내용이 화면보다 길면 카드가 눌려 잘리고(스크롤도 안 생긴다),
시스템 화면의 [카페24 연결] 버튼처럼 카드 아래쪽이 사라진다.
축소를 막아 넘친 만큼 .erp-page 가 스크롤하게 한다.
이 파일은 카페24 템플릿에서만 로드되므로 다른 모듈에는 영향이 없다. */
.erp-page > * {
flex-shrink: 0;
}
/* 2분할 화면은 편집 영역을 최대한 넓게 쓴다 — 이 페이지에서만 폭 제한을 푼다.
box-sizing 을 함께 바꿔야 한다: .erp-page 는 width:100% + padding:24px 라서
max-width 를 풀면 padding 이 폭에 더해져 문서 전체에 가로 스크롤이 생긴다. */
.erp-page {
max-width: none;
box-sizing: border-box;
}
.cf24-card {
margin-bottom: var(--sp-16, 16px);
}
/* ════════════════════════════════════════════════════════════
좌우 2분할: 왼쪽 목록(좁게) | 오른쪽 상세페이지 편집(넓게)
각 칸이 따로 스크롤되고, 전체 높이는 화면에 맞춘다.
════════════════════════════════════════════════════════════ */
.cf24-split {
display: grid;
grid-template-columns: 550px minmax(0, 1fr);
gap: var(--sp-12, 12px);
align-items: stretch;
}
/* .erp-page 는 flex column 이다. 위 flex-shrink:0 규칙의 예외로, 분할 영역만은
남은 높이를 모두 차지하고 안에서 스크롤되게 한다(휴가/쿠팡 모듈과 같은 방식). */
.erp-page > .cf24-split {
flex: 1 1 auto;
min-height: 0;
}
.cf24-pane {
min-height: 0;
overflow: auto;
margin-bottom: 0;
padding: var(--sp-12, 12px);
}
.cf24-pane-list {
display: flex;
flex-direction: column;
gap: var(--sp-8, 8px);
overflow: hidden; /* 표만 스크롤 — 검색/필터는 고정 */
}
.cf24-pane-editor {
display: flex;
flex-direction: column;
gap: var(--sp-8, 8px);
padding: var(--sp-16, 16px);
}
@media (max-width: 1100px) {
.cf24-split {
grid-template-columns: minmax(0, 1fr);
}
.cf24-pane {
overflow: visible;
}
.cf24-pane-list {
max-height: 45vh;
}
}
/* ── 왼쪽: 검색·필터 ── */
.cf24-filters {
display: flex;
flex-direction: column;
gap: var(--sp-8, 8px);
flex: 0 0 auto;
}
.cf24-checks {
display: flex;
gap: var(--sp-12, 12px);
font-size: var(--text-caption, 12px);
}
.cf24-checks label {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
}
.cf24-list-count {
font-size: var(--text-caption, 12px);
color: var(--color-midtone-gray, #737373);
}
/* ── 왼쪽: 목록 표 (좁게) ── */
.cf24-list-scroll {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
}
.cf24-list-table {
font-size: 12px;
table-layout: fixed;
width: 100%;
}
.cf24-list-table thead th {
position: sticky;
top: 0;
z-index: 1;
padding: 6px 4px;
white-space: nowrap;
}
.cf24-list-table tbody td {
padding: 6px 4px;
vertical-align: top;
}
/* 목록 폭 550px 기준. 고정폭을 뺀 나머지(약 290px)가 상품명 몫이다.
진열/판매 칸은 제목(2글자) + 정렬 화살표가 들어갈 만큼만. */
.cf24-col-no { width: 40px; text-align: right; color: var(--color-midtone-gray, #737373); }
.cf24-col-flag { width: 40px; text-align: center; }
.cf24-col-date { width: 78px; white-space: nowrap; color: var(--color-midtone-gray, #737373); }
.cf24-col-name { word-break: break-word; }
/* 긴 상품명은 2줄까지만 — 행 높이를 고르게 유지해 목록을 훑기 쉽게 한다.
전체 이름은 title 툴팁과 오른쪽 편집기 제목에서 확인한다. */
.cf24-col-name a {
color: inherit;
text-decoration: none;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.4;
}
.cf24-row {
cursor: pointer;
}
.cf24-row:hover {
background: var(--color-ghost-gray, #f2f2f2);
}
.cf24-row.is-active {
background: var(--color-rich-black, #0a0a0a);
}
.cf24-row.is-active td,
.cf24-row.is-active .cf24-col-name a {
color: #fff;
}
/* 진열/판매 표시는 좁은 칸이라 배지 대신 점으로 */
.cf24-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-subtle-ash, #e5e5e5);
}
.cf24-dot-on {
background: var(--color-success-green, #10c22b);
}
/* 제목행 클릭 정렬 */
.cf24-sortable {
cursor: pointer;
user-select: none;
}
.cf24-sortable::after {
content: "↕";
opacity: 0.35;
margin-left: 2px;
}
.cf24-sortable.is-asc::after { content: "▲"; opacity: 1; }
.cf24-sortable.is-desc::after { content: "▼"; opacity: 1; }
/* ── 오른쪽: 편집기 ── */
.cf24-editor-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: var(--sp-12, 12px);
flex: 0 0 auto;
}
.cf24-editor-title {
margin: 0;
font-size: var(--text-heading, 18px);
letter-spacing: -0.45px;
}
.cf24-editor-sub {
margin: 4px 0 0;
font-size: var(--text-caption, 12px);
color: var(--color-midtone-gray, #737373);
}
.cf24-editor-badges {
display: flex;
gap: 4px;
flex: 0 0 auto;
}
.cf24-editor-form {
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 0;
gap: var(--sp-8, 8px);
}
.cf24-editor-bar {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--sp-8, 8px);
flex-wrap: wrap;
flex: 0 0 auto;
}
.cf24-editor-bar-right {
display: flex;
gap: var(--sp-8, 8px);
align-items: center;
}
.cf24-check-inline {
display: inline-flex;
align-items: center;
gap: 4px;
margin-left: var(--sp-12, 12px);
cursor: pointer;
color: var(--color-rich-black, #0a0a0a);
}
.cf24-memo {
width: 260px;
padding: 6px var(--sp-10, 10px);
border: 1px solid var(--color-subtle-ash, #e5e5e5);
border-radius: var(--r-lg, 10px);
font-size: 12px;
}
.cf24-html-main {
flex: 1 1 auto;
min-height: 340px;
}
/* ════════════════════════════════════════════════════════════
문법 강조 편집기
투명한 <textarea> 를 색칠된 <pre> 위에 정확히 겹쳐 놓는 방식이다.
두 요소의 폰트·줄높이·여백·줄바꿈 규칙이 **완전히 같아야** 글자가 어긋나지
않는다. 아래 두 선택자에 붙은 속성을 바꿀 때는 반드시 함께 바꿀 것.
외부 라이브러리를 쓰지 않는다(자체 호스팅 원칙 + CDN 의존 제거).
════════════════════════════════════════════════════════════ */
/* 스크롤은 **textarea 가** 담당하고, 색칠 층과 줄 번호가 transform 으로 따라간다.
폭·높이를 계산해 맞추는 방식은 flex 안에서 `width: max-content` 가 기대대로
동작하지 않아 실패했다 — 긴 줄이 있으면 textarea 만 내부 스크롤되고 색칠 층은
제자리에 남아, 커서 위치와 보이는 글자가 어긋났다(실측 706 vs 686).
스크롤 동기화 방식은 크기 계산이 아예 필요 없어 어긋날 여지가 없다. */
.cf24-code {
position: relative;
flex: 1 1 auto;
min-height: 340px;
overflow: hidden;
border: 1px solid var(--color-subtle-ash, #e5e5e5);
border-radius: var(--r-lg, 10px);
background: var(--color-canvas-white, #fff);
}
/* 줄 번호 — 코드 영역 왼쪽에 고정된 별도 칸(가로 스크롤과 무관) */
.cf24-gutter {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 42px;
overflow: hidden;
padding: var(--sp-12, 12px) var(--sp-8, 8px) 0 0;
text-align: right;
background: var(--color-ghost-gray, #f6f8fa);
border-right: 1px solid var(--color-subtle-ash, #e5e5e5);
color: #8c959f;
user-select: none;
white-space: pre;
}
.cf24-gutter-inner {
will-change: transform; /* 세로 스크롤을 따라 움직인다 */
}
.cf24-code-body {
position: absolute;
left: 43px; /* 줄 번호 칸 + 경계선 */
right: 0;
top: 0;
bottom: 0;
overflow: hidden;
}
/* 두 층의 글자가 정확히 겹치려면 아래 속성이 전부 같아야 한다.
줄 번호(.cf24-gutter)도 같은 글꼴 지표를 써야 줄이 맞는다. */
.cf24-gutter,
.cf24-code-hl,
.cf24-code-input {
margin: 0;
border: 0;
box-sizing: border-box;
font-family: var(--font-geist-mono, ui-monospace, "Consolas", monospace);
font-size: 12px;
line-height: 1.6;
tab-size: 2;
}
.cf24-code-hl,
.cf24-code-input {
padding: var(--sp-12, 12px);
white-space: pre; /* 줄바꿈하지 않는다 — 줄 번호가 어긋나지 않게 */
overflow-wrap: normal;
}
/* 색칠 층 — textarea 의 스크롤량만큼 transform 으로 이동한다. */
.cf24-code-hl {
position: absolute;
top: 0;
left: 0;
width: max-content;
min-width: 100%;
pointer-events: none;
color: var(--color-rich-black, #0a0a0a);
will-change: transform;
}
/* textarea 가 실제 스크롤 주체다. */
.cf24-code-input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
background: transparent;
color: transparent;
caret-color: var(--color-rich-black, #0a0a0a);
resize: none;
overflow: auto;
}
.cf24-code-input:focus {
outline: none;
}
.cf24-code:focus-within {
border-color: var(--color-rich-black, #0a0a0a);
}
/* 선택 영역이 보이게 (글자는 투명이므로 배경만 남는다) */
.cf24-code-input::selection {
background: #b3d4fc;
}
/* 토큰 색 — GitHub 라이트 계열 */
.cf24-t-tag { color: #116329; } /* 태그 이름 */
.cf24-t-attr { color: #953800; } /* 속성 이름 */
.cf24-t-val { color: #0a3069; } /* 속성 값 */
.cf24-t-pun { color: #57606a; } /* < > / = */
.cf24-t-com { color: #6e7781; font-style: italic; } /* 주석 */
.cf24-t-doc { color: #6639ba; } /* DOCTYPE 등 선언 */
.cf24-code-hint {
font-size: var(--text-caption, 12px);
color: var(--color-midtone-gray, #737373);
}
.cf24-details {
flex: 0 0 auto;
border-top: 1px solid var(--color-subtle-ash, #e5e5e5);
padding-top: var(--sp-8, 8px);
}
.cf24-details > summary {
cursor: pointer;
font-size: var(--text-caption, 12px);
color: var(--color-midtone-gray, #737373);
}
.cf24-compact {
font-size: 12px;
}
.cf24-compact tbody td,
.cf24-compact thead th {
padding: 6px 8px;
}
.cf24-empty-pane {
margin: auto;
text-align: center;
padding: var(--sp-24, 24px);
}
.cf24-empty-pane h3 {
margin: 0 0 var(--sp-8, 8px);
}
.cf24-card-head {
display: flex;
align-items: center;
gap: var(--sp-8, 8px);
margin-bottom: var(--sp-12, 12px);
}
.cf24-card-head h3 {
margin: 0;
font-size: var(--text-heading, 18px);
letter-spacing: -0.45px;
}
.cf24-muted {
color: var(--color-midtone-gray, #737373);
font-size: var(--text-caption, 12px);
}
.cf24-err {
color: var(--color-callout-red, #c22b10);
}
.cf24-nowrap {
white-space: nowrap;
}
/* 상태 배지 */
.cf24-badge-ok {
background: var(--color-success-green, #10c22b);
color: #fff;
}
.cf24-badge-off {
background: var(--color-ghost-gray, #f2f2f2);
color: var(--color-rich-black, #0a0a0a);
}
/* 안내/오류 배너 */
.cf24-flash {
padding: var(--sp-10, 10px) var(--sp-12, 12px);
border-radius: var(--r-lg, 10px);
margin-bottom: var(--sp-12, 12px);
font-size: var(--text-body, 14px);
}
.cf24-flash-ok {
background: #eefaf0;
border: 1px solid var(--color-success-green, #10c22b);
}
.cf24-flash-err {
background: #fdefec;
border: 1px solid var(--color-callout-red, #c22b10);
}
/* 키-값 표 */
.cf24-kv th {
width: 180px;
text-align: left;
color: var(--color-midtone-gray, #737373);
font-weight: 500;
white-space: nowrap;
}
.cf24-actions {
display: flex;
gap: var(--sp-8, 8px);
align-items: center;
margin-top: var(--sp-12, 12px);
}
.cf24-warn {
color: var(--color-callout-red, #c22b10);
font-size: var(--text-caption, 12px);
font-weight: 500;
}
/* 검색 도구모음 / 페이지 이동 */
.cf24-toolbar {
display: flex;
gap: var(--sp-8, 8px);
align-items: center;
flex-wrap: wrap;
margin-bottom: var(--sp-12, 12px);
}
/* 한 줄 높이로 고정한다. 세로 flex 안에서 flex-basis 를 주면 그 값이 '높이'로
적용돼 입력란이 거대해진다(실제로 그랬다 — flex 방향을 항상 확인할 것). */
.cf24-search {
flex: 0 0 auto;
width: 100%;
box-sizing: border-box;
height: 32px;
padding: 0 var(--sp-10, 10px);
border: 1px solid var(--color-subtle-ash, #e5e5e5);
border-radius: var(--r-lg, 10px);
font-size: var(--text-body, 14px);
}
.cf24-pager {
display: flex;
gap: var(--sp-12, 12px);
align-items: center;
justify-content: center;
margin-top: var(--sp-16, 16px);
}
/* 상세설명 HTML 원문 */
.cf24-label {
display: block;
margin: var(--sp-16, 16px) 0 var(--sp-8, 8px);
font-size: var(--text-caption, 12px);
font-weight: 500;
color: var(--color-midtone-gray, #737373);
}
.cf24-html {
width: 100%;
box-sizing: border-box;
padding: var(--sp-12, 12px);
border: 1px solid var(--color-subtle-ash, #e5e5e5);
border-radius: var(--r-lg, 10px);
background: var(--color-canvas-white, #fff);
font-family: var(--font-geist-mono, ui-monospace, monospace);
font-size: 12px;
line-height: 1.6;
white-space: pre;
overflow: auto;
resize: vertical;
}
/* 읽기 전용은 배경으로 구분 — 편집 가능한 칸과 헷갈리지 않게 */
.cf24-html[readonly] {
background: var(--color-ghost-gray, #f2f2f2);
}
.cf24-note {
margin: var(--sp-12, 12px) 0 0;
padding: var(--sp-10, 10px) var(--sp-12, 12px);
border-left: 3px solid var(--color-subtle-ash, #e5e5e5);
background: var(--color-ghost-gray, #f2f2f2);
border-radius: var(--r-sm, 6px);
color: var(--color-midtone-gray, #737373);
font-size: var(--text-caption, 12px);
line-height: 1.7;
}
/* 넓은 로그 표는 카드 안에서만 가로 스크롤 */
.cf24-scroll {
overflow-x: auto;
}
.cf24-scroll code {
font-family: var(--font-geist-mono, ui-monospace, monospace);
font-size: 12px;
}
.cf24-empty {
padding: var(--sp-24, 24px);
}
.cf24-empty h3 {
margin: 0 0 var(--sp-8, 8px);
}