683 lines
19 KiB
Vue
683 lines
19 KiB
Vue
<script setup>
|
|
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
|
|
import { RouterLink, useRoute } from 'vue-router'
|
|
import { api } from '../lib/api'
|
|
import { displayInitialFrom } from '../lib/display'
|
|
import { loginPath } from '../lib/paths'
|
|
import { toApiUrl } from '../lib/runtime'
|
|
|
|
const props = defineProps({
|
|
tierListId: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
canWrite: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
currentUserId: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
title: {
|
|
type: String,
|
|
default: '댓글',
|
|
},
|
|
description: {
|
|
type: String,
|
|
default: '티어표에 대한 의견을 남기고 답글로 대화를 이어갈 수 있어요.',
|
|
},
|
|
})
|
|
|
|
const route = useRoute()
|
|
const comments = ref([])
|
|
const isLoading = ref(false)
|
|
const error = ref('')
|
|
const commentDraft = ref('')
|
|
const replyDrafts = ref({})
|
|
const openedReplyComposerId = ref('')
|
|
const submittingTargetId = ref('')
|
|
const deletingCommentId = ref('')
|
|
const replyInputRefs = ref({})
|
|
let activeCommentRetryTimer = 0
|
|
|
|
const totalCommentCount = computed(() =>
|
|
comments.value.reduce((count, comment) => count + 1 + (comment.replies?.length || 0), 0)
|
|
)
|
|
const loginTarget = computed(() => loginPath(route.fullPath))
|
|
const highlightedCommentId = computed(() =>
|
|
typeof route.query.commentId === 'string' ? route.query.commentId.trim() : ''
|
|
)
|
|
|
|
function avatarUrlOf(user) {
|
|
return user?.authorAvatarSrc ? toApiUrl(user.authorAvatarSrc) : ''
|
|
}
|
|
|
|
function avatarFallbackOf(user) {
|
|
return displayInitialFrom(user?.authorName, user?.authorAccountName, '?')
|
|
}
|
|
|
|
function displayNameOf(user) {
|
|
return user?.authorName || '알 수 없음'
|
|
}
|
|
|
|
function formatDate(ts) {
|
|
return new Date(Number(ts || 0)).toLocaleString('ko-KR', {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
})
|
|
}
|
|
|
|
function isOwnComment(comment) {
|
|
return !!props.currentUserId && props.currentUserId === comment?.authorId
|
|
}
|
|
|
|
function isHighlighted(commentId) {
|
|
return !!commentId && highlightedCommentId.value === commentId
|
|
}
|
|
|
|
function clearActiveCommentRetry() {
|
|
if (typeof window === 'undefined' || !activeCommentRetryTimer) return
|
|
window.clearTimeout(activeCommentRetryTimer)
|
|
activeCommentRetryTimer = 0
|
|
}
|
|
|
|
async function scrollToHighlightedComment(attempt = 0) {
|
|
clearActiveCommentRetry()
|
|
if (!highlightedCommentId.value || typeof document === 'undefined') return
|
|
await nextTick()
|
|
const target = document.querySelector(`[data-comment-id="${highlightedCommentId.value}"]`)
|
|
if (target) {
|
|
target.scrollIntoView({ behavior: attempt === 0 ? 'auto' : 'smooth', block: 'center' })
|
|
return
|
|
}
|
|
if (attempt >= 6 || typeof window === 'undefined') return
|
|
activeCommentRetryTimer = window.setTimeout(() => {
|
|
scrollToHighlightedComment(attempt + 1)
|
|
}, 180)
|
|
}
|
|
|
|
async function loadComments() {
|
|
if (!props.tierListId) {
|
|
comments.value = []
|
|
return
|
|
}
|
|
isLoading.value = true
|
|
error.value = ''
|
|
try {
|
|
const data = await api.listTierListComments(props.tierListId)
|
|
comments.value = Array.isArray(data.comments) ? data.comments : []
|
|
scrollToHighlightedComment()
|
|
} catch (loadError) {
|
|
error.value = '댓글을 불러오지 못했어요.'
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function submitComment(parentCommentId = '') {
|
|
if (!props.canWrite || !props.tierListId) return
|
|
const isReply = !!parentCommentId
|
|
const draftValue = isReply ? replyDrafts.value[parentCommentId] || '' : commentDraft.value
|
|
const content = String(draftValue || '').trim()
|
|
if (!content) return
|
|
|
|
submittingTargetId.value = parentCommentId || 'root'
|
|
error.value = ''
|
|
try {
|
|
const data = await api.createTierListComment(props.tierListId, {
|
|
content,
|
|
parentCommentId,
|
|
})
|
|
comments.value = Array.isArray(data.comments) ? data.comments : []
|
|
if (isReply) {
|
|
replyDrafts.value = { ...replyDrafts.value, [parentCommentId]: '' }
|
|
openedReplyComposerId.value = ''
|
|
} else {
|
|
commentDraft.value = ''
|
|
}
|
|
await nextTick()
|
|
if (data.createdCommentId && typeof window !== 'undefined') {
|
|
const target = document.querySelector(`[data-comment-id="${data.createdCommentId}"]`)
|
|
if (target) target.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
}
|
|
} catch (submitError) {
|
|
const code = submitError?.data?.error
|
|
if (code === 'comment_reply_depth_invalid') {
|
|
error.value = '답글에는 다시 답글을 달 수 없어요.'
|
|
} else {
|
|
error.value = '댓글을 저장하지 못했어요.'
|
|
}
|
|
} finally {
|
|
submittingTargetId.value = ''
|
|
}
|
|
}
|
|
|
|
async function deleteComment(commentId) {
|
|
if (!commentId || !props.tierListId) return
|
|
deletingCommentId.value = commentId
|
|
error.value = ''
|
|
try {
|
|
const data = await api.deleteTierListComment(props.tierListId, commentId)
|
|
comments.value = Array.isArray(data.comments) ? data.comments : []
|
|
} catch (deleteError) {
|
|
error.value = '댓글을 삭제하지 못했어요.'
|
|
} finally {
|
|
deletingCommentId.value = ''
|
|
}
|
|
}
|
|
|
|
function registerReplyInput(commentId, element) {
|
|
if (!commentId) return
|
|
if (element) {
|
|
replyInputRefs.value[commentId] = element
|
|
return
|
|
}
|
|
delete replyInputRefs.value[commentId]
|
|
}
|
|
|
|
async function focusReplyInput(commentId) {
|
|
if (!commentId) return
|
|
await nextTick()
|
|
const target = replyInputRefs.value[commentId]
|
|
if (target && typeof target.focus === 'function') {
|
|
target.focus()
|
|
const value = target.value || ''
|
|
if (typeof target.setSelectionRange === 'function') {
|
|
target.setSelectionRange(value.length, value.length)
|
|
}
|
|
}
|
|
}
|
|
|
|
async function toggleReplyComposer(commentId) {
|
|
const nextId = openedReplyComposerId.value === commentId ? '' : commentId
|
|
openedReplyComposerId.value = nextId
|
|
if (!nextId) return
|
|
await focusReplyInput(nextId)
|
|
}
|
|
|
|
watch(() => props.tierListId, loadComments, { immediate: true })
|
|
watch(highlightedCommentId, () => {
|
|
scrollToHighlightedComment()
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
clearActiveCommentRetry()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<section class="commentsCard">
|
|
<header class="commentsCard__head">
|
|
<div>
|
|
<div class="commentsCard__eyebrow">Comments</div>
|
|
<h3 class="commentsCard__title">{{ title }}</h3>
|
|
<p class="commentsCard__desc">{{ description }}</p>
|
|
</div>
|
|
<div class="commentsCard__count">{{ totalCommentCount }}개</div>
|
|
</header>
|
|
|
|
<div v-if="error" class="commentsCard__error">{{ error }}</div>
|
|
|
|
<div v-if="props.canWrite" class="commentsComposer">
|
|
<textarea
|
|
v-model="commentDraft"
|
|
class="commentsComposer__input"
|
|
maxlength="2000"
|
|
rows="3"
|
|
placeholder="이 티어표에 대한 의견을 남겨보세요."
|
|
/>
|
|
<div class="commentsComposer__footer">
|
|
<span class="commentsComposer__hint">{{ commentDraft.length }}/2000</span>
|
|
<button class="btn btn--save btn--small" type="button" :disabled="!commentDraft.trim() || submittingTargetId === 'root'" @click="submitComment()">
|
|
댓글 등록
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div v-else class="commentsLoginCta">
|
|
<div class="commentsLoginCta__text">로그인하면 댓글과 답글을 남길 수 있어요.</div>
|
|
<RouterLink class="btn btn--ghost btn--small" :to="loginTarget">로그인</RouterLink>
|
|
</div>
|
|
|
|
<div v-if="isLoading" class="commentsCard__empty">댓글을 불러오는 중이에요.</div>
|
|
<div v-else-if="comments.length === 0" class="commentsCard__empty">아직 댓글이 없어요. 첫 댓글을 남겨보세요.</div>
|
|
|
|
<div v-else class="commentsThread">
|
|
<article
|
|
v-for="comment in comments"
|
|
:key="comment.id"
|
|
class="commentItem"
|
|
:class="{ 'commentItem--highlighted': isHighlighted(comment.id) }"
|
|
:data-comment-id="comment.id"
|
|
>
|
|
<div class="commentItem__head">
|
|
<div class="commentItem__author">
|
|
<img v-if="avatarUrlOf(comment)" class="commentItem__avatar" :src="avatarUrlOf(comment)" :alt="displayNameOf(comment)" draggable="false" />
|
|
<div v-else class="commentItem__avatar commentItem__avatar--fallback">{{ avatarFallbackOf(comment) }}</div>
|
|
<div class="commentItem__meta">
|
|
<div class="commentItem__metaTop">
|
|
<div class="commentItem__name">{{ displayNameOf(comment) }}</div>
|
|
<span class="commentItem__badge">댓글</span>
|
|
</div>
|
|
<div class="commentItem__date">{{ formatDate(comment.createdAt) }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="commentItem__actions">
|
|
<button v-if="props.canWrite" class="commentItem__action" type="button" @click="toggleReplyComposer(comment.id)">답글</button>
|
|
<button
|
|
v-if="isOwnComment(comment)"
|
|
class="commentItem__action commentItem__action--danger"
|
|
type="button"
|
|
:disabled="deletingCommentId === comment.id"
|
|
@click="deleteComment(comment.id)"
|
|
>
|
|
삭제
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="commentItem__body">{{ comment.content }}</div>
|
|
|
|
<div v-if="openedReplyComposerId === comment.id && props.canWrite" class="replyComposer">
|
|
<textarea
|
|
v-model="replyDrafts[comment.id]"
|
|
:ref="(element) => registerReplyInput(comment.id, element)"
|
|
class="commentsComposer__input commentsComposer__input--reply"
|
|
maxlength="2000"
|
|
rows="2"
|
|
placeholder="답글을 입력하세요."
|
|
/>
|
|
<div class="commentsComposer__footer">
|
|
<span class="commentsComposer__hint">{{ (replyDrafts[comment.id] || '').length }}/2000</span>
|
|
<button class="btn btn--save commentsComposer__submit" type="button" :disabled="!(replyDrafts[comment.id] || '').trim() || submittingTargetId === comment.id" @click="submitComment(comment.id)">
|
|
답글 등록
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="comment.replies?.length" class="replyList">
|
|
<article
|
|
v-for="reply in comment.replies"
|
|
:key="reply.id"
|
|
class="commentItem commentItem--reply"
|
|
:class="{ 'commentItem--highlighted': isHighlighted(reply.id) }"
|
|
:data-comment-id="reply.id"
|
|
>
|
|
<div class="commentItem__head">
|
|
<div class="commentItem__author">
|
|
<img v-if="avatarUrlOf(reply)" class="commentItem__avatar" :src="avatarUrlOf(reply)" :alt="displayNameOf(reply)" draggable="false" />
|
|
<div v-else class="commentItem__avatar commentItem__avatar--fallback">{{ avatarFallbackOf(reply) }}</div>
|
|
<div class="commentItem__meta">
|
|
<div class="commentItem__metaTop">
|
|
<div class="commentItem__name">{{ displayNameOf(reply) }}</div>
|
|
<span class="commentItem__badge commentItem__badge--reply">답글</span>
|
|
</div>
|
|
<div class="commentItem__date">{{ formatDate(reply.createdAt) }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="commentItem__actions">
|
|
<button
|
|
v-if="isOwnComment(reply)"
|
|
class="commentItem__action commentItem__action--danger"
|
|
type="button"
|
|
:disabled="deletingCommentId === reply.id"
|
|
@click="deleteComment(reply.id)"
|
|
>
|
|
삭제
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="commentItem__body">{{ reply.content }}</div>
|
|
</article>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.commentsCard {
|
|
margin-top: 24px;
|
|
padding: 24px;
|
|
border-radius: 28px;
|
|
border: 1px solid var(--theme-card-border);
|
|
background: var(--theme-card-bg);
|
|
box-shadow: inset 0 1px 0 var(--theme-card-shadow);
|
|
}
|
|
|
|
.commentsCard__head {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 16px;
|
|
margin-bottom: 18px;
|
|
}
|
|
|
|
.commentsCard__eyebrow {
|
|
font-size: 11px;
|
|
font-weight: 900;
|
|
letter-spacing: 0.16em;
|
|
text-transform: uppercase;
|
|
color: var(--theme-text-faint);
|
|
}
|
|
|
|
.commentsCard__title {
|
|
margin: 6px 0 8px;
|
|
font-size: 22px;
|
|
font-weight: 900;
|
|
}
|
|
|
|
.commentsCard__desc {
|
|
margin: 0;
|
|
color: var(--theme-text-muted);
|
|
font-size: 12px;
|
|
line-height: 1.55;
|
|
}
|
|
|
|
.commentsCard__count {
|
|
flex: 0 0 auto;
|
|
align-self: flex-start;
|
|
padding: 8px 12px;
|
|
border-radius: 999px;
|
|
background: var(--theme-surface-soft);
|
|
color: var(--theme-text-muted);
|
|
font-size: 13px;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.commentsCard__error {
|
|
margin-bottom: 16px;
|
|
padding: 12px 14px;
|
|
border-radius: 16px;
|
|
border: 1px solid var(--theme-danger-border);
|
|
background: var(--theme-danger-bg);
|
|
color: var(--theme-text);
|
|
}
|
|
|
|
.commentsComposer,
|
|
.commentsLoginCta {
|
|
margin-bottom: 18px;
|
|
padding: 16px;
|
|
border-radius: 22px;
|
|
background: color-mix(in srgb, var(--theme-surface) 88%, var(--theme-surface-soft));
|
|
box-shadow: inset 0 1px 0 color-mix(in srgb, white 5%, transparent);
|
|
}
|
|
|
|
.commentsComposer__input {
|
|
width: 100%;
|
|
min-height: 92px;
|
|
resize: vertical;
|
|
padding: 14px 16px;
|
|
border-radius: 18px;
|
|
border: 0;
|
|
background: var(--theme-input-bg);
|
|
color: var(--theme-text);
|
|
box-sizing: border-box;
|
|
box-shadow:
|
|
inset 0 0 0 1px color-mix(in srgb, var(--theme-field-border) 38%, transparent),
|
|
0 10px 24px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.commentsComposer__input--reply {
|
|
min-height: 72px;
|
|
background: color-mix(in srgb, var(--theme-input-bg) 82%, var(--theme-surface-soft));
|
|
}
|
|
|
|
.commentsComposer__input:focus {
|
|
outline: none;
|
|
border-color: color-mix(in srgb, var(--theme-accent) 60%, var(--theme-field-border));
|
|
box-shadow:
|
|
inset 0 0 0 1px color-mix(in srgb, var(--theme-accent) 52%, transparent),
|
|
0 0 0 3px color-mix(in srgb, var(--theme-accent) 16%, transparent);
|
|
}
|
|
|
|
.commentsComposer__footer,
|
|
.commentsLoginCta {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.commentsComposer__footer {
|
|
margin-top: 12px;
|
|
}
|
|
|
|
.commentsComposer__hint,
|
|
.commentsLoginCta__text {
|
|
color: var(--theme-text-muted);
|
|
font-size: 13px;
|
|
}
|
|
|
|
.commentsCard__empty {
|
|
padding: 20px 0;
|
|
color: var(--theme-text-muted);
|
|
}
|
|
|
|
.commentsThread {
|
|
display: grid;
|
|
gap: 16px;
|
|
}
|
|
|
|
.commentItem {
|
|
position: relative;
|
|
padding: 18px 18px 16px;
|
|
border-radius: 24px;
|
|
background: linear-gradient(180deg, color-mix(in srgb, var(--theme-surface) 88%, var(--theme-surface-soft)) 0%, var(--theme-surface) 100%);
|
|
box-shadow:
|
|
inset 0 1px 0 color-mix(in srgb, white 6%, transparent),
|
|
0 14px 32px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.commentItem--reply {
|
|
margin-top: 12px;
|
|
margin-left: 28px;
|
|
border-radius: 20px;
|
|
background: linear-gradient(180deg, color-mix(in srgb, var(--theme-accent) 6%, var(--theme-surface)) 0%, var(--theme-surface) 100%);
|
|
box-shadow:
|
|
inset 0 1px 0 color-mix(in srgb, white 4%, transparent),
|
|
0 10px 24px rgba(0, 0, 0, 0.06);
|
|
}
|
|
|
|
.commentItem--reply::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 22px;
|
|
left: -16px;
|
|
width: 12px;
|
|
height: calc(100% - 44px);
|
|
border-left: 2px solid color-mix(in srgb, var(--theme-accent) 36%, var(--theme-card-border));
|
|
border-bottom: 2px solid color-mix(in srgb, var(--theme-accent) 36%, var(--theme-card-border));
|
|
border-bottom-left-radius: 14px;
|
|
}
|
|
|
|
.commentItem--highlighted {
|
|
border-color: color-mix(in srgb, var(--theme-accent) 65%, var(--theme-card-border));
|
|
box-shadow: 0 0 0 1px color-mix(in srgb, var(--theme-accent) 38%, transparent);
|
|
}
|
|
|
|
.commentItem__head {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.commentItem__author {
|
|
min-width: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.commentItem__avatar {
|
|
width: 38px;
|
|
height: 38px;
|
|
border-radius: 999px;
|
|
object-fit: cover;
|
|
border: 1px solid var(--theme-avatar-border);
|
|
background: var(--theme-border);
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.commentItem__avatar--fallback {
|
|
display: grid;
|
|
place-items: center;
|
|
font-size: 14px;
|
|
font-weight: 900;
|
|
}
|
|
|
|
.commentItem__meta {
|
|
min-width: 0;
|
|
}
|
|
|
|
.commentItem__metaTop {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.commentItem__name {
|
|
font-size: 14px;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.commentItem__badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 24px;
|
|
padding: 0 9px;
|
|
border-radius: 999px;
|
|
background: color-mix(in srgb, var(--theme-surface-soft) 84%, transparent);
|
|
color: var(--theme-text-muted);
|
|
font-size: 11px;
|
|
font-weight: 900;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.commentItem__badge--reply {
|
|
background: color-mix(in srgb, var(--theme-accent) 15%, var(--theme-surface-soft));
|
|
color: var(--theme-text);
|
|
}
|
|
|
|
.commentItem__date {
|
|
margin-top: 4px;
|
|
font-size: 12px;
|
|
color: var(--theme-text-faint);
|
|
}
|
|
|
|
.commentItem__actions {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 8px;
|
|
}
|
|
|
|
.commentItem__action {
|
|
border: 0;
|
|
background: transparent;
|
|
color: var(--theme-text-muted);
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.commentItem__action--danger {
|
|
color: var(--theme-danger-text, #ff8f8f);
|
|
}
|
|
|
|
.commentItem__body {
|
|
padding: 14px 15px;
|
|
border-radius: 18px;
|
|
background: color-mix(in srgb, var(--theme-input-bg) 82%, var(--theme-surface));
|
|
box-shadow:
|
|
inset 0 0 0 1px color-mix(in srgb, var(--theme-card-border) 28%, transparent),
|
|
0 8px 18px rgba(0, 0, 0, 0.04);
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.replyComposer {
|
|
margin-top: 14px;
|
|
padding: 14px;
|
|
border-radius: 18px;
|
|
background: color-mix(in srgb, var(--theme-surface) 76%, var(--theme-surface-soft));
|
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--theme-card-border) 24%, transparent);
|
|
}
|
|
|
|
.commentsComposer__submit {
|
|
min-width: 112px;
|
|
min-height: 42px;
|
|
}
|
|
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
min-height: 42px;
|
|
padding: 10px 16px;
|
|
border-radius: 14px;
|
|
border: 1px solid color-mix(in srgb, var(--theme-border) 78%, transparent);
|
|
background: color-mix(in srgb, var(--theme-surface-soft) 88%, transparent);
|
|
color: var(--theme-text);
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 800;
|
|
transition: transform 160ms ease, background 160ms ease, box-shadow 160ms ease;
|
|
}
|
|
|
|
.btn:hover {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn:disabled {
|
|
opacity: 0.58;
|
|
cursor: default;
|
|
transform: none;
|
|
}
|
|
|
|
.btn--save {
|
|
min-width: 112px;
|
|
background: rgba(96, 165, 250, 0.22);
|
|
border-color: rgba(96, 165, 250, 0.36);
|
|
box-shadow:
|
|
inset 0 1px 0 rgba(255, 255, 255, 0.08),
|
|
0 10px 18px rgba(59, 130, 246, 0.14);
|
|
}
|
|
|
|
.btn--save:hover {
|
|
background: rgba(96, 165, 250, 0.3);
|
|
}
|
|
|
|
.btn--ghost {
|
|
background: color-mix(in srgb, var(--theme-surface-soft) 86%, transparent);
|
|
}
|
|
|
|
@media (max-width: 860px) {
|
|
.commentsCard {
|
|
padding: 20px;
|
|
}
|
|
|
|
.commentsCard__head,
|
|
.commentsComposer__footer,
|
|
.commentsLoginCta,
|
|
.commentItem__head {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.commentItem--reply {
|
|
margin-left: 14px;
|
|
}
|
|
|
|
.commentItem--reply::before {
|
|
left: -10px;
|
|
width: 8px;
|
|
}
|
|
}
|
|
</style>
|