@import "tailwindcss"; @import "tw-animate-css"; /* 디자인 토큰(색상·폰트·radius)은 shared 단일 소스에서 관리. site 와 공유 가능. */ @import "@o2o/shared/styles/tokens.css"; @import "@o2o/shared/styles/base.css"; /* 캔버스가 그리는 것은 발행본과 같은 사이트다 — 시안 토큰·유틸을 같은 파일에서 읽는다. 규칙은 전부 `.site-canvas` 안으로 스코프돼 있어 관리자 크롬에는 닿지 않는다. */ @import "@o2o/shared/styles/site.css"; body { background-color: var(--background); color: var(--foreground); font-family: var(--font-sans); transition: background-color 0.2s ease, color 0.2s ease; } @layer base { * { @apply border-border outline-ring/50; } body { @apply bg-background text-foreground; } html { @apply font-sans; } } /* 캔버스(발행 사이트 미리보기)는 관리자 토큰이 아니라 템플릿 색을 쓴다. .site-canvas 안쪽은 --tpl-* 변수로만 색을 잡는다 — 관리자 크롬 색이 새어 들어오지 않게. */ @layer components { .site-canvas { color-scheme: light; background-color: var(--tpl-bg, #ffffff); color: var(--tpl-text, #09090b); /* 본문 서체도 토큰이다. 변수가 없으면(빌더 캔버스 등) 지금까지처럼 관리자 본문 서체를 쓴다. */ font-family: var(--tpl-font-body, var(--font-sans)); } .site-canvas .serif-title { font-family: var(--tpl-font-heading, 'Noto Serif KR', 'Batang', 'Times New Roman', serif); } /* 캔버스 안의 '면'(카드·패널)과 테두리도 토큰을 따른다. ★ bg-white / bg-stone-* 이 캔버스 안에만 82곳 흩어져 있어 파일마다 고치는 대신 여기서 덮는다. 폴백이 원래 Tailwind 값이라, --tpl-* 이 없는 곳에서는 지금까지와 완전히 동일하다. ★ 팔레트를 바꿔도 화면이 안 변하는 원인의 절반이 이 면들이었다(나머지 절반은 섹션 바탕). */ .site-canvas .bg-white { background-color: var(--tpl-card, #ffffff); } .site-canvas .bg-stone-50 { background-color: var(--tpl-card, var(--color-stone-50)); } .site-canvas .bg-stone-100 { background-color: var(--tpl-surface-alt, var(--color-stone-100)); } /* 테두리는 팔레트 색을 그대로 쓰면 너무 세다 — 30% 만 섞는다. */ .site-canvas .border-stone-200, .site-canvas .border-stone-300 { border-color: color-mix(in oklab, var(--tpl-border, var(--color-stone-200)) 30%, transparent); } /* serif-title 을 안 쓰는 제목도 제목 서체를 따르게 한다. ★ 폴백이 inherit 이라 --tpl-font-heading 이 없으면 예전과 똑같이 본문 서체를 그대로 물려받는다. */ .site-canvas h1, .site-canvas h2, .site-canvas h3, .site-canvas h4 { font-family: var(--tpl-font-heading, inherit); letter-spacing: var(--tpl-heading-tracking, normal); } /* ★ 굵기는 폴백을 inherit 로 두지 않는다. 간판체(Gugi)는 굵기가 한 벌뿐이라 700 을 주면 브라우저가 가짜 볼드를 씌워 획이 뭉갠다 — 템플릿이 400 을 지정할 수 있어야 한다. */ .site-canvas :is(h1, h2, h3, h4).tpl-title { font-weight: var(--tpl-heading-weight, 700); } /* 카드·패널 테두리 두께도 템플릿이 정한다. 레트로는 2px 라야 인쇄물처럼 보인다. */ .site-canvas .border { border-width: var(--tpl-border-width, 1px); } } /* 히어로 제목의 앞말만 갈아 끼운다 — '홈페이지' 는 고정이고 수식어가 돈다. ★ CSS 만으로 돈다(마퀴와 같은 이유). 문구를 **일곱 줄** 쌓는다 — 마지막 줄이 첫 줄의 복제라 -85.714% 에서 0% 로 되감을 때 글자가 튀지 않는다. ★ 줄 높이를 1.25em 으로 못 박는다. 창이 1줄이고 트랙이 7줄이라 한 칸이 정확히 1/7 이어야 한다. ★ 문구 개수(6)와 이 키프레임은 한 몸이다. 문구를 늘리면 여기 stop 도 같이 고쳐야 한다 — 안 고치면 마지막 몇 개가 영영 안 보이거나 빈 줄이 지나간다(LandingPage HEADLINES). */ @keyframes o2o-rotate-6 { 0%, 14% { transform: translateY(0); } 16.67%, 30.67% { transform: translateY(-14.286%); } 33.33%, 47.33% { transform: translateY(-28.571%); } 50%, 64% { transform: translateY(-42.857%); } 66.67%, 80.67% { transform: translateY(-57.143%); } 83.33%, 97.33% { transform: translateY(-71.429%); } 100% { transform: translateY(-85.714%); } } .o2o-rotator { display: block; height: 1.25em; overflow: hidden; } .o2o-rotator-track { /* ★ display:block 이 없으면 아무 일도 안 일어난다. 이 요소는 이라 기본이 inline 이고, **인라인 요소에는 transform 이 적용되지 않는다** — 애니메이션은 걸려 있는데 화면은 정지다. */ display: block; animation: o2o-rotate-6 16s cubic-bezier(0.65, 0, 0.35, 1) infinite; } .o2o-rotator-track > span { display: block; height: 1.25em; line-height: 1.25; } @media (prefers-reduced-motion: reduce) { .o2o-rotator-track { animation: none; } } /* 랜딩 히어로의 발행 사이트 마퀴. **CSS 만으로 돈다** — 타이머를 쓰면 탭이 백그라운드일 때 프레임이 밀려 끊긴 것처럼 보인다. 트랙에 같은 목록을 두 벌 넣고 -50% 까지 밀면 이음매가 없다. */ @keyframes o2o-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } } .o2o-marquee-track { animation: o2o-marquee var(--marquee-duration, 48s) linear infinite; } .o2o-marquee:hover .o2o-marquee-track { animation-play-state: paused; } @media (prefers-reduced-motion: reduce) { .o2o-marquee-track { animation: none; } } /* 수집 진행바 shimmer — 0%(빈 트랙) 구간에서도 '진행 중'이 보이게 한다. */ @keyframes o2o-shimmer { from { transform: translateX(-100%); } to { transform: translateX(500%); } } .o2o-progress-shimmer { animation: o2o-shimmer 1.8s ease-in-out infinite; } /* 노치/홈 인디케이터 회피 (viewport-fit=cover 와 짝). 화면 맨 위/맨 아래에 붙는 바에만 붙인다 — 스크롤되는 본문에는 쓰지 않는다. */ @layer components { .safe-t { padding-top: env(safe-area-inset-top); } .safe-b { padding-bottom: env(safe-area-inset-bottom); } }