refactor(css): 단일 style.css 를 역할별 파일로 분할 + Pretendard 로컬 호스팅
public/css/{tokens,base,layout,components,modal}.css 로 분리하고 _header 에서 다중 link 로드.
tokens.css 에 Pretendard @font-face(public/fonts/PretendardVariable.woff2, 없으면 시스템 폰트 폴백).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
69
public/css/base.css
Normal file
69
public/css/base.css
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/* 리셋 · 타이포 · 공용 유틸(.btn/.tag/.avatar) · 로그인 · 키프레임 · 반응형. */
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
html, body { margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
html, body { background: var(--bg); color: var(--fg); }
|
||||||
|
body {
|
||||||
|
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo', 'Segoe UI', 'Malgun Gothic', sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||||
|
transition: background-color .25s ease, color .2s ease;
|
||||||
|
}
|
||||||
|
::selection { background: color-mix(in srgb, var(--link) 22%, transparent); }
|
||||||
|
::placeholder { color: var(--fg3); }
|
||||||
|
a { color: inherit; text-decoration: none; }
|
||||||
|
svg { display: block; }
|
||||||
|
.tabnum { font-variant-numeric: tabular-nums; }
|
||||||
|
|
||||||
|
/* 공통 아바타 래퍼 (identicon 헬퍼가 사용) */
|
||||||
|
.avatar {
|
||||||
|
display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
|
||||||
|
overflow: hidden; background: var(--avatar-bg);
|
||||||
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fg) 6%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag { font-size: 11px; font-weight: 500; color: var(--accent-fg); background: var(--accent-bg); border-radius: 999px; padding: 1px 9px; }
|
||||||
|
|
||||||
|
/* 버튼/링크 공용 (단독 페이지·로그인) */
|
||||||
|
.btn { display: inline-flex; align-items: center; gap: 6px; background: var(--btn-bg); border: 1px solid var(--btn-bd); border-radius: 8px; padding: 8px 14px; cursor: pointer; font-family: inherit; font-size: 13.5px; font-weight: 600; color: var(--fg); transition: background .14s ease; }
|
||||||
|
.btn:hover { background: var(--btn-hover); }
|
||||||
|
.btn-sm { padding: 6px 11px; font-size: 12.5px; }
|
||||||
|
.btn-primary { background: var(--link); border-color: color-mix(in srgb, var(--fg) 12%, transparent); color: #fff; }
|
||||||
|
.btn-primary:hover { filter: brightness(1.06); }
|
||||||
|
.btn-success { background: var(--success); border-color: color-mix(in srgb, var(--fg) 12%, transparent); color: #fff; }
|
||||||
|
.btn-success:hover { background: var(--success-hover); }
|
||||||
|
.muted { color: var(--fg2); }
|
||||||
|
.back-link { font-size: 13px; color: var(--fg2); }
|
||||||
|
.back-link:hover { color: var(--link); }
|
||||||
|
|
||||||
|
/* 로그인 폴백 페이지 */
|
||||||
|
.login-wrap { min-height: calc(100vh - 60px); display: flex; align-items: center; justify-content: center; padding: 24px; }
|
||||||
|
.login-card { width: 100%; max-width: 360px; background: var(--card); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 8px 24px var(--shadow); padding: 32px 28px; text-align: center; }
|
||||||
|
.login-logo { margin-bottom: 12px; }
|
||||||
|
.login-title { font-size: 20px; font-weight: 800; letter-spacing: .06em; margin-bottom: 18px; }
|
||||||
|
.alert { background: var(--attn-bg); border: 1px solid var(--attn-bd); color: var(--attn-fg); border-radius: 8px; padding: 10px 12px; font-size: 13px; margin-bottom: 16px; }
|
||||||
|
|
||||||
|
/* 단독 상세 페이지(project.ejs) */
|
||||||
|
.detail-wrap { max-width: 760px; margin: 0 auto; padding: 28px 24px 64px; }
|
||||||
|
|
||||||
|
/* 애니메이션 키프레임 */
|
||||||
|
@keyframes aidev-flyup { 0%{transform:translateY(0) scale(.5);opacity:0} 12%{opacity:1} 100%{transform:translateY(-180px) scale(1.15);opacity:0} }
|
||||||
|
@keyframes aidev-sparkle { 0%{transform:scale(0) rotate(0);opacity:1} 100%{transform:scale(1.5) rotate(120deg);opacity:0} }
|
||||||
|
@keyframes aidev-rocket { 0%{transform:translateX(-40px) rotate(-12deg);opacity:0} 12%{opacity:1} 88%{opacity:1} 100%{transform:translateX(calc(100vw + 60px)) rotate(-12deg);opacity:0} }
|
||||||
|
@keyframes aidev-shake { 10%,90%{transform:translateX(-1px) rotate(-1deg)} 20%,80%{transform:translateX(2px) rotate(1.5deg)} 30%,50%,70%{transform:translateX(-4px) rotate(-2deg)} 40%,60%{transform:translateX(4px) rotate(2deg)} }
|
||||||
|
@keyframes aidev-stamp { 0%{transform:scale(2.4) rotate(-22deg);opacity:0} 55%{transform:scale(.86) rotate(-11deg);opacity:1} 100%{transform:scale(1) rotate(-11deg);opacity:1} }
|
||||||
|
@keyframes aidev-pop { 0%{transform:scale(.96) translateY(6px);opacity:0} 100%{transform:scale(1) translateY(0);opacity:1} }
|
||||||
|
@keyframes aidev-fade { from{opacity:0} to{opacity:1} }
|
||||||
|
@keyframes aidev-toast { 0%{transform:translateX(-50%) translateY(12px);opacity:0} 12%{transform:translateX(-50%) translateY(0);opacity:1} 88%{transform:translateX(-50%) translateY(0);opacity:1} 100%{transform:translateX(-50%) translateY(8px);opacity:0} }
|
||||||
|
|
||||||
|
/* 반응형 */
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.tools-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.myproj-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.feed-grid { grid-template-columns: 1fr; }
|
||||||
|
.brand-sub { display: none; }
|
||||||
|
}
|
||||||
|
@media (max-width: 560px) {
|
||||||
|
.tools-grid, .myproj-grid { grid-template-columns: 1fr; }
|
||||||
|
.hdr-search { display: none; }
|
||||||
|
}
|
||||||
138
public/css/components.css
Normal file
138
public/css/components.css
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/* 콘텐츠 컴포넌트 — 개발 도구 · 내 프로젝트 · 공유 피드 카드. */
|
||||||
|
|
||||||
|
/* ===================== ① 개발 도구 ===================== */
|
||||||
|
.tools-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
|
||||||
|
.tool-card {
|
||||||
|
position: relative; text-align: left; background: var(--card); border: 1px solid var(--border-soft);
|
||||||
|
border-radius: 12px; padding: 16px; cursor: pointer; font-family: inherit; display: block;
|
||||||
|
transition: box-shadow .16s ease, border-color .16s ease, transform .16s ease;
|
||||||
|
}
|
||||||
|
.tool-card:hover { border-color: var(--link); box-shadow: 0 6px 18px var(--shadow-hover); transform: translateY(-3px); }
|
||||||
|
.tool-card.admin { border-color: var(--admin-bd); }
|
||||||
|
.tool-icon {
|
||||||
|
width: 44px; height: 44px; border-radius: 11px; display: inline-flex; align-items: center; justify-content: center;
|
||||||
|
font-size: 24px; background: var(--tile); border: 1px solid var(--tile-bd);
|
||||||
|
}
|
||||||
|
.tool-card.admin .tool-icon { background: var(--admin-bg); border-color: var(--admin-bd); }
|
||||||
|
.tool-name { display: block; margin-top: 14px; font-size: 14.5px; font-weight: 700; color: var(--fg); }
|
||||||
|
.tool-desc { display: block; margin-top: 3px; font-size: 12.5px; color: var(--fg2); line-height: 1.45; }
|
||||||
|
.sso-badge {
|
||||||
|
position: absolute; top: 13px; right: 13px; font-size: 10px; font-weight: 700; letter-spacing: .06em;
|
||||||
|
color: var(--accent-fg); background: var(--accent-bg); border: 1px solid var(--accent-bd); border-radius: 4px; padding: 2px 6px;
|
||||||
|
}
|
||||||
|
.tool-admin-badge {
|
||||||
|
display: inline-flex; align-items: center; gap: 5px; margin-top: 11px; font-size: 11px; font-weight: 600;
|
||||||
|
color: var(--admin-fg); background: var(--admin-bg); border: 1px solid var(--admin-bd); border-radius: 999px; padding: 2px 8px; white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== ② 내 프로젝트 ===================== */
|
||||||
|
.myproj { position: relative; background: var(--panel); border: 1px solid var(--border-soft); border-radius: 14px; padding: 22px 22px 24px; }
|
||||||
|
.myproj-accent { position: absolute; left: 0; top: 18px; bottom: 18px; width: 4px; border-radius: 0 4px 4px 0; background: var(--link); }
|
||||||
|
.myproj-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; padding-left: 8px; flex-wrap: wrap; }
|
||||||
|
.myproj-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||||||
|
.filter-tabs { display: inline-flex; align-items: center; background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 2px; gap: 2px; }
|
||||||
|
.filter-tab {
|
||||||
|
display: inline-flex; align-items: center; gap: 5px; background: transparent; border: 0; border-radius: 6px; padding: 5px 11px;
|
||||||
|
cursor: pointer; font-family: inherit; font-size: 12.5px; font-weight: 600; color: var(--fg2); white-space: nowrap;
|
||||||
|
transition: background .14s ease, color .14s ease;
|
||||||
|
}
|
||||||
|
.filter-tab.on { background: var(--panel); color: var(--fg); box-shadow: inset 0 0 0 1px var(--border); }
|
||||||
|
.new-proj-btn {
|
||||||
|
display: inline-flex; align-items: center; gap: 6px; background: var(--success);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent); border-radius: 8px; padding: 7px 13px;
|
||||||
|
cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 700; color: #fff; white-space: nowrap; transition: background .14s ease;
|
||||||
|
}
|
||||||
|
.new-proj-btn:hover { background: var(--success-hover); }
|
||||||
|
.myproj-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; padding-left: 8px; }
|
||||||
|
|
||||||
|
/* 프로젝트 카드 (내 프로젝트 / 피드 공용 베이스) */
|
||||||
|
.pcard {
|
||||||
|
background: var(--card); border: 1px solid var(--border-soft); border-radius: 12px; padding: 15px 16px 14px;
|
||||||
|
cursor: pointer; transition: box-shadow .16s ease, border-color .16s ease, transform .16s ease; display: flex; flex-direction: column;
|
||||||
|
}
|
||||||
|
.pcard:hover { border-color: var(--link); box-shadow: 0 6px 18px var(--shadow); transform: translateY(-2px); }
|
||||||
|
.pcard.private { background: var(--bg); border: 1.5px dashed var(--border); }
|
||||||
|
.pcard-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
||||||
|
.pcard-titlewrap { display: flex; align-items: center; gap: 7px; min-width: 0; }
|
||||||
|
.pcard-repoico { display: inline-flex; color: var(--fg2); flex: 0 0 auto; }
|
||||||
|
.pcard-title { font-size: 14px; font-weight: 700; color: var(--link); line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.pcard-desc { margin-top: 8px; font-size: 12.5px; color: var(--fg2); line-height: 1.5; flex: 1; }
|
||||||
|
.pcard-tags { margin-top: 11px; display: flex; flex-wrap: wrap; gap: 6px; }
|
||||||
|
.pcard-foot { margin-top: 13px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
||||||
|
.pcard-stats { display: flex; align-items: center; gap: 13px; font-size: 12px; color: var(--fg2); }
|
||||||
|
.pcard-stats .stat { display: inline-flex; align-items: center; gap: 4px; }
|
||||||
|
.lang-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
|
||||||
|
.ico-heart { display: inline-flex; color: var(--heart); }
|
||||||
|
.ico-star { display: inline-flex; color: var(--attn); }
|
||||||
|
.ico-comment { display: inline-flex; color: var(--fg2); }
|
||||||
|
.vis-badge {
|
||||||
|
display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; font-size: 11px; font-weight: 600;
|
||||||
|
border-radius: 999px; padding: 1px 8px 1px 7px; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.vis-badge.public { color: var(--success-fg); background: var(--success-bg); border: 1px solid var(--success-bd); }
|
||||||
|
.vis-badge.private { color: var(--fg2); background: var(--panel); border: 1px solid var(--border); }
|
||||||
|
.toggle-share-btn { background: transparent; border: 0; padding: 0; cursor: pointer; font-family: inherit; font-size: 11.5px; font-weight: 600; color: var(--fg3); white-space: nowrap; }
|
||||||
|
.toggle-share-btn:hover { color: var(--fg); }
|
||||||
|
.private-note { font-size: 11.5px; color: var(--fg3); }
|
||||||
|
.share-mini-btn {
|
||||||
|
display: inline-flex; align-items: center; gap: 5px; background: var(--success);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent); border-radius: 7px; padding: 4px 10px;
|
||||||
|
cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 700; color: #fff; white-space: nowrap; transition: background .14s ease;
|
||||||
|
}
|
||||||
|
.share-mini-btn:hover { background: var(--success-hover); }
|
||||||
|
.add-card {
|
||||||
|
background: transparent; border: 1.5px dashed var(--border); border-radius: 12px; padding: 16px; cursor: pointer; font-family: inherit;
|
||||||
|
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: var(--fg2); min-height: 158px; width: 100%;
|
||||||
|
transition: border-color .16s ease, color .16s ease, background .16s ease;
|
||||||
|
}
|
||||||
|
.add-card:hover { border-color: var(--success); color: var(--success); background: var(--card); }
|
||||||
|
.add-card-plus { width: 38px; height: 38px; border-radius: 50%; background: var(--success-bg); border: 1px solid var(--success-bd); display: inline-flex; align-items: center; justify-content: center; color: var(--success); }
|
||||||
|
.add-card-title { font-size: 13.5px; font-weight: 700; white-space: nowrap; }
|
||||||
|
.add-card-sub { font-size: 11.5px; text-align: center; line-height: 1.4; }
|
||||||
|
.empty { grid-column: 1 / -1; text-align: center; padding: 30px; color: var(--fg2); font-size: 13px; border: 1px dashed var(--border); border-radius: 12px; }
|
||||||
|
|
||||||
|
/* ===================== ③ 공유 피드 ===================== */
|
||||||
|
.feed-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; margin-bottom: 16px; border-bottom: 1px solid var(--border-soft); }
|
||||||
|
.feed-head .section-titlewrap { padding-bottom: 14px; }
|
||||||
|
.feed-head-right { display: flex; align-items: flex-end; gap: 14px; }
|
||||||
|
.feed-star-btn {
|
||||||
|
display: inline-flex; align-items: center; gap: 6px; background: transparent; border: 1px solid var(--btn-bd); border-radius: 7px;
|
||||||
|
padding: 6px 12px; margin-bottom: 8px; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--fg2); white-space: nowrap;
|
||||||
|
transition: background .14s ease, border-color .14s ease, color .14s ease;
|
||||||
|
}
|
||||||
|
.feed-star-btn:hover { border-color: var(--attn-bd); color: var(--attn-fg); }
|
||||||
|
.feed-star-btn.on { background: var(--attn-bg); border-color: var(--attn-bd); color: var(--attn-fg); }
|
||||||
|
.feed-star-btn .ico { display: inline-flex; }
|
||||||
|
.sort-tabs { display: flex; gap: 2px; }
|
||||||
|
.sort-tab { background: transparent; border: 0; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; padding: 8px 14px 14px; color: var(--fg2); transition: color .14s ease; }
|
||||||
|
.sort-tab.on { color: var(--fg); box-shadow: inset 0 -2px 0 var(--tab-underline); }
|
||||||
|
.feed-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
||||||
|
|
||||||
|
.fcard { background: var(--card); border: 1px solid var(--border-soft); border-radius: 12px; padding: 18px; cursor: pointer; transition: box-shadow .16s ease, border-color .16s ease, transform .16s ease; display: flex; flex-direction: column; }
|
||||||
|
.fcard:hover { border-color: var(--link); box-shadow: 0 6px 18px var(--shadow); transform: translateY(-2px); }
|
||||||
|
.fcard-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
|
||||||
|
.fcard-titlewrap { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
||||||
|
.fcard-title { font-size: 15px; font-weight: 700; color: var(--link); line-height: 1.3; }
|
||||||
|
.react-btns { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
|
||||||
|
.react-btn {
|
||||||
|
display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; background: var(--btn-bg); border: 1px solid var(--btn-bd);
|
||||||
|
border-radius: 7px; padding: 4px 9px; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--fg);
|
||||||
|
transition: background .14s ease, border-color .14s ease, color .14s ease;
|
||||||
|
}
|
||||||
|
.react-btn .count { font-variant-numeric: tabular-nums; }
|
||||||
|
.react-btn.heart.on { background: var(--heart-bg); border-color: var(--heart-bd); color: var(--heart-fg); }
|
||||||
|
.react-btn.star.on { background: var(--attn-bg); border-color: var(--attn-bd); color: var(--attn-fg); }
|
||||||
|
/* 채움(on) / 외곽선(off) 아이콘 토글 — JS 가 .on 클래스만 바꾸면 됨 */
|
||||||
|
.react-btn .ico, .modal-action .ico { display: inline-flex; }
|
||||||
|
.react-btn .ico-on, .modal-action .ico-on { display: none; }
|
||||||
|
.react-btn.on .ico-on, .modal-action.on .ico-on { display: inline-flex; }
|
||||||
|
.react-btn.on .ico-off, .modal-action.on .ico-off { display: none; }
|
||||||
|
.fcard-desc { margin-top: 8px; font-size: 13px; color: var(--fg2); line-height: 1.55; }
|
||||||
|
.fcard-tags { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 6px; }
|
||||||
|
.fcard-foot { margin-top: 15px; display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
|
||||||
|
.fcard-author { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
||||||
|
.fcard-author .sabun { font-size: 12.5px; font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
|
||||||
|
.fcard-author .date { font-size: 11.5px; color: var(--fg3); }
|
||||||
|
.fcard-meta { display: flex; align-items: center; gap: 13px; font-size: 12px; color: var(--fg2); }
|
||||||
|
.fcard-meta .stat { display: inline-flex; align-items: center; gap: 5px; }
|
||||||
|
.bara-link { display: inline-flex; align-items: center; gap: 4px; background: transparent; border: 0; padding: 0; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--link); white-space: nowrap; }
|
||||||
71
public/css/layout.css
Normal file
71
public/css/layout.css
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/* 골격 — 헤더 · 메인/섹션 · 푸터. */
|
||||||
|
|
||||||
|
/* ===================== 헤더 ===================== */
|
||||||
|
.hdr { position: sticky; top: 0; z-index: 300; background: var(--hbg); border-bottom: 1px solid var(--hbd); }
|
||||||
|
.hdr-inner { max-width: 1180px; margin: 0 auto; padding: 0 24px; height: 60px; display: flex; align-items: center; gap: 16px; }
|
||||||
|
.brand { display: flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; flex: 0 0 auto; }
|
||||||
|
.brand-mark { display: inline-flex; color: var(--hfg); transition: transform .2s ease, filter .2s ease; }
|
||||||
|
.brand-name { font-weight: 800; font-size: 15px; letter-spacing: .13em; color: var(--hfg); }
|
||||||
|
.brand-sub { font-size: 11px; color: var(--hfg2); font-weight: 500; border-left: 1px solid var(--hbd); padding-left: 10px; margin-left: 2px; white-space: nowrap; }
|
||||||
|
|
||||||
|
.hdr-search { flex: 1; max-width: 300px; position: relative; display: flex; align-items: center; }
|
||||||
|
.hdr-search-ico { position: absolute; left: 10px; display: inline-flex; color: var(--hfg2); pointer-events: none; }
|
||||||
|
.hdr-search input {
|
||||||
|
width: 100%; font-family: inherit; font-size: 13px; color: var(--hfg);
|
||||||
|
background: var(--hsearch-bg); border: 1px solid var(--hsearch-bd); border-radius: 7px;
|
||||||
|
padding: 6px 10px 6px 32px; outline: none;
|
||||||
|
}
|
||||||
|
.hdr-search input:focus { border-color: var(--link); background: var(--bg); color: var(--fg); }
|
||||||
|
.hdr-search input::-webkit-search-cancel-button { filter: invert(.5); }
|
||||||
|
|
||||||
|
.hdr-right { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; margin-left: auto; }
|
||||||
|
.icon-btn {
|
||||||
|
width: 32px; height: 32px; border-radius: 8px; border: 1px solid var(--hbd); background: transparent;
|
||||||
|
cursor: pointer; display: inline-flex; align-items: center; justify-content: center; color: var(--hfg2);
|
||||||
|
transition: background .14s ease, color .14s ease;
|
||||||
|
}
|
||||||
|
.icon-btn:hover { background: rgba(255,255,255,.08); color: var(--hfg); }
|
||||||
|
.theme-ico-sun { display: none; }
|
||||||
|
html[data-aidev-theme="dark"] .theme-ico-moon { display: none; }
|
||||||
|
html[data-aidev-theme="dark"] .theme-ico-sun { display: inline-flex; }
|
||||||
|
|
||||||
|
.admin-pill {
|
||||||
|
display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600;
|
||||||
|
color: #fff; background: color-mix(in srgb, var(--admin) 80%, transparent);
|
||||||
|
border: 1px solid var(--hbd); border-radius: 999px; padding: 4px 11px; white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usermenu { position: relative; }
|
||||||
|
.usermenu-btn {
|
||||||
|
display: flex; align-items: center; gap: 8px; background: transparent; border: 1px solid transparent;
|
||||||
|
border-radius: 8px; padding: 4px 6px 4px 8px; cursor: pointer; font-family: inherit;
|
||||||
|
}
|
||||||
|
.usermenu-btn:hover { background: rgba(255,255,255,.08); }
|
||||||
|
.usermenu-btn .who { font-size: 13px; font-weight: 600; color: var(--hfg); font-variant-numeric: tabular-nums; }
|
||||||
|
.usermenu-btn .caret { display: inline-flex; color: var(--hfg2); }
|
||||||
|
.usermenu-pop {
|
||||||
|
position: absolute; top: 46px; right: 0; width: 212px; background: var(--card); border: 1px solid var(--border);
|
||||||
|
border-radius: 10px; box-shadow: 0 8px 24px var(--shadow-strong); padding: 6px; z-index: 320; animation: aidev-pop .14s ease-out;
|
||||||
|
}
|
||||||
|
.usermenu-pop[hidden] { display: none; }
|
||||||
|
.usermenu-head { padding: 8px 10px 10px; border-bottom: 1px solid var(--divider); margin-bottom: 6px; }
|
||||||
|
.usermenu-label { font-size: 12px; color: var(--fg2); }
|
||||||
|
.usermenu-sabun { font-size: 13.5px; font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
|
||||||
|
.usermenu-role { font-size: 11.5px; color: var(--fg2); margin-top: 2px; }
|
||||||
|
.usermenu-item { width: 100%; text-align: left; background: transparent; border: 0; border-radius: 6px; padding: 8px 10px; cursor: pointer; font-family: inherit; font-size: 13px; color: var(--fg); display: flex; align-items: center; gap: 8px; }
|
||||||
|
.usermenu-item:hover { background: var(--panel); }
|
||||||
|
.usermenu-ico { display: inline-flex; color: var(--fg2); }
|
||||||
|
|
||||||
|
/* ===================== 메인 / 섹션 ===================== */
|
||||||
|
.main { max-width: 1180px; margin: 0 auto; padding: 32px 24px 64px; }
|
||||||
|
.section { margin-bottom: 40px; }
|
||||||
|
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
|
||||||
|
.section-titlewrap { display: flex; align-items: baseline; gap: 10px; }
|
||||||
|
.section-title { margin: 0; font-size: 18px; font-weight: 700; color: var(--fg); letter-spacing: -.01em; }
|
||||||
|
.section-sub { font-size: 13px; color: var(--fg2); }
|
||||||
|
|
||||||
|
/* ===================== 푸터 ===================== */
|
||||||
|
.footer { border-top: 1px solid var(--border-soft); }
|
||||||
|
.footer-inner { max-width: 1180px; margin: 0 auto; padding: 20px 24px 28px; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
|
||||||
|
.footer-brand { display: flex; align-items: center; gap: 8px; color: var(--fg3); font-size: 12px; }
|
||||||
|
.footer-tech { font-size: 11.5px; color: var(--fg3); font-family: 'SF Mono', ui-monospace, Menlo, monospace; }
|
||||||
88
public/css/modal.css
Normal file
88
public/css/modal.css
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/* 모달 — 상세 모달 · 댓글 스레드 · 공유(등록) 모달 · 토스트 · 플라이어. */
|
||||||
|
|
||||||
|
/* ===================== 상세 모달 ===================== */
|
||||||
|
.modal-overlay { position: fixed; inset: 0; z-index: 500; background: var(--overlay); display: flex; align-items: flex-start; justify-content: center; padding: 48px 20px; overflow-y: auto; animation: aidev-fade .16s ease-out; }
|
||||||
|
.modal-overlay[hidden] { display: none; }
|
||||||
|
.modal { width: 100%; max-width: 640px; background: var(--card); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 16px 48px var(--shadow-strong); overflow: hidden; animation: aidev-pop .18s ease-out; }
|
||||||
|
.modal-head { padding: 22px 24px 18px; border-bottom: 1px solid var(--divider); }
|
||||||
|
.modal-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
|
||||||
|
.modal-titlewrap { min-width: 0; }
|
||||||
|
.modal-title-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||||
|
.modal-repoico { display: inline-flex; color: var(--fg2); }
|
||||||
|
.modal-title { margin: 0; font-size: 20px; font-weight: 800; color: var(--link); line-height: 1.3; letter-spacing: -.01em; }
|
||||||
|
.modal-meta { margin-top: 10px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||||
|
.modal-meta .sabun { font-size: 13px; font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
|
||||||
|
.modal-meta .date { font-size: 12px; color: var(--fg3); }
|
||||||
|
.modal-meta .lang { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--fg2); margin-left: 4px; }
|
||||||
|
.modal-close { flex: 0 0 auto; width: 32px; height: 32px; border-radius: 8px; border: 1px solid transparent; background: transparent; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; color: var(--fg2); }
|
||||||
|
.modal-close:hover { background: var(--panel); border-color: var(--border-soft); }
|
||||||
|
.modal-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
|
||||||
|
.modal-desc { margin: 14px 0 0; font-size: 14px; color: var(--fg); line-height: 1.65; white-space: pre-wrap; }
|
||||||
|
.modal-actions { margin-top: 18px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||||||
|
.modal-action {
|
||||||
|
display: inline-flex; align-items: center; gap: 7px; background: var(--btn-bg); border: 1px solid var(--btn-bd); border-radius: 8px;
|
||||||
|
padding: 8px 14px; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--fg);
|
||||||
|
transition: background .14s ease, border-color .14s ease, color .14s ease;
|
||||||
|
}
|
||||||
|
.modal-action .count { font-variant-numeric: tabular-nums; padding-left: 8px; margin-left: 2px; border-left: 1px solid var(--border); }
|
||||||
|
.modal-action.heart.on { background: var(--heart-bg); border-color: var(--heart-bd); color: var(--heart-fg); }
|
||||||
|
.modal-action.star.on { background: var(--attn-bg); border-color: var(--attn-bd); color: var(--attn-fg); }
|
||||||
|
.modal-action.primary { background: var(--success); border-color: color-mix(in srgb, var(--fg) 12%, transparent); color: #fff; }
|
||||||
|
.modal-action.primary:hover { background: var(--success-hover); }
|
||||||
|
.modal-action.ghost:hover { background: var(--btn-hover); }
|
||||||
|
|
||||||
|
/* 댓글 스레드 */
|
||||||
|
.modal-body { padding: 18px 24px 4px; }
|
||||||
|
.comments-title { font-size: 13px; font-weight: 700; color: var(--fg); margin-bottom: 14px; display: flex; align-items: center; gap: 7px; }
|
||||||
|
.comments-count { font-weight: 600; color: var(--fg2); background: var(--panel); border: 1px solid var(--border-soft); border-radius: 999px; padding: 0 8px; font-size: 12px; font-variant-numeric: tabular-nums; }
|
||||||
|
.comment-list { display: flex; flex-direction: column; gap: 4px; }
|
||||||
|
.comment { display: flex; gap: 11px; padding: 11px 0; }
|
||||||
|
.comment-main { min-width: 0; flex: 1; }
|
||||||
|
.comment-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||||
|
.comment-author { font-size: 13px; font-weight: 700; color: var(--fg); font-variant-numeric: tabular-nums; }
|
||||||
|
.comment-time { font-size: 11.5px; color: var(--fg3); }
|
||||||
|
.lgtm { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 800; letter-spacing: .04em; color: var(--success-fg); background: var(--success-bg); border: 1px solid var(--success-bd2); border-radius: 5px; padding: 1px 7px; animation: aidev-stamp .42s cubic-bezier(.2,.8,.3,1.2); }
|
||||||
|
.comment-text { margin-top: 3px; font-size: 13.5px; color: var(--fg); line-height: 1.6; white-space: pre-wrap; }
|
||||||
|
.comment-actions { margin-top: 5px; display: flex; align-items: center; gap: 12px; }
|
||||||
|
.comment-link { background: transparent; border: 0; padding: 0; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--fg2); display: inline-flex; align-items: center; gap: 5px; }
|
||||||
|
.comment-link:hover { color: var(--link); }
|
||||||
|
.comment-del:hover { color: var(--danger); }
|
||||||
|
.replies { margin-left: 21px; padding-left: 18px; border-left: 2px solid var(--divider); display: flex; flex-direction: column; }
|
||||||
|
.reply { display: flex; gap: 10px; padding: 10px 0; }
|
||||||
|
.reply .comment-author { font-size: 12.5px; }
|
||||||
|
.reply .comment-text { font-size: 13px; }
|
||||||
|
|
||||||
|
.modal-foot { padding: 14px 24px 22px; border-top: 1px solid var(--divider); margin-top: 6px; background: var(--bg-soft); }
|
||||||
|
.reply-banner { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 9px; background: var(--accent-bg); border: 1px solid var(--accent-bd); border-radius: 7px; padding: 6px 10px; }
|
||||||
|
.reply-banner span { font-size: 12px; color: var(--accent-strong); }
|
||||||
|
.reply-banner button { background: transparent; border: 0; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--link); }
|
||||||
|
.comment-form { display: flex; gap: 10px; align-items: flex-end; }
|
||||||
|
.comment-input { flex: 1; resize: none; font-family: inherit; font-size: 13.5px; line-height: 1.5; color: var(--fg); background: var(--inset); border: 1px solid var(--border); border-radius: 9px; padding: 9px 12px; outline: none; min-height: 40px; }
|
||||||
|
.comment-input:focus { border-color: var(--link); box-shadow: 0 0 0 3px var(--focus-ring); }
|
||||||
|
.submit-btn { flex: 0 0 auto; background: var(--success); border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent); border-radius: 9px; padding: 10px 16px; cursor: pointer; font-family: inherit; font-size: 13.5px; font-weight: 700; color: #fff; align-self: stretch; white-space: nowrap; transition: background .14s ease; }
|
||||||
|
.submit-btn:hover { background: var(--success-hover); }
|
||||||
|
.comment-hint { margin-top: 7px; font-size: 11px; color: var(--fg3); padding-left: 38px; }
|
||||||
|
|
||||||
|
/* ===================== 공유(등록) 모달 ===================== */
|
||||||
|
.share-modal { width: 100%; max-width: 520px; background: var(--card); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 16px 48px var(--shadow-strong); overflow: hidden; animation: aidev-pop .18s ease-out; }
|
||||||
|
.share-head { padding: 20px 24px; border-bottom: 1px solid var(--divider); display: flex; align-items: center; justify-content: space-between; }
|
||||||
|
.share-head h3 { margin: 0; font-size: 17px; font-weight: 800; color: var(--fg); }
|
||||||
|
.share-body { padding: 20px 24px; display: flex; flex-direction: column; gap: 15px; }
|
||||||
|
.share-label { display: block; font-size: 12.5px; font-weight: 700; color: var(--fg); margin-bottom: 6px; }
|
||||||
|
.share-label .req { color: var(--danger); }
|
||||||
|
.share-label .hint { font-weight: 500; color: var(--fg3); }
|
||||||
|
.share-input { width: 100%; font-family: inherit; font-size: 14px; color: var(--fg); background: var(--inset); border: 1px solid var(--border); border-radius: 8px; padding: 9px 12px; outline: none; }
|
||||||
|
.share-input.mono { font-family: 'SF Mono', ui-monospace, Menlo, monospace; font-size: 13px; }
|
||||||
|
.share-input:focus { border-color: var(--link); box-shadow: 0 0 0 3px var(--focus-ring); }
|
||||||
|
.vis-toggle { display: inline-flex; background: var(--inset); border: 1px solid var(--border); border-radius: 8px; padding: 3px; gap: 3px; }
|
||||||
|
.vis-opt { display: inline-flex; align-items: center; gap: 5px; background: transparent; border: 0; border-radius: 6px; padding: 6px 16px; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--fg2); white-space: nowrap; transition: background .14s ease, color .14s ease; }
|
||||||
|
.vis-opt.on-public { background: var(--success-bg); color: var(--success-fg); box-shadow: inset 0 0 0 1px var(--success-bd); }
|
||||||
|
.vis-opt.on-private { background: var(--panel); color: var(--fg); box-shadow: inset 0 0 0 1px var(--border); }
|
||||||
|
.share-vishint { margin-top: 7px; font-size: 11.5px; color: var(--fg3); }
|
||||||
|
.modal-foot-actions { padding: 16px 24px; border-top: 1px solid var(--divider); background: var(--bg-soft); display: flex; align-items: center; justify-content: flex-end; gap: 9px; }
|
||||||
|
|
||||||
|
/* ===================== 토스트 / 날아다니는 이모지 ===================== */
|
||||||
|
.toast { position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%); z-index: 600; background: #1f2328; color: #fff; border: 1px solid rgba(255,255,255,.12); border-radius: 10px; padding: 11px 16px; font-size: 13px; font-weight: 500; box-shadow: 0 8px 24px rgba(1,4,9,.4); display: flex; align-items: center; gap: 9px; max-width: 90vw; animation: aidev-toast 2.6s ease forwards; }
|
||||||
|
.toast .toast-ico { display: inline-flex; color: #3fb950; }
|
||||||
|
.flyers { position: fixed; inset: 0; z-index: 650; pointer-events: none; overflow: hidden; }
|
||||||
|
.flyer { position: fixed; pointer-events: none; user-select: none; will-change: transform, opacity; }
|
||||||
46
public/css/tokens.css
Normal file
46
public/css/tokens.css
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/* 디자인 토큰 — 폰트 + CSS 변수(라이트/다크). 가장 먼저 로드. */
|
||||||
|
|
||||||
|
/* Pretendard — 폐쇄망 대비 로컬 호스팅(가변 폰트 1개). 파일이 없으면 시스템 폰트로 폴백.
|
||||||
|
파일 받기: public/fonts/PretendardVariable.woff2 (README 안내 참고) */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Pretendard';
|
||||||
|
font-weight: 45 920;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/public/fonts/PretendardVariable.woff2') format('woff2-variations');
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
color-scheme: light;
|
||||||
|
--bg:#ffffff; --panel:#f6f8fa; --card:#ffffff; --tile:#f3f5f8; --tile-bd:#e1e6eb;
|
||||||
|
--inset:#ffffff; --bg-soft:#fbfcfd;
|
||||||
|
--border:#d0d7de; --border-soft:#d8dee4; --divider:#eaecef;
|
||||||
|
--fg:#1f2328; --fg2:#656d76; --fg3:#8b949e;
|
||||||
|
--link:#0969da; --accent-bg:#ddf4ff; --accent-bd:#b6e3ff; --accent-fg:#0969da; --accent-strong:#0a3069;
|
||||||
|
--success:#1f883d; --success-hover:#1a7f37; --success-bg:#dafbe1; --success-bd:#b7ebc4; --success-bd2:#4ac26b; --success-fg:#1a7f37;
|
||||||
|
--attn:#d4a72c; --attn-fg:#9a6700; --attn-bg:#fff8e6; --attn-bd:#eac54f;
|
||||||
|
--admin:#8250df; --admin-fg:#8250df; --admin-bg:#f3effc; --admin-bd:#e7defb;
|
||||||
|
--avatar-bg:#eaeef2; --overlay:rgba(31,35,40,.42);
|
||||||
|
--shadow:rgba(31,35,40,.08); --shadow-hover:rgba(31,35,40,.12); --shadow-strong:rgba(31,35,40,.22);
|
||||||
|
--btn-bg:#f6f8fa; --btn-bd:#d0d7de; --btn-hover:#eef1f4;
|
||||||
|
--tab-underline:#fd8c73; --focus-ring:rgba(9,105,218,.12); --danger:#cf222e;
|
||||||
|
--heart:#bf3989; --heart-fg:#bf3989; --heart-bg:#ffeff5; --heart-bd:#ffc1da;
|
||||||
|
--hbg:#24292f; --hfg:#ffffff; --hfg2:#b9c0c9; --hbd:#444c56; --hsearch-bg:#383f47; --hsearch-bd:#565b62; --hsearch-fg:#cdd3da;
|
||||||
|
}
|
||||||
|
html[data-aidev-theme="dark"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg:#0d1117; --panel:#161b22; --card:#1c2128; --tile:#2d333b; --tile-bd:#373e47;
|
||||||
|
--inset:#0d1117; --bg-soft:#161b22;
|
||||||
|
--border:#30363d; --border-soft:#373e47; --divider:#262c34;
|
||||||
|
--fg:#e6edf3; --fg2:#9198a1; --fg3:#6e7681;
|
||||||
|
--link:#4493f8; --accent-bg:rgba(56,139,253,.15); --accent-bd:rgba(56,139,253,.4); --accent-fg:#4493f8; --accent-strong:#a5d6ff;
|
||||||
|
--success:#238636; --success-hover:#2ea043; --success-bg:rgba(46,160,67,.15); --success-bd:rgba(46,160,67,.4); --success-bd2:rgba(63,185,80,.6); --success-fg:#3fb950;
|
||||||
|
--attn:#e3b341; --attn-fg:#e3b341; --attn-bg:rgba(227,179,65,.12); --attn-bd:rgba(187,128,9,.6);
|
||||||
|
--admin:#bc8cff; --admin-fg:#bc8cff; --admin-bg:rgba(163,113,247,.15); --admin-bd:rgba(163,113,247,.4);
|
||||||
|
--avatar-bg:#2d333b; --overlay:rgba(1,4,9,.7);
|
||||||
|
--shadow:rgba(1,4,9,.4); --shadow-hover:rgba(1,4,9,.55); --shadow-strong:rgba(1,4,9,.6);
|
||||||
|
--btn-bg:#21262d; --btn-bd:#30363d; --btn-hover:#30363d;
|
||||||
|
--tab-underline:#f78166; --focus-ring:rgba(56,139,253,.3); --danger:#f85149;
|
||||||
|
--heart:#db61a2; --heart-fg:#db61a2; --heart-bg:rgba(219,97,162,.15); --heart-bd:rgba(219,97,162,.4);
|
||||||
|
--hbg:#010409; --hfg:#e6edf3; --hfg2:#9198a1; --hbd:#21262d; --hsearch-bg:#0d1117; --hsearch-bd:#30363d; --hsearch-fg:#9198a1;
|
||||||
|
}
|
||||||
399
public/style.css
399
public/style.css
@@ -1,399 +0,0 @@
|
|||||||
/* AI DEV 포털 — 단일 스타일시트. (디자인 레퍼런스 GitHub 감성 / 라이트·다크)
|
|
||||||
빌드 없음: CSS 변수 + 클래스만. 다크는 html[data-aidev-theme="dark"] 로 전환. */
|
|
||||||
|
|
||||||
* { box-sizing: border-box; }
|
|
||||||
html, body { margin: 0; padding: 0; }
|
|
||||||
|
|
||||||
:root {
|
|
||||||
color-scheme: light;
|
|
||||||
--bg:#ffffff; --panel:#f6f8fa; --card:#ffffff; --tile:#f3f5f8; --tile-bd:#e1e6eb;
|
|
||||||
--inset:#ffffff; --bg-soft:#fbfcfd;
|
|
||||||
--border:#d0d7de; --border-soft:#d8dee4; --divider:#eaecef;
|
|
||||||
--fg:#1f2328; --fg2:#656d76; --fg3:#8b949e;
|
|
||||||
--link:#0969da; --accent-bg:#ddf4ff; --accent-bd:#b6e3ff; --accent-fg:#0969da; --accent-strong:#0a3069;
|
|
||||||
--success:#1f883d; --success-hover:#1a7f37; --success-bg:#dafbe1; --success-bd:#b7ebc4; --success-bd2:#4ac26b; --success-fg:#1a7f37;
|
|
||||||
--attn:#d4a72c; --attn-fg:#9a6700; --attn-bg:#fff8e6; --attn-bd:#eac54f;
|
|
||||||
--admin:#8250df; --admin-fg:#8250df; --admin-bg:#f3effc; --admin-bd:#e7defb;
|
|
||||||
--avatar-bg:#eaeef2; --overlay:rgba(31,35,40,.42);
|
|
||||||
--shadow:rgba(31,35,40,.08); --shadow-hover:rgba(31,35,40,.12); --shadow-strong:rgba(31,35,40,.22);
|
|
||||||
--btn-bg:#f6f8fa; --btn-bd:#d0d7de; --btn-hover:#eef1f4;
|
|
||||||
--tab-underline:#fd8c73; --focus-ring:rgba(9,105,218,.12); --danger:#cf222e;
|
|
||||||
--heart:#bf3989; --heart-fg:#bf3989; --heart-bg:#ffeff5; --heart-bd:#ffc1da;
|
|
||||||
--hbg:#24292f; --hfg:#ffffff; --hfg2:#b9c0c9; --hbd:#444c56; --hsearch-bg:#383f47; --hsearch-bd:#565b62; --hsearch-fg:#cdd3da;
|
|
||||||
}
|
|
||||||
html[data-aidev-theme="dark"] {
|
|
||||||
color-scheme: dark;
|
|
||||||
--bg:#0d1117; --panel:#161b22; --card:#1c2128; --tile:#2d333b; --tile-bd:#373e47;
|
|
||||||
--inset:#0d1117; --bg-soft:#161b22;
|
|
||||||
--border:#30363d; --border-soft:#373e47; --divider:#262c34;
|
|
||||||
--fg:#e6edf3; --fg2:#9198a1; --fg3:#6e7681;
|
|
||||||
--link:#4493f8; --accent-bg:rgba(56,139,253,.15); --accent-bd:rgba(56,139,253,.4); --accent-fg:#4493f8; --accent-strong:#a5d6ff;
|
|
||||||
--success:#238636; --success-hover:#2ea043; --success-bg:rgba(46,160,67,.15); --success-bd:rgba(46,160,67,.4); --success-bd2:rgba(63,185,80,.6); --success-fg:#3fb950;
|
|
||||||
--attn:#e3b341; --attn-fg:#e3b341; --attn-bg:rgba(227,179,65,.12); --attn-bd:rgba(187,128,9,.6);
|
|
||||||
--admin:#bc8cff; --admin-fg:#bc8cff; --admin-bg:rgba(163,113,247,.15); --admin-bd:rgba(163,113,247,.4);
|
|
||||||
--avatar-bg:#2d333b; --overlay:rgba(1,4,9,.7);
|
|
||||||
--shadow:rgba(1,4,9,.4); --shadow-hover:rgba(1,4,9,.55); --shadow-strong:rgba(1,4,9,.6);
|
|
||||||
--btn-bg:#21262d; --btn-bd:#30363d; --btn-hover:#30363d;
|
|
||||||
--tab-underline:#f78166; --focus-ring:rgba(56,139,253,.3); --danger:#f85149;
|
|
||||||
--heart:#db61a2; --heart-fg:#db61a2; --heart-bg:rgba(219,97,162,.15); --heart-bd:rgba(219,97,162,.4);
|
|
||||||
--hbg:#010409; --hfg:#e6edf3; --hfg2:#9198a1; --hbd:#21262d; --hsearch-bg:#0d1117; --hsearch-bd:#30363d; --hsearch-fg:#9198a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body { background: var(--bg); color: var(--fg); }
|
|
||||||
body {
|
|
||||||
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo', 'Segoe UI', 'Malgun Gothic', sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
||||||
transition: background-color .25s ease, color .2s ease;
|
|
||||||
}
|
|
||||||
::selection { background: color-mix(in srgb, var(--link) 22%, transparent); }
|
|
||||||
::placeholder { color: var(--fg3); }
|
|
||||||
a { color: inherit; text-decoration: none; }
|
|
||||||
svg { display: block; }
|
|
||||||
.tabnum { font-variant-numeric: tabular-nums; }
|
|
||||||
|
|
||||||
/* 공통 아바타 래퍼 (identicon 헬퍼가 사용) */
|
|
||||||
.avatar {
|
|
||||||
display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
|
|
||||||
overflow: hidden; background: var(--avatar-bg);
|
|
||||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fg) 6%, transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===================== 헤더 ===================== */
|
|
||||||
.hdr { position: sticky; top: 0; z-index: 300; background: var(--hbg); border-bottom: 1px solid var(--hbd); }
|
|
||||||
.hdr-inner { max-width: 1180px; margin: 0 auto; padding: 0 24px; height: 60px; display: flex; align-items: center; gap: 16px; }
|
|
||||||
.brand { display: flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; flex: 0 0 auto; }
|
|
||||||
.brand-mark { display: inline-flex; color: var(--hfg); transition: transform .2s ease, filter .2s ease; }
|
|
||||||
.brand-name { font-weight: 800; font-size: 15px; letter-spacing: .13em; color: var(--hfg); }
|
|
||||||
.brand-sub { font-size: 11px; color: var(--hfg2); font-weight: 500; border-left: 1px solid var(--hbd); padding-left: 10px; margin-left: 2px; white-space: nowrap; }
|
|
||||||
|
|
||||||
.hdr-search { flex: 1; max-width: 300px; position: relative; display: flex; align-items: center; }
|
|
||||||
.hdr-search-ico { position: absolute; left: 10px; display: inline-flex; color: var(--hfg2); pointer-events: none; }
|
|
||||||
.hdr-search input {
|
|
||||||
width: 100%; font-family: inherit; font-size: 13px; color: var(--hfg);
|
|
||||||
background: var(--hsearch-bg); border: 1px solid var(--hsearch-bd); border-radius: 7px;
|
|
||||||
padding: 6px 10px 6px 32px; outline: none;
|
|
||||||
}
|
|
||||||
.hdr-search input:focus { border-color: var(--link); background: var(--bg); color: var(--fg); }
|
|
||||||
.hdr-search input::-webkit-search-cancel-button { filter: invert(.5); }
|
|
||||||
|
|
||||||
.hdr-right { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; margin-left: auto; }
|
|
||||||
.icon-btn {
|
|
||||||
width: 32px; height: 32px; border-radius: 8px; border: 1px solid var(--hbd); background: transparent;
|
|
||||||
cursor: pointer; display: inline-flex; align-items: center; justify-content: center; color: var(--hfg2);
|
|
||||||
transition: background .14s ease, color .14s ease;
|
|
||||||
}
|
|
||||||
.icon-btn:hover { background: rgba(255,255,255,.08); color: var(--hfg); }
|
|
||||||
.theme-ico-sun { display: none; }
|
|
||||||
html[data-aidev-theme="dark"] .theme-ico-moon { display: none; }
|
|
||||||
html[data-aidev-theme="dark"] .theme-ico-sun { display: inline-flex; }
|
|
||||||
|
|
||||||
.admin-pill {
|
|
||||||
display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600;
|
|
||||||
color: #fff; background: color-mix(in srgb, var(--admin) 80%, transparent);
|
|
||||||
border: 1px solid var(--hbd); border-radius: 999px; padding: 4px 11px; white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usermenu { position: relative; }
|
|
||||||
.usermenu-btn {
|
|
||||||
display: flex; align-items: center; gap: 8px; background: transparent; border: 1px solid transparent;
|
|
||||||
border-radius: 8px; padding: 4px 6px 4px 8px; cursor: pointer; font-family: inherit;
|
|
||||||
}
|
|
||||||
.usermenu-btn:hover { background: rgba(255,255,255,.08); }
|
|
||||||
.usermenu-btn .who { font-size: 13px; font-weight: 600; color: var(--hfg); font-variant-numeric: tabular-nums; }
|
|
||||||
.usermenu-btn .caret { display: inline-flex; color: var(--hfg2); }
|
|
||||||
.usermenu-pop {
|
|
||||||
position: absolute; top: 46px; right: 0; width: 212px; background: var(--card); border: 1px solid var(--border);
|
|
||||||
border-radius: 10px; box-shadow: 0 8px 24px var(--shadow-strong); padding: 6px; z-index: 320; animation: aidev-pop .14s ease-out;
|
|
||||||
}
|
|
||||||
.usermenu-pop[hidden] { display: none; }
|
|
||||||
.usermenu-head { padding: 8px 10px 10px; border-bottom: 1px solid var(--divider); margin-bottom: 6px; }
|
|
||||||
.usermenu-label { font-size: 12px; color: var(--fg2); }
|
|
||||||
.usermenu-sabun { font-size: 13.5px; font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
|
|
||||||
.usermenu-role { font-size: 11.5px; color: var(--fg2); margin-top: 2px; }
|
|
||||||
.usermenu-item { width: 100%; text-align: left; background: transparent; border: 0; border-radius: 6px; padding: 8px 10px; cursor: pointer; font-family: inherit; font-size: 13px; color: var(--fg); display: flex; align-items: center; gap: 8px; }
|
|
||||||
.usermenu-item:hover { background: var(--panel); }
|
|
||||||
.usermenu-ico { display: inline-flex; color: var(--fg2); }
|
|
||||||
|
|
||||||
/* ===================== 레이아웃 / 섹션 ===================== */
|
|
||||||
.main { max-width: 1180px; margin: 0 auto; padding: 32px 24px 64px; }
|
|
||||||
.section { margin-bottom: 40px; }
|
|
||||||
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
|
|
||||||
.section-titlewrap { display: flex; align-items: baseline; gap: 10px; }
|
|
||||||
.section-title { margin: 0; font-size: 18px; font-weight: 700; color: var(--fg); letter-spacing: -.01em; }
|
|
||||||
.section-sub { font-size: 13px; color: var(--fg2); }
|
|
||||||
|
|
||||||
/* ===================== ① 개발 도구 ===================== */
|
|
||||||
.tools-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
|
|
||||||
.tool-card {
|
|
||||||
position: relative; text-align: left; background: var(--card); border: 1px solid var(--border-soft);
|
|
||||||
border-radius: 12px; padding: 16px; cursor: pointer; font-family: inherit; display: block;
|
|
||||||
transition: box-shadow .16s ease, border-color .16s ease, transform .16s ease;
|
|
||||||
}
|
|
||||||
.tool-card:hover { border-color: var(--link); box-shadow: 0 6px 18px var(--shadow-hover); transform: translateY(-3px); }
|
|
||||||
.tool-card.admin { border-color: var(--admin-bd); }
|
|
||||||
.tool-icon {
|
|
||||||
width: 44px; height: 44px; border-radius: 11px; display: inline-flex; align-items: center; justify-content: center;
|
|
||||||
font-size: 24px; background: var(--tile); border: 1px solid var(--tile-bd);
|
|
||||||
}
|
|
||||||
.tool-card.admin .tool-icon { background: var(--admin-bg); border-color: var(--admin-bd); }
|
|
||||||
.tool-name { display: block; margin-top: 14px; font-size: 14.5px; font-weight: 700; color: var(--fg); }
|
|
||||||
.tool-desc { display: block; margin-top: 3px; font-size: 12.5px; color: var(--fg2); line-height: 1.45; }
|
|
||||||
.sso-badge {
|
|
||||||
position: absolute; top: 13px; right: 13px; font-size: 10px; font-weight: 700; letter-spacing: .06em;
|
|
||||||
color: var(--accent-fg); background: var(--accent-bg); border: 1px solid var(--accent-bd); border-radius: 4px; padding: 2px 6px;
|
|
||||||
}
|
|
||||||
.tool-admin-badge {
|
|
||||||
display: inline-flex; align-items: center; gap: 5px; margin-top: 11px; font-size: 11px; font-weight: 600;
|
|
||||||
color: var(--admin-fg); background: var(--admin-bg); border: 1px solid var(--admin-bd); border-radius: 999px; padding: 2px 8px; white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===================== ② 내 프로젝트 ===================== */
|
|
||||||
.myproj { position: relative; background: var(--panel); border: 1px solid var(--border-soft); border-radius: 14px; padding: 22px 22px 24px; }
|
|
||||||
.myproj-accent { position: absolute; left: 0; top: 18px; bottom: 18px; width: 4px; border-radius: 0 4px 4px 0; background: var(--link); }
|
|
||||||
.myproj-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; padding-left: 8px; flex-wrap: wrap; }
|
|
||||||
.myproj-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
||||||
.filter-tabs { display: inline-flex; align-items: center; background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 2px; gap: 2px; }
|
|
||||||
.filter-tab {
|
|
||||||
display: inline-flex; align-items: center; gap: 5px; background: transparent; border: 0; border-radius: 6px; padding: 5px 11px;
|
|
||||||
cursor: pointer; font-family: inherit; font-size: 12.5px; font-weight: 600; color: var(--fg2); white-space: nowrap;
|
|
||||||
transition: background .14s ease, color .14s ease;
|
|
||||||
}
|
|
||||||
.filter-tab.on { background: var(--panel); color: var(--fg); box-shadow: inset 0 0 0 1px var(--border); }
|
|
||||||
.new-proj-btn {
|
|
||||||
display: inline-flex; align-items: center; gap: 6px; background: var(--success);
|
|
||||||
border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent); border-radius: 8px; padding: 7px 13px;
|
|
||||||
cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 700; color: #fff; white-space: nowrap; transition: background .14s ease;
|
|
||||||
}
|
|
||||||
.new-proj-btn:hover { background: var(--success-hover); }
|
|
||||||
.myproj-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; padding-left: 8px; }
|
|
||||||
|
|
||||||
/* 프로젝트 카드 (내 프로젝트 / 피드 공용 베이스) */
|
|
||||||
.pcard {
|
|
||||||
background: var(--card); border: 1px solid var(--border-soft); border-radius: 12px; padding: 15px 16px 14px;
|
|
||||||
cursor: pointer; transition: box-shadow .16s ease, border-color .16s ease, transform .16s ease; display: flex; flex-direction: column;
|
|
||||||
}
|
|
||||||
.pcard:hover { border-color: var(--link); box-shadow: 0 6px 18px var(--shadow); transform: translateY(-2px); }
|
|
||||||
.pcard.private { background: var(--bg); border: 1.5px dashed var(--border); }
|
|
||||||
.pcard-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
||||||
.pcard-titlewrap { display: flex; align-items: center; gap: 7px; min-width: 0; }
|
|
||||||
.pcard-repoico { display: inline-flex; color: var(--fg2); flex: 0 0 auto; }
|
|
||||||
.pcard-title { font-size: 14px; font-weight: 700; color: var(--link); line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
||||||
.pcard-desc { margin-top: 8px; font-size: 12.5px; color: var(--fg2); line-height: 1.5; flex: 1; }
|
|
||||||
.pcard-tags { margin-top: 11px; display: flex; flex-wrap: wrap; gap: 6px; }
|
|
||||||
.pcard-foot { margin-top: 13px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
||||||
.pcard-stats { display: flex; align-items: center; gap: 13px; font-size: 12px; color: var(--fg2); }
|
|
||||||
.pcard-stats .stat { display: inline-flex; align-items: center; gap: 4px; }
|
|
||||||
.lang-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
|
|
||||||
.ico-heart { display: inline-flex; color: var(--heart); }
|
|
||||||
.ico-star { display: inline-flex; color: var(--attn); }
|
|
||||||
.ico-comment { display: inline-flex; color: var(--fg2); }
|
|
||||||
.vis-badge {
|
|
||||||
display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; font-size: 11px; font-weight: 600;
|
|
||||||
border-radius: 999px; padding: 1px 8px 1px 7px; white-space: nowrap;
|
|
||||||
}
|
|
||||||
.vis-badge.public { color: var(--success-fg); background: var(--success-bg); border: 1px solid var(--success-bd); }
|
|
||||||
.vis-badge.private { color: var(--fg2); background: var(--panel); border: 1px solid var(--border); }
|
|
||||||
.toggle-share-btn { background: transparent; border: 0; padding: 0; cursor: pointer; font-family: inherit; font-size: 11.5px; font-weight: 600; color: var(--fg3); white-space: nowrap; }
|
|
||||||
.toggle-share-btn:hover { color: var(--fg); }
|
|
||||||
.private-note { font-size: 11.5px; color: var(--fg3); }
|
|
||||||
.share-mini-btn {
|
|
||||||
display: inline-flex; align-items: center; gap: 5px; background: var(--success);
|
|
||||||
border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent); border-radius: 7px; padding: 4px 10px;
|
|
||||||
cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 700; color: #fff; white-space: nowrap; transition: background .14s ease;
|
|
||||||
}
|
|
||||||
.share-mini-btn:hover { background: var(--success-hover); }
|
|
||||||
.add-card {
|
|
||||||
background: transparent; border: 1.5px dashed var(--border); border-radius: 12px; padding: 16px; cursor: pointer; font-family: inherit;
|
|
||||||
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: var(--fg2); min-height: 158px; width: 100%;
|
|
||||||
transition: border-color .16s ease, color .16s ease, background .16s ease;
|
|
||||||
}
|
|
||||||
.add-card:hover { border-color: var(--success); color: var(--success); background: var(--card); }
|
|
||||||
.add-card-plus { width: 38px; height: 38px; border-radius: 50%; background: var(--success-bg); border: 1px solid var(--success-bd); display: inline-flex; align-items: center; justify-content: center; color: var(--success); }
|
|
||||||
.add-card-title { font-size: 13.5px; font-weight: 700; white-space: nowrap; }
|
|
||||||
.add-card-sub { font-size: 11.5px; text-align: center; line-height: 1.4; }
|
|
||||||
.empty { grid-column: 1 / -1; text-align: center; padding: 30px; color: var(--fg2); font-size: 13px; border: 1px dashed var(--border); border-radius: 12px; }
|
|
||||||
|
|
||||||
/* ===================== ③ 공유 피드 ===================== */
|
|
||||||
.feed-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; margin-bottom: 16px; border-bottom: 1px solid var(--border-soft); }
|
|
||||||
.feed-head .section-titlewrap { padding-bottom: 14px; }
|
|
||||||
.feed-head-right { display: flex; align-items: flex-end; gap: 14px; }
|
|
||||||
.feed-star-btn {
|
|
||||||
display: inline-flex; align-items: center; gap: 6px; background: transparent; border: 1px solid var(--btn-bd); border-radius: 7px;
|
|
||||||
padding: 6px 12px; margin-bottom: 8px; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--fg2); white-space: nowrap;
|
|
||||||
transition: background .14s ease, border-color .14s ease, color .14s ease;
|
|
||||||
}
|
|
||||||
.feed-star-btn:hover { border-color: var(--attn-bd); color: var(--attn-fg); }
|
|
||||||
.feed-star-btn.on { background: var(--attn-bg); border-color: var(--attn-bd); color: var(--attn-fg); }
|
|
||||||
.feed-star-btn .ico { display: inline-flex; }
|
|
||||||
.sort-tabs { display: flex; gap: 2px; }
|
|
||||||
.sort-tab { background: transparent; border: 0; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; padding: 8px 14px 14px; color: var(--fg2); transition: color .14s ease; }
|
|
||||||
.sort-tab.on { color: var(--fg); box-shadow: inset 0 -2px 0 var(--tab-underline); }
|
|
||||||
.feed-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
|
||||||
|
|
||||||
.fcard { background: var(--card); border: 1px solid var(--border-soft); border-radius: 12px; padding: 18px; cursor: pointer; transition: box-shadow .16s ease, border-color .16s ease, transform .16s ease; display: flex; flex-direction: column; }
|
|
||||||
.fcard:hover { border-color: var(--link); box-shadow: 0 6px 18px var(--shadow); transform: translateY(-2px); }
|
|
||||||
.fcard-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
|
|
||||||
.fcard-titlewrap { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
||||||
.fcard-title { font-size: 15px; font-weight: 700; color: var(--link); line-height: 1.3; }
|
|
||||||
.react-btns { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
|
|
||||||
.react-btn {
|
|
||||||
display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; background: var(--btn-bg); border: 1px solid var(--btn-bd);
|
|
||||||
border-radius: 7px; padding: 4px 9px; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--fg);
|
|
||||||
transition: background .14s ease, border-color .14s ease, color .14s ease;
|
|
||||||
}
|
|
||||||
.react-btn .count { font-variant-numeric: tabular-nums; }
|
|
||||||
.react-btn.heart.on { background: var(--heart-bg); border-color: var(--heart-bd); color: var(--heart-fg); }
|
|
||||||
.react-btn.star.on { background: var(--attn-bg); border-color: var(--attn-bd); color: var(--attn-fg); }
|
|
||||||
/* 채움(on) / 외곽선(off) 아이콘 토글 — JS 가 .on 클래스만 바꾸면 됨 */
|
|
||||||
.react-btn .ico, .modal-action .ico { display: inline-flex; }
|
|
||||||
.react-btn .ico-on, .modal-action .ico-on { display: none; }
|
|
||||||
.react-btn.on .ico-on, .modal-action.on .ico-on { display: inline-flex; }
|
|
||||||
.react-btn.on .ico-off, .modal-action.on .ico-off { display: none; }
|
|
||||||
.fcard-desc { margin-top: 8px; font-size: 13px; color: var(--fg2); line-height: 1.55; }
|
|
||||||
.fcard-tags { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 6px; }
|
|
||||||
.fcard-foot { margin-top: 15px; display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
|
|
||||||
.fcard-author { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
||||||
.fcard-author .sabun { font-size: 12.5px; font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
|
|
||||||
.fcard-author .date { font-size: 11.5px; color: var(--fg3); }
|
|
||||||
.fcard-meta { display: flex; align-items: center; gap: 13px; font-size: 12px; color: var(--fg2); }
|
|
||||||
.fcard-meta .stat { display: inline-flex; align-items: center; gap: 5px; }
|
|
||||||
.bara-link { display: inline-flex; align-items: center; gap: 4px; background: transparent; border: 0; padding: 0; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--link); white-space: nowrap; }
|
|
||||||
|
|
||||||
.tag { font-size: 11px; font-weight: 500; color: var(--accent-fg); background: var(--accent-bg); border-radius: 999px; padding: 1px 9px; }
|
|
||||||
|
|
||||||
/* ===================== 상세 모달 ===================== */
|
|
||||||
.modal-overlay { position: fixed; inset: 0; z-index: 500; background: var(--overlay); display: flex; align-items: flex-start; justify-content: center; padding: 48px 20px; overflow-y: auto; animation: aidev-fade .16s ease-out; }
|
|
||||||
.modal-overlay[hidden] { display: none; }
|
|
||||||
.modal { width: 100%; max-width: 640px; background: var(--card); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 16px 48px var(--shadow-strong); overflow: hidden; animation: aidev-pop .18s ease-out; }
|
|
||||||
.modal-head { padding: 22px 24px 18px; border-bottom: 1px solid var(--divider); }
|
|
||||||
.modal-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
|
|
||||||
.modal-titlewrap { min-width: 0; }
|
|
||||||
.modal-title-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
||||||
.modal-repoico { display: inline-flex; color: var(--fg2); }
|
|
||||||
.modal-title { margin: 0; font-size: 20px; font-weight: 800; color: var(--link); line-height: 1.3; letter-spacing: -.01em; }
|
|
||||||
.modal-meta { margin-top: 10px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
||||||
.modal-meta .sabun { font-size: 13px; font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
|
|
||||||
.modal-meta .date { font-size: 12px; color: var(--fg3); }
|
|
||||||
.modal-meta .lang { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--fg2); margin-left: 4px; }
|
|
||||||
.modal-close { flex: 0 0 auto; width: 32px; height: 32px; border-radius: 8px; border: 1px solid transparent; background: transparent; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; color: var(--fg2); }
|
|
||||||
.modal-close:hover { background: var(--panel); border-color: var(--border-soft); }
|
|
||||||
.modal-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
|
|
||||||
.modal-desc { margin: 14px 0 0; font-size: 14px; color: var(--fg); line-height: 1.65; white-space: pre-wrap; }
|
|
||||||
.modal-actions { margin-top: 18px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
||||||
.modal-action {
|
|
||||||
display: inline-flex; align-items: center; gap: 7px; background: var(--btn-bg); border: 1px solid var(--btn-bd); border-radius: 8px;
|
|
||||||
padding: 8px 14px; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--fg);
|
|
||||||
transition: background .14s ease, border-color .14s ease, color .14s ease;
|
|
||||||
}
|
|
||||||
.modal-action .count { font-variant-numeric: tabular-nums; padding-left: 8px; margin-left: 2px; border-left: 1px solid var(--border); }
|
|
||||||
.modal-action.heart.on { background: var(--heart-bg); border-color: var(--heart-bd); color: var(--heart-fg); }
|
|
||||||
.modal-action.star.on { background: var(--attn-bg); border-color: var(--attn-bd); color: var(--attn-fg); }
|
|
||||||
.modal-action.primary { background: var(--success); border-color: color-mix(in srgb, var(--fg) 12%, transparent); color: #fff; }
|
|
||||||
.modal-action.primary:hover { background: var(--success-hover); }
|
|
||||||
.modal-action.ghost:hover { background: var(--btn-hover); }
|
|
||||||
|
|
||||||
/* 댓글 스레드 */
|
|
||||||
.modal-body { padding: 18px 24px 4px; }
|
|
||||||
.comments-title { font-size: 13px; font-weight: 700; color: var(--fg); margin-bottom: 14px; display: flex; align-items: center; gap: 7px; }
|
|
||||||
.comments-count { font-weight: 600; color: var(--fg2); background: var(--panel); border: 1px solid var(--border-soft); border-radius: 999px; padding: 0 8px; font-size: 12px; font-variant-numeric: tabular-nums; }
|
|
||||||
.comment-list { display: flex; flex-direction: column; gap: 4px; }
|
|
||||||
.comment { display: flex; gap: 11px; padding: 11px 0; }
|
|
||||||
.comment-main { min-width: 0; flex: 1; }
|
|
||||||
.comment-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
||||||
.comment-author { font-size: 13px; font-weight: 700; color: var(--fg); font-variant-numeric: tabular-nums; }
|
|
||||||
.comment-time { font-size: 11.5px; color: var(--fg3); }
|
|
||||||
.lgtm { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 800; letter-spacing: .04em; color: var(--success-fg); background: var(--success-bg); border: 1px solid var(--success-bd2); border-radius: 5px; padding: 1px 7px; animation: aidev-stamp .42s cubic-bezier(.2,.8,.3,1.2); }
|
|
||||||
.comment-text { margin-top: 3px; font-size: 13.5px; color: var(--fg); line-height: 1.6; white-space: pre-wrap; }
|
|
||||||
.comment-actions { margin-top: 5px; display: flex; align-items: center; gap: 12px; }
|
|
||||||
.comment-link { background: transparent; border: 0; padding: 0; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--fg2); display: inline-flex; align-items: center; gap: 5px; }
|
|
||||||
.comment-link:hover { color: var(--link); }
|
|
||||||
.comment-del:hover { color: var(--danger); }
|
|
||||||
.replies { margin-left: 21px; padding-left: 18px; border-left: 2px solid var(--divider); display: flex; flex-direction: column; }
|
|
||||||
.reply { display: flex; gap: 10px; padding: 10px 0; }
|
|
||||||
.reply .comment-author { font-size: 12.5px; }
|
|
||||||
.reply .comment-text { font-size: 13px; }
|
|
||||||
|
|
||||||
.modal-foot { padding: 14px 24px 22px; border-top: 1px solid var(--divider); margin-top: 6px; background: var(--bg-soft); }
|
|
||||||
.reply-banner { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 9px; background: var(--accent-bg); border: 1px solid var(--accent-bd); border-radius: 7px; padding: 6px 10px; }
|
|
||||||
.reply-banner span { font-size: 12px; color: var(--accent-strong); }
|
|
||||||
.reply-banner button { background: transparent; border: 0; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--link); }
|
|
||||||
.comment-form { display: flex; gap: 10px; align-items: flex-end; }
|
|
||||||
.comment-input { flex: 1; resize: none; font-family: inherit; font-size: 13.5px; line-height: 1.5; color: var(--fg); background: var(--inset); border: 1px solid var(--border); border-radius: 9px; padding: 9px 12px; outline: none; min-height: 40px; }
|
|
||||||
.comment-input:focus { border-color: var(--link); box-shadow: 0 0 0 3px var(--focus-ring); }
|
|
||||||
.submit-btn { flex: 0 0 auto; background: var(--success); border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent); border-radius: 9px; padding: 10px 16px; cursor: pointer; font-family: inherit; font-size: 13.5px; font-weight: 700; color: #fff; align-self: stretch; white-space: nowrap; transition: background .14s ease; }
|
|
||||||
.submit-btn:hover { background: var(--success-hover); }
|
|
||||||
.comment-hint { margin-top: 7px; font-size: 11px; color: var(--fg3); padding-left: 38px; }
|
|
||||||
|
|
||||||
/* ===================== 공유(등록) 모달 ===================== */
|
|
||||||
.share-modal { width: 100%; max-width: 520px; background: var(--card); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 16px 48px var(--shadow-strong); overflow: hidden; animation: aidev-pop .18s ease-out; }
|
|
||||||
.share-head { padding: 20px 24px; border-bottom: 1px solid var(--divider); display: flex; align-items: center; justify-content: space-between; }
|
|
||||||
.share-head h3 { margin: 0; font-size: 17px; font-weight: 800; color: var(--fg); }
|
|
||||||
.share-body { padding: 20px 24px; display: flex; flex-direction: column; gap: 15px; }
|
|
||||||
.share-label { display: block; font-size: 12.5px; font-weight: 700; color: var(--fg); margin-bottom: 6px; }
|
|
||||||
.share-label .req { color: var(--danger); }
|
|
||||||
.share-label .hint { font-weight: 500; color: var(--fg3); }
|
|
||||||
.share-input { width: 100%; font-family: inherit; font-size: 14px; color: var(--fg); background: var(--inset); border: 1px solid var(--border); border-radius: 8px; padding: 9px 12px; outline: none; }
|
|
||||||
.share-input.mono { font-family: 'SF Mono', ui-monospace, Menlo, monospace; font-size: 13px; }
|
|
||||||
.share-input:focus { border-color: var(--link); box-shadow: 0 0 0 3px var(--focus-ring); }
|
|
||||||
.vis-toggle { display: inline-flex; background: var(--inset); border: 1px solid var(--border); border-radius: 8px; padding: 3px; gap: 3px; }
|
|
||||||
.vis-opt { display: inline-flex; align-items: center; gap: 5px; background: transparent; border: 0; border-radius: 6px; padding: 6px 16px; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--fg2); white-space: nowrap; transition: background .14s ease, color .14s ease; }
|
|
||||||
.vis-opt.on-public { background: var(--success-bg); color: var(--success-fg); box-shadow: inset 0 0 0 1px var(--success-bd); }
|
|
||||||
.vis-opt.on-private { background: var(--panel); color: var(--fg); box-shadow: inset 0 0 0 1px var(--border); }
|
|
||||||
.share-vishint { margin-top: 7px; font-size: 11.5px; color: var(--fg3); }
|
|
||||||
.modal-foot-actions { padding: 16px 24px; border-top: 1px solid var(--divider); background: var(--bg-soft); display: flex; align-items: center; justify-content: flex-end; gap: 9px; }
|
|
||||||
|
|
||||||
/* ===================== 토스트 / 날아다니는 이모지 ===================== */
|
|
||||||
.toast { position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%); z-index: 600; background: #1f2328; color: #fff; border: 1px solid rgba(255,255,255,.12); border-radius: 10px; padding: 11px 16px; font-size: 13px; font-weight: 500; box-shadow: 0 8px 24px rgba(1,4,9,.4); display: flex; align-items: center; gap: 9px; max-width: 90vw; animation: aidev-toast 2.6s ease forwards; }
|
|
||||||
.toast .toast-ico { display: inline-flex; color: #3fb950; }
|
|
||||||
.flyers { position: fixed; inset: 0; z-index: 650; pointer-events: none; overflow: hidden; }
|
|
||||||
.flyer { position: fixed; pointer-events: none; user-select: none; will-change: transform, opacity; }
|
|
||||||
|
|
||||||
/* ===================== 푸터 ===================== */
|
|
||||||
.footer { border-top: 1px solid var(--border-soft); }
|
|
||||||
.footer-inner { max-width: 1180px; margin: 0 auto; padding: 20px 24px 28px; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
|
|
||||||
.footer-brand { display: flex; align-items: center; gap: 8px; color: var(--fg3); font-size: 12px; }
|
|
||||||
.footer-tech { font-size: 11.5px; color: var(--fg3); font-family: 'SF Mono', ui-monospace, Menlo, monospace; }
|
|
||||||
|
|
||||||
/* ===================== 버튼/태그 공용 (단독 페이지·로그인) ===================== */
|
|
||||||
.btn { display: inline-flex; align-items: center; gap: 6px; background: var(--btn-bg); border: 1px solid var(--btn-bd); border-radius: 8px; padding: 8px 14px; cursor: pointer; font-family: inherit; font-size: 13.5px; font-weight: 600; color: var(--fg); transition: background .14s ease; }
|
|
||||||
.btn:hover { background: var(--btn-hover); }
|
|
||||||
.btn-sm { padding: 6px 11px; font-size: 12.5px; }
|
|
||||||
.btn-primary { background: var(--link); border-color: color-mix(in srgb, var(--fg) 12%, transparent); color: #fff; }
|
|
||||||
.btn-primary:hover { filter: brightness(1.06); }
|
|
||||||
.btn-success { background: var(--success); border-color: color-mix(in srgb, var(--fg) 12%, transparent); color: #fff; }
|
|
||||||
.btn-success:hover { background: var(--success-hover); }
|
|
||||||
.muted { color: var(--fg2); }
|
|
||||||
.back-link { font-size: 13px; color: var(--fg2); }
|
|
||||||
.back-link:hover { color: var(--link); }
|
|
||||||
|
|
||||||
/* ===================== 로그인 폴백 페이지 ===================== */
|
|
||||||
.login-wrap { min-height: calc(100vh - 60px); display: flex; align-items: center; justify-content: center; padding: 24px; }
|
|
||||||
.login-card { width: 100%; max-width: 360px; background: var(--card); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 8px 24px var(--shadow); padding: 32px 28px; text-align: center; }
|
|
||||||
.login-logo { margin-bottom: 12px; }
|
|
||||||
.login-title { font-size: 20px; font-weight: 800; letter-spacing: .06em; margin-bottom: 18px; }
|
|
||||||
.alert { background: var(--attn-bg); border: 1px solid var(--attn-bd); color: var(--attn-fg); border-radius: 8px; padding: 10px 12px; font-size: 13px; margin-bottom: 16px; }
|
|
||||||
|
|
||||||
/* ===================== 단독 상세 페이지(project.ejs) ===================== */
|
|
||||||
.detail-wrap { max-width: 760px; margin: 0 auto; padding: 28px 24px 64px; }
|
|
||||||
|
|
||||||
/* ===================== 애니메이션 키프레임 ===================== */
|
|
||||||
@keyframes aidev-flyup { 0%{transform:translateY(0) scale(.5);opacity:0} 12%{opacity:1} 100%{transform:translateY(-180px) scale(1.15);opacity:0} }
|
|
||||||
@keyframes aidev-sparkle { 0%{transform:scale(0) rotate(0);opacity:1} 100%{transform:scale(1.5) rotate(120deg);opacity:0} }
|
|
||||||
@keyframes aidev-rocket { 0%{transform:translateX(-40px) rotate(-12deg);opacity:0} 12%{opacity:1} 88%{opacity:1} 100%{transform:translateX(calc(100vw + 60px)) rotate(-12deg);opacity:0} }
|
|
||||||
@keyframes aidev-shake { 10%,90%{transform:translateX(-1px) rotate(-1deg)} 20%,80%{transform:translateX(2px) rotate(1.5deg)} 30%,50%,70%{transform:translateX(-4px) rotate(-2deg)} 40%,60%{transform:translateX(4px) rotate(2deg)} }
|
|
||||||
@keyframes aidev-stamp { 0%{transform:scale(2.4) rotate(-22deg);opacity:0} 55%{transform:scale(.86) rotate(-11deg);opacity:1} 100%{transform:scale(1) rotate(-11deg);opacity:1} }
|
|
||||||
@keyframes aidev-pop { 0%{transform:scale(.96) translateY(6px);opacity:0} 100%{transform:scale(1) translateY(0);opacity:1} }
|
|
||||||
@keyframes aidev-fade { from{opacity:0} to{opacity:1} }
|
|
||||||
@keyframes aidev-toast { 0%{transform:translateX(-50%) translateY(12px);opacity:0} 12%{transform:translateX(-50%) translateY(0);opacity:1} 88%{transform:translateX(-50%) translateY(0);opacity:1} 100%{transform:translateX(-50%) translateY(8px);opacity:0} }
|
|
||||||
|
|
||||||
/* ===================== 반응형 ===================== */
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.tools-grid { grid-template-columns: repeat(2, 1fr); }
|
|
||||||
.myproj-grid { grid-template-columns: repeat(2, 1fr); }
|
|
||||||
.feed-grid { grid-template-columns: 1fr; }
|
|
||||||
.brand-sub { display: none; }
|
|
||||||
}
|
|
||||||
@media (max-width: 560px) {
|
|
||||||
.tools-grid, .myproj-grid { grid-template-columns: 1fr; }
|
|
||||||
.hdr-search { display: none; }
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,11 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title><%= brand %></title>
|
<title><%= brand %></title>
|
||||||
<link rel="stylesheet" href="/public/style.css" />
|
<link rel="stylesheet" href="/public/css/tokens.css" />
|
||||||
|
<link rel="stylesheet" href="/public/css/base.css" />
|
||||||
|
<link rel="stylesheet" href="/public/css/layout.css" />
|
||||||
|
<link rel="stylesheet" href="/public/css/components.css" />
|
||||||
|
<link rel="stylesheet" href="/public/css/modal.css" />
|
||||||
<script>
|
<script>
|
||||||
// FOUC 방지: 저장된 테마를 페인트 전에 documentElement 에 적용
|
// FOUC 방지: 저장된 테마를 페인트 전에 documentElement 에 적용
|
||||||
(function () {
|
(function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user