style.scss 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. // ─────────────────────────────────────────────────────────────
  2. // /chat — 메인 실시간 채팅 (d2-community §⑤, roomKey=main)
  3. // 색/치수는 app/styles/tokens 변수만 사용 (하드코딩 금지)
  4. // Twitch 스타일: border-radius 0, 선 기반 깔끔한 UI
  5. // 채팅 도크(우측 고정) + 팝업(/chat-popup) 공용 스타일
  6. // ─────────────────────────────────────────────────────────────
  7. #chat-room {
  8. display: flex;
  9. flex-direction: column;
  10. height: 100%;
  11. min-height: 0;
  12. padding: var(--sp-4) var(--sp-5);
  13. gap: var(--sp-3);
  14. @media (max-width: 767.98px) {
  15. padding: var(--sp-3);
  16. }
  17. // ── 헤더: 참여자 수(클릭→목록) + 상태 + 컨트롤 ──
  18. .chat-room__head {
  19. display: flex;
  20. align-items: center;
  21. justify-content: space-between;
  22. flex-wrap: wrap;
  23. gap: var(--sp-2);
  24. }
  25. // 참여자 수 버튼 (실시간 채팅 label 위치)
  26. .chat-room__count {
  27. display: inline-flex;
  28. align-items: center;
  29. gap: var(--sp-2);
  30. padding: var(--sp-1) var(--sp-2);
  31. background: transparent;
  32. border: none;
  33. cursor: pointer;
  34. font-size: var(--fs-sm);
  35. color: var(--text-secondary);
  36. font-variant-numeric: tabular-nums;
  37. // --bg-icon(다크)은 양 테마 공통 → 라이트에서 어두운 텍스트가 안 보이므로 흰색 고정
  38. // (옆 .chat-room__icon-btn 알림/설정 버튼과 동일). 숫자/caret/아이콘까지 함께 밝게.
  39. &:hover, &:focus-visible {
  40. background: var(--bg-icon);
  41. color: #fff;
  42. .chat-room__count-num,
  43. .chat-room__count-caret,
  44. > svg:first-child {
  45. color: #fff;
  46. }
  47. }
  48. > svg:first-child {
  49. color: var(--text-muted);
  50. }
  51. }
  52. .chat-room__count-num {
  53. font-weight: 700;
  54. color: var(--text-primary);
  55. }
  56. .chat-room__count-caret {
  57. font-size: var(--fs-2xs);
  58. color: var(--text-muted);
  59. transition: transform 0.15s ease;
  60. &.is-open {
  61. transform: rotate(180deg);
  62. }
  63. }
  64. .chat-room__meta {
  65. display: flex;
  66. align-items: center;
  67. gap: var(--sp-2);
  68. font-size: var(--fs-sm);
  69. color: var(--text-secondary);
  70. }
  71. .chat-room__state {
  72. color: var(--text-muted);
  73. white-space: nowrap;
  74. &--warn {
  75. color: var(--az-accent);
  76. }
  77. &--error {
  78. color: var(--color-danger);
  79. }
  80. }
  81. .chat-room__notice {
  82. display: flex;
  83. align-items: center;
  84. gap: var(--sp-3);
  85. padding: var(--sp-3) var(--sp-4);
  86. background: var(--bg-subtle);
  87. border: 1px solid var(--border-default);
  88. border-radius: 0;
  89. font-size: var(--fs-sm);
  90. color: var(--text-secondary);
  91. svg {
  92. flex-shrink: 0;
  93. color: var(--az-accent);
  94. }
  95. }
  96. .chat-room__body {
  97. position: relative;
  98. display: flex;
  99. flex: 1;
  100. min-height: 0;
  101. }
  102. .chat-room__messages {
  103. flex: 1;
  104. min-height: 0;
  105. overflow-y: auto;
  106. padding: var(--sp-3);
  107. background: var(--bg-elevated);
  108. border: 1px solid var(--border-default);
  109. border-radius: 0;
  110. }
  111. .chat-room__empty {
  112. padding: var(--sp-6) var(--sp-4);
  113. text-align: center;
  114. font-size: var(--fs-sm);
  115. color: var(--text-muted);
  116. }
  117. .chat-room__message {
  118. display: flex;
  119. align-items: baseline;
  120. gap: var(--sp-3);
  121. padding: var(--sp-2) var(--sp-3);
  122. // 글씨 크게/작게 — --chat-fs 배율 (이름·내용 상속)
  123. font-size: calc(var(--fs-base) * var(--chat-fs, 1));
  124. line-height: 1.5;
  125. border-radius: 0;
  126. &:hover {
  127. background: var(--bg-subtle-hover);
  128. }
  129. }
  130. .chat-room__time {
  131. flex-shrink: 0;
  132. font-size: var(--fs-2xs);
  133. color: var(--text-muted);
  134. font-variant-numeric: tabular-nums;
  135. }
  136. .chat-room__badge {
  137. flex-shrink: 0;
  138. align-self: center;
  139. width: var(--sp-5);
  140. height: var(--sp-5);
  141. object-fit: contain;
  142. }
  143. .chat-room__name {
  144. flex-shrink: 0;
  145. font-weight: 600;
  146. color: var(--title-color, var(--text-secondary));
  147. }
  148. .chat-room__content {
  149. color: var(--text-primary);
  150. word-break: break-word;
  151. overflow-wrap: anywhere;
  152. }
  153. // 시스템 메시지 — 일반 메시지와 스타일 구분
  154. .chat-room__system {
  155. padding: var(--sp-2) var(--sp-3);
  156. text-align: center;
  157. font-size: var(--fs-xs);
  158. font-style: italic;
  159. color: var(--text-muted);
  160. }
  161. // 참여자 목록 오버레이 — 본체(.chat-room__body) 를 덮음
  162. .chat-room__participants {
  163. position: absolute;
  164. inset: 0;
  165. z-index: 20;
  166. display: flex;
  167. }
  168. // "새 메시지 ↓" 점프 버튼 — 위로 스크롤 중 새 메시지 도착 시 노출
  169. .chat-room__jump {
  170. position: absolute;
  171. bottom: var(--sp-4);
  172. left: 50%;
  173. transform: translateX(-50%);
  174. display: inline-flex;
  175. align-items: center;
  176. gap: var(--sp-2);
  177. padding: var(--sp-2) var(--sp-4);
  178. background: var(--az-accent);
  179. color: var(--btn-primary-text);
  180. font-size: var(--fs-sm);
  181. font-weight: 600;
  182. border: none;
  183. border-radius: 0;
  184. cursor: pointer;
  185. box-shadow: 0 var(--sp-1) var(--sp-3) var(--shadow-color);
  186. &:hover {
  187. background: var(--az-accent-hover);
  188. }
  189. }
  190. .chat-room__footer {
  191. position: relative;
  192. display: flex;
  193. flex-direction: column;
  194. gap: var(--sp-2);
  195. }
  196. .chat-room__input-row {
  197. display: flex;
  198. align-items: stretch;
  199. gap: var(--sp-2);
  200. }
  201. .chat-room__input {
  202. flex: 1;
  203. min-width: 0;
  204. padding: var(--sp-3) var(--sp-4);
  205. background: var(--bg-input);
  206. color: var(--text-primary);
  207. font-size: var(--fs-base);
  208. border: 1px solid var(--border-strong);
  209. border-radius: 0;
  210. &::placeholder {
  211. color: var(--text-muted);
  212. }
  213. &:disabled {
  214. opacity: 0.6;
  215. cursor: not-allowed;
  216. }
  217. }
  218. .chat-room__send {
  219. flex-shrink: 0;
  220. display: inline-flex;
  221. align-items: center;
  222. justify-content: center;
  223. min-width: var(--sp-7);
  224. padding: 0 var(--sp-4);
  225. background: var(--az-accent);
  226. color: var(--btn-primary-text);
  227. font-size: var(--fs-base);
  228. font-weight: 600;
  229. border: none;
  230. border-radius: 0;
  231. cursor: pointer;
  232. &:hover:not(:disabled) {
  233. background: var(--az-accent-hover);
  234. }
  235. &:disabled {
  236. background: var(--bg-subtle);
  237. color: var(--text-muted);
  238. cursor: not-allowed;
  239. }
  240. }
  241. .chat-room__cooldown {
  242. font-variant-numeric: tabular-nums;
  243. }
  244. // 푸터 이모지 버튼 — 입력창·전송과 높이 정렬(선 기반 아이콘 버튼)
  245. #emojiPicker {
  246. flex-shrink: 0;
  247. position: static; // 팝업(aside)의 위치 기준을 .chat-room__footer 로 이동
  248. > button {
  249. display: inline-flex;
  250. align-items: center;
  251. justify-content: center;
  252. width: var(--sp-7);
  253. height: 100%;
  254. min-height: var(--sp-7);
  255. background: var(--bg-subtle);
  256. border: 1px solid var(--border-strong);
  257. color: var(--text-secondary);
  258. cursor: pointer;
  259. &:hover, &:focus, &:active {
  260. background: var(--bg-subtle-hover);
  261. color: var(--text-link);
  262. }
  263. }
  264. // 좁은 도크 폭 안에서 위로 열림 — footer 폭에 맞춰 span, 화면 밖 클리핑 방지
  265. > aside {
  266. position: absolute;
  267. top: auto;
  268. bottom: calc(100% + var(--sp-2));
  269. left: var(--sp-3);
  270. right: var(--sp-3);
  271. width: auto !important;
  272. height: min(340px, 50vh) !important;
  273. max-width: none;
  274. // Twitch 플랫: 이모지 팝업도 라운드 제거 (라이브러리 기본 8px 상쇄)
  275. border-radius: 0;
  276. --epr-width: auto;
  277. --epr-height: 340px;
  278. --epr-picker-border-radius: 0;
  279. }
  280. }
  281. .chat-room__blocked {
  282. padding: var(--sp-4);
  283. text-align: center;
  284. background: var(--color-danger-bg);
  285. border: 1px solid var(--color-danger-border);
  286. border-radius: 0;
  287. font-size: var(--fs-sm);
  288. color: var(--color-danger);
  289. }
  290. // 팝업(/chat-popup) — 전체 창 채움
  291. &.chat-room--popup {
  292. flex: 1;
  293. min-height: 0;
  294. }
  295. }
  296. // ── 공용 아이콘 버튼 (헤더 수신설정 · 설정 트리거 · 닫기) ──
  297. .chat-room__icon-btn,
  298. .chat-menu__trigger,
  299. .chat-participants__close,
  300. .chat-modal__close {
  301. display: inline-flex;
  302. align-items: center;
  303. justify-content: center;
  304. width: var(--sp-6);
  305. height: var(--sp-6);
  306. background: transparent;
  307. border: none;
  308. cursor: pointer;
  309. color: var(--text-secondary);
  310. font-size: var(--fs-base);
  311. border-radius: 0;
  312. &:hover {
  313. background: var(--bg-icon);
  314. color: #fff; // --bg-icon(다크)은 양 테마 공통 → 라이트에서 --text-primary(어두움)가 안 보이므로 흰색 고정
  315. }
  316. }
  317. // ── 접속 참여자 목록 ──
  318. .chat-participants {
  319. display: flex;
  320. flex: 1;
  321. min-width: 0;
  322. flex-direction: column;
  323. background: var(--bg-elevated);
  324. border: 1px solid var(--border-default);
  325. &__head {
  326. display: flex;
  327. align-items: center;
  328. justify-content: space-between;
  329. padding: var(--sp-3);
  330. border-bottom: 1px solid var(--border-default);
  331. }
  332. &__title {
  333. font-size: var(--fs-sm);
  334. font-weight: 600;
  335. color: var(--text-primary);
  336. }
  337. &__body {
  338. flex: 1;
  339. min-height: 0;
  340. overflow-y: auto;
  341. }
  342. &__empty {
  343. padding: var(--sp-6) var(--sp-4);
  344. text-align: center;
  345. font-size: var(--fs-sm);
  346. color: var(--text-muted);
  347. }
  348. &__list {
  349. display: flex;
  350. flex-direction: column;
  351. }
  352. &__item {
  353. display: flex;
  354. align-items: center;
  355. gap: var(--sp-3);
  356. padding: var(--sp-2) var(--sp-3);
  357. &:hover {
  358. background: var(--bg-subtle-hover);
  359. }
  360. }
  361. &__avatar {
  362. flex-shrink: 0;
  363. display: inline-flex;
  364. align-items: center;
  365. justify-content: center;
  366. width: var(--sp-6);
  367. height: var(--sp-6);
  368. background: var(--bg-subtle);
  369. color: var(--text-muted);
  370. overflow: hidden;
  371. img {
  372. width: 100%;
  373. height: 100%;
  374. object-fit: cover;
  375. }
  376. }
  377. &__info {
  378. display: flex;
  379. flex-direction: column;
  380. flex: 1;
  381. min-width: 0;
  382. }
  383. &__name {
  384. font-size: var(--fs-sm);
  385. font-weight: 600;
  386. color: var(--text-primary);
  387. white-space: nowrap;
  388. overflow: hidden;
  389. text-overflow: ellipsis;
  390. }
  391. &__sid {
  392. font-size: var(--fs-2xs);
  393. color: var(--text-muted);
  394. white-space: nowrap;
  395. overflow: hidden;
  396. text-overflow: ellipsis;
  397. }
  398. &__level {
  399. flex-shrink: 0;
  400. padding: 0 var(--sp-2);
  401. font-size: var(--fs-2xs);
  402. font-weight: 600;
  403. color: var(--az-accent);
  404. border: 1px solid var(--border-default);
  405. }
  406. }
  407. // ── 설정 dropdown (톱니바퀴) ──
  408. .chat-menu {
  409. position: relative;
  410. display: inline-flex;
  411. &__dropdown {
  412. position: absolute;
  413. top: calc(100% + var(--sp-2));
  414. right: 0;
  415. z-index: 30;
  416. min-width: 160px;
  417. padding: var(--sp-2) 0;
  418. background: var(--bg-elevated);
  419. border: 1px solid var(--border-default);
  420. box-shadow: 0 var(--sp-1) var(--sp-3) var(--shadow-color);
  421. }
  422. &__item {
  423. display: flex;
  424. align-items: center;
  425. gap: var(--sp-3);
  426. width: 100%;
  427. padding: var(--sp-2) var(--sp-4);
  428. background: transparent;
  429. border: none;
  430. cursor: pointer;
  431. font-size: var(--fs-sm);
  432. color: var(--text-secondary);
  433. text-align: left;
  434. svg {
  435. width: var(--sp-4);
  436. color: var(--text-muted);
  437. }
  438. &:hover {
  439. background: var(--bg-subtle-hover);
  440. color: var(--text-primary);
  441. svg {
  442. color: var(--az-accent);
  443. }
  444. }
  445. }
  446. &__divider {
  447. height: 1px;
  448. margin: var(--sp-2) 0;
  449. background: var(--border-default);
  450. }
  451. }
  452. // ── 수신설정 modal (portal → body) ──
  453. .chat-modal {
  454. position: fixed;
  455. inset: 0;
  456. z-index: 1200;
  457. display: flex;
  458. align-items: center;
  459. justify-content: center;
  460. padding: var(--sp-4);
  461. background: var(--overlay-color);
  462. &__panel {
  463. display: flex;
  464. flex-direction: column;
  465. width: min(420px, 100%);
  466. max-height: min(560px, calc(100dvh - 2rem));
  467. background: var(--bg-page);
  468. border: 1px solid var(--border-default);
  469. box-shadow: 0 var(--sp-2) var(--sp-5) var(--shadow-color);
  470. }
  471. &__head {
  472. display: flex;
  473. align-items: center;
  474. justify-content: space-between;
  475. padding: var(--sp-4);
  476. border-bottom: 1px solid var(--border-default);
  477. }
  478. &__title {
  479. font-size: var(--fs-lg);
  480. font-weight: 700;
  481. color: var(--text-primary);
  482. }
  483. &__body {
  484. display: flex;
  485. flex-direction: column;
  486. gap: var(--sp-4);
  487. padding: var(--sp-4);
  488. overflow-y: auto;
  489. }
  490. &__loading {
  491. padding: var(--sp-5);
  492. text-align: center;
  493. font-size: var(--fs-sm);
  494. color: var(--text-muted);
  495. }
  496. &__error {
  497. font-size: var(--fs-sm);
  498. color: var(--color-danger);
  499. }
  500. &__foot {
  501. display: flex;
  502. justify-content: flex-end;
  503. gap: var(--sp-3);
  504. padding: var(--sp-4);
  505. border-top: 1px solid var(--border-default);
  506. }
  507. &__btn {
  508. padding: var(--sp-2) var(--sp-5);
  509. font-size: var(--fs-sm);
  510. font-weight: 600;
  511. border: 1px solid transparent;
  512. border-radius: 0;
  513. cursor: pointer;
  514. &--ghost {
  515. background: transparent;
  516. border-color: var(--border-strong);
  517. color: var(--text-secondary);
  518. &:hover:not(:disabled) {
  519. background: var(--bg-subtle-hover);
  520. color: var(--text-primary);
  521. }
  522. }
  523. &--primary {
  524. background: var(--az-accent);
  525. color: var(--btn-primary-text);
  526. &:hover:not(:disabled) {
  527. background: var(--az-accent-hover);
  528. }
  529. }
  530. &:disabled {
  531. opacity: 0.6;
  532. cursor: not-allowed;
  533. }
  534. }
  535. }
  536. // ── 토글 스위치 (초대/귓속말/팔로잉 접속 표시) — 선 기반 사각 스위치 ──
  537. .chat-toggle {
  538. display: flex;
  539. align-items: center;
  540. justify-content: space-between;
  541. gap: var(--sp-4);
  542. cursor: pointer;
  543. &__text {
  544. display: flex;
  545. flex-direction: column;
  546. gap: var(--sp-1);
  547. min-width: 0;
  548. }
  549. &__label {
  550. font-size: var(--fs-base);
  551. font-weight: 600;
  552. color: var(--text-primary);
  553. }
  554. &__desc {
  555. font-size: var(--fs-2xs);
  556. color: var(--text-muted);
  557. }
  558. &__switch {
  559. position: relative;
  560. flex-shrink: 0;
  561. width: 44px;
  562. height: 24px;
  563. input {
  564. position: absolute;
  565. inset: 0;
  566. z-index: 1;
  567. margin: 0;
  568. opacity: 0;
  569. cursor: pointer;
  570. &:disabled {
  571. cursor: not-allowed;
  572. }
  573. }
  574. }
  575. &__slider {
  576. position: absolute;
  577. inset: 0;
  578. background: var(--bg-subtle);
  579. border: 1px solid var(--border-strong);
  580. transition: background 0.15s ease, border-color 0.15s ease;
  581. &::before {
  582. content: '';
  583. position: absolute;
  584. top: 2px;
  585. left: 2px;
  586. width: 18px;
  587. height: 18px;
  588. background: var(--text-muted);
  589. transition: transform 0.15s ease, background 0.15s ease;
  590. }
  591. }
  592. input:checked + &__slider {
  593. background: var(--az-accent);
  594. border-color: var(--az-accent);
  595. &::before {
  596. transform: translateX(20px);
  597. background: var(--btn-primary-text);
  598. }
  599. }
  600. input:focus-visible + &__slider {
  601. outline: 2px solid var(--az-accent);
  602. outline-offset: 2px;
  603. }
  604. }
  605. // ── 채팅 팝업 창(/chat-popup) 래퍼 — 전체 뷰포트 ──
  606. .chat-popup {
  607. display: flex;
  608. height: 100vh;
  609. height: 100dvh;
  610. }