feat: strengthen design skill and refresh all showcases
Some checks failed
ci / build (push) Has been cancelled

This commit is contained in:
Yun Chan 2026-09-12 18:16:33 +09:00
parent 69e5cc1efd
commit e09d4efb1c
257 changed files with 8196 additions and 2374 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,13 @@
# MaruBuri source record
Downloaded 2026-09-12 from the official NAVER Hangeul static host. The files are retained unchanged: no conversion or subsetting was performed.
| File | Official URL | HTTP content type | Size | SHA-256 |
| --- | --- | --- | --- | --- |
| `MaruBuri-Regular.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2 | `font/woff2` | 433776 bytes | `4CF1341CF2F23FB3E263712DFDE1D8F25EEDCC328B696A2E5A2C8ADD55E5C17B` |
| `MaruBuri-SemiBold.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2 | `font/woff2` | 466108 bytes | `7AEF4862FA3219B0A7DEB6746E27116A38C4069F4424EB51EDF103B29795F5C2` |
- Official stylesheet checked: https://hangeul.pstatic.net/hangeul_static/css/maru-buri.css
- Official NAVER license page saved verbatim as `LICENSE.html`: https://help.naver.com/service/11029/contents/18088?lang=ko&osType=PC
- `LICENSE.html` SHA-256: `8538F7921744D5E040F603BC301465EFBD038F1054D0754B7508DE3286452EC3`
- The saved official page includes the NAVER copyright notice and the SIL Open Font License 1.1 text for MaruBuri.

View file

@ -0,0 +1,17 @@
# 향연
연기 세로 사진과 주변 여백을 유지한다. 히어로 사진의 이중 액자를 제거해 사진면과 기록면을 직접 맞대고, 숙성 장부와 아카이브 목록은 라벨·값이 같은 기준선에서 읽히게 조정한다. 본문은 17px 이상과 42rem 이하의 폭을 쓴다.
| 판단 축 | 적용 | 한계 |
| --- | --- | --- |
| 구도 | Hofmann의 면과 여백 관계를 참고해 사진 주위의 불필요한 액자 여백을 줄였다. | 이 참고가 모든 전시형 페이지의 최적 해법은 아니다. |
| 타입 | Ruder를 참고해 장부의 라벨·값과 본문의 역할을 분리했다. | 읽기 속도나 이해도 향상을 주장하지 않는다. |
| 색 | Albers를 참고해 따뜻한 바탕과 적갈색 동작색의 역할 경계를 유지했다. | 대비 수치는 브라우저 검증으로 재확인한다. |
토큰은 `--surface`, `--ink`, `--accent`, `--step-0`, `--space-*`를 사용한다. 로컬 MaruBuri 400/600은 `styles/fonts.css`, Pretendard는 기존 CDN을 유지한다. 가상 기록·예시 가격·기존 앵커는 바꾸지 않았다.
## 2026-09-12 현재 검증 기록
- `verify-final` 18개 묶음은 16개가 즉시 통과했다. 전체 묶음의 invariant·visual 검사는 Gaon·Dure를 대상으로 하며, 나머지 2개는 수정 뒤 release 독립 재실행에서 ALL PASS·ALL MATCH였다.
- root는 28개 모바일, 21개 데스크톱, 7개 하위 경로와 30개 앱 상태를 직접 검토해 수용했다. stress 112건은 hard failure 0이며, 가로 넘침 4건은 targeted fix 뒤 390px 재측정에서 0이다.
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.

View file

@ -16,13 +16,13 @@
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
href="./styles/fonts.css?v=20260912all"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
/>
<link rel="stylesheet" href="./styles/main.css?v=2" />
<link rel="stylesheet" href="./styles/main.css?v=20260912all" />
<link
rel="icon"
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%23F6F2E9'/%3E%3Cpath d='M16 6c0 3 0 5 0 7m0 4c0 3 2 3 2 5m-2 2c0 2-2 3-2 5' fill='none' stroke='%237C3B24' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E"
@ -45,24 +45,20 @@
</header>
<main id="main">
<!--
히어로 — 이 페이지의 리스크: 상품 사진 대신 연기 한 줄기.
여백이 주인공이고 사진은 그 여백을 가르는 흔적 하나다.
-->
<section class="hero" aria-labelledby="hero-h">
<div class="wrap hero-grid">
<div class="hero-text">
<p class="hero-kicker reveal">자연 침향 · 백단 · 서울 삼청동</p>
<p class="hero-kicker reveal">가상 향 아카이브 · 기록 형식 예시</p>
<h1 id="hero-h" class="reveal" style="--delay: 80ms">
연기는 흩어지고<br />향기만 머뭅니다
</h1>
<p class="lead reveal" style="--delay: 160ms">
향연은 향을 판매하기 전에 기록부터 남깁니다. 어느 해에 들인 나무인지,
수지가 몇이었는지, 연소에 몇 분이 걸렸는지. 향은 사라지는 것이니
남는 것은 문서뿐입니다.
향연은 향의 산지·숙성·연소 정보를 어떻게 기록할 수 있는지 보여 주는
가상 아카이브입니다. 아래 상품·가격·방문 정보는 판매나 예약을 위한
정보가 아니라 기록 형식의 예시입니다.
</p>
<div class="hero-actions reveal" style="--delay: 220ms">
<a class="btn" href="#collection">아카이브 보기</a>
<a class="btn" href="#collection">기록 구성 보기</a>
</div>
</div>
<figure class="hero-photo reveal" style="--delay: 120ms">
@ -74,15 +70,11 @@
loading="eager"
fetchpriority="high"
/>
<figcaption>침향 한 줄기 · 삼청동 아카이브룸</figcaption>
<figcaption>침향 연출 이미지 · 가상 아카이브</figcaption>
</figure>
</div>
</section>
<!--
향목 — 나무의 단면. 침향은 나무가 아니라 나무의 병(병해수지)이라는 것을
이 섹션에서만 설명한다.
-->
<section id="wood" class="section" aria-labelledby="wood-h">
<div class="wrap two">
<figure class="two-photo reveal">
@ -103,31 +95,26 @@
얻을 수 없고, 자연에서 쓰러진 나무에서만 길어 올립니다.
</p>
<p class="muted">
향연은 인공접종 목재를 다루지 않습니다. 매입 단계에서 수지 함량을
측정해 기록하고, 그 기록이 통과하지 못한 나무는 아카이브에 들어오지
않습니다.
이 섹션은 산지·수지 함량·숙성 상태를 함께 기록하는 방식을 설명합니다.
특정 목재의 매입 기준이나 실제 아카이브 운영 정책을 뜻하지 않습니다.
</p>
</div>
</div>
</section>
<!--
숙성 — 아카이브의 뼈대. 연도가 곧 목록이다.
카드가 아니라 장부의 행: 연도 · 입목 · 수지 · 상태.
-->
<section id="aging" class="section band" aria-labelledby="aging-h">
<div class="wrap">
<div class="aging-head">
<h2 id="aging-h">숙성 장부</h2>
<h2 id="aging-h">숙성 기록 예시</h2>
<p class="muted">
들여온 해와 수지 함량, 지금 상태입니다. 향은 재고가 아니라 연도입니다 —
같은 나무가 다시 들어오지 않습니다.
들여온 해와 수지 함량, 상태를 한 줄에서 읽는 기록 형식입니다. 아래
연도·산지·수지 함량은 실제 측정이나 재고를 뜻하지 않습니다.
</p>
</div>
<div class="table-wrap reveal">
<table>
<caption class="sr-only">
숙성 장부 — 입목 연도, 산지, 수지 함량, 현재 상태
숙성 기록 예시 — 입목 연도, 산지, 수지 함량, 상태
</caption>
<thead>
<tr>
@ -142,85 +129,80 @@
<th scope="row">2001년</th>
<td>베트남 카호아</td>
<td class="num">64.2%</td>
<td>열매 · 매진 임박</td>
<td>열매 · 상태 예시</td>
</tr>
<tr>
<th scope="row">2012년</th>
<td>인도네시아 리아우</td>
<td class="num">51.8%</td>
<td>침향 절편 · 소량</td>
<td>침향 절편 · 상태 예시</td>
</tr>
<tr>
<th scope="row">2024년</th>
<td>캄보디아 프놈펜 인근</td>
<td class="num">47.3%</td>
<td>숙성 중 · 비매품</td>
<td>숙성 중 · 상태 예시</td>
</tr>
</tbody>
</table>
</div>
<p class="aging-note muted">
수지 함량: 에탄올 용출법 실측. 열매(초험)는 기증용으로만 남겨둡니다.
수지 함량과 상태 표기는 기록 형식을 설명하기 위한 예시 값입니다.
</p>
</div>
</section>
<!--
아카이브 — 세 개의 물건. 사진 없는 목록.
종이의 물성을 믿고 글자로만 판다(R2: 텍스트가 상품).
-->
<section id="collection" class="section" aria-labelledby="collection-h">
<div class="wrap">
<h2 id="collection-h">지금 만질 수 있는 것</h2>
<h2 id="collection-h">기록으로 살펴보는 구성</h2>
<ol class="collection">
<li class="item reveal">
<div class="item-text">
<h3>자연 침향 절편</h3>
<p>2001년 입목 · 수지 64.2% · 10g</p>
</div>
<p class="item-price">180,000원</p>
<p class="item-price">예시 180,000원</p>
</li>
<li class="item reveal" style="--delay: 80ms">
<div class="item-text">
<h3>백단 침 향</h3>
<p>인도 마이소르 노목 · 20g · 연소 45분</p>
</div>
<p class="item-price">42,000원</p>
<p class="item-price">예시 42,000원</p>
</li>
<li class="item reveal" style="--delay: 160ms">
<div class="item-text">
<h3>침향 인엽</h3>
<p>리아우산 · 2012년 입목 · 3g</p>
</div>
<p class="item-price">96,000원</p>
<p class="item-price">예시 96,000원</p>
</li>
</ol>
<p class="muted collection-note">
모든 물건은 삼청동 아카이브룸에서 직접 내어드립니다. 향을 맡아 보고
정하시면 됩니다 — 예약제이며 회당 한 분만 들어갑니다.
이 목록은 상품명·단위·가격을 기록하는 방식을 보여 줍니다. 실제 상품 판매,
향 시향, 예약 또는 방문을 제공하지 않습니다.
</p>
</div>
</section>
<!-- 방문 — 마지막 섹션이 푸터 역할 -->
<section id="visit" class="section visit" aria-labelledby="visit-h">
<div class="wrap visit-grid">
<div>
<h2 id="visit-h">삼청동, 예약제</h2>
<h2 id="visit-h">방문 정보 예시</h2>
<address>
서울 종로구 삼청로 00길 00<br />
경복궁역 5번 출구에서 도보 6분
가상 아카이브 안내<br />
실제 장소와 방문 경로를 제공하지 않습니다.
</address>
</div>
<div class="visit-info">
<dl>
<div>
<dt>예약</dt>
<dt>방문 시간 예시</dt>
<dd>화–토 · 11:00, 14:00, 16:00 회당 50분</dd>
</div>
<div>
<dt>문의</dt>
<dd><a class="link" href="mailto:archive@hyang.kr">archive@hyang.kr</a></dd>
<dt>예약</dt>
<dd>실제 예약 및 문의를 받지 않습니다.</dd>
</div>
</dl>
</div>
@ -229,18 +211,5 @@
</section>
</main>
<script>
const io = new IntersectionObserver(
(entries) => {
for (const e of entries) {
if (!e.isIntersecting) continue;
e.target.classList.add("is-in");
io.unobserve(e.target);
}
},
{ rootMargin: "0px 0px -8% 0px", threshold: 0.05 },
);
for (const el of document.querySelectorAll(".reveal")) io.observe(el);
</script>
</body>
</html>

View file

@ -0,0 +1,15 @@
@font-face {
font-family: "MaruBuri";
src: url("../assets/fonts/MaruBuri-Regular.woff2") format("woff2");
font-style: normal;
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: "MaruBuri";
src: url("../assets/fonts/MaruBuri-SemiBold.woff2") format("woff2");
font-style: normal;
font-weight: 600;
font-display: swap;
}

View file

@ -1,4 +1,4 @@
@import url("./tokens.css");
@import url("./tokens.css?v=20260912all");
/* ==========================================================================
향연 — quiet-luxury. 밝은 편집면 위의 기록.
@ -138,6 +138,7 @@ a {
}
.nav {
display: flex;
flex-wrap: wrap;
gap: clamp(var(--space-3), 2vw, var(--space-5));
font-size: var(--step--1);
}
@ -157,13 +158,26 @@ a {
/* ── 히어로 ───────────────────────────────────────────────────────────── */
.hero {
padding-block: var(--space-8) var(--space-7);
display: grid;
align-items: center;
min-block-size: min(46rem, calc(100svh - 4rem));
padding-block: var(--space-6);
border-bottom: 1px solid var(--line);
}
.hero-grid {
display: grid;
grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
gap: var(--space-7) var(--space-6);
align-items: start;
grid-template-columns: minmax(0, 1fr) minmax(16rem, 28rem);
gap: clamp(var(--space-5), 6vw, var(--space-8));
align-items: center;
}
.hero-grid > *,
.two > *,
.visit-grid > * {
min-width: 0;
}
.hero-text {
max-width: 38rem;
padding-block: var(--space-4);
}
.hero-kicker {
font-size: var(--step--1);
@ -172,7 +186,7 @@ a {
margin-bottom: var(--space-5);
}
.lead {
max-width: 30em;
max-width: 42rem;
}
.hero-text .lead {
margin-top: var(--space-5);
@ -181,7 +195,9 @@ a {
margin-top: var(--space-6);
}
.btn {
display: inline-block;
display: inline-flex;
align-items: center;
min-height: 44px;
border: 1px solid var(--ink);
color: var(--ink);
text-decoration: none;
@ -196,22 +212,24 @@ a {
}
.hero-photo {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--space-2);
justify-content: end;
width: min(100%, 28rem);
justify-self: end;
align-items: start;
}
.hero-photo img {
display: block;
width: 100%;
aspect-ratio: 3 / 4;
height: auto;
object-fit: cover;
border-radius: var(--radius);
}
.hero-photo figcaption {
font-size: var(--step--1);
color: var(--ink-faint);
color: var(--ink-muted);
letter-spacing: 0.06em;
}
@ -220,6 +238,12 @@ a {
.section {
padding-block: var(--space-8);
}
.section[id] {
scroll-margin-top: 6rem;
}
#aging .wrap {
width: min(100% - 2 * var(--pad-inline), var(--container-wide));
}
.band {
background: var(--surface-raised);
border-block: 1px solid var(--line);
@ -247,6 +271,9 @@ a {
.two-text p + p {
margin-top: var(--space-4);
}
.two-text p {
max-width: 42rem;
}
.two-text .muted {
margin-top: var(--space-4);
}
@ -254,7 +281,7 @@ a {
/* ── 숙성 장부 ────────────────────────────────────────────────────────── */
.aging-head {
max-width: 36em;
max-width: 42rem;
margin-bottom: var(--space-5);
}
.aging-head p {
@ -305,8 +332,8 @@ tbody th {
border-top: 1px solid var(--line);
}
.item {
display: flex;
justify-content: space-between;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: baseline;
gap: var(--space-4);
padding-block: var(--space-4);
@ -323,11 +350,12 @@ tbody th {
.item-price {
font-family: var(--font-display);
font-size: var(--step-1);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.collection-note {
margin-top: var(--space-4);
max-width: 34em;
max-width: 42rem;
}
/* ── 방문 ─────────────────────────────────────────────────────────────── */
@ -368,23 +396,9 @@ tbody th {
/* ── 등장 모션 — 다른 페이지보다 느리다 ──────────────────────────────── */
.reveal {
opacity: 0;
translate: 0 16px;
transition: opacity var(--dur-normal) var(--ease-soft),
translate var(--dur-normal) var(--ease-soft);
transition-delay: var(--delay, 0ms);
}
.reveal.is-in {
opacity: 1;
translate: 0 0;
}
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 1;
translate: 0 0;
transition: none;
}
}
/* ── 반응형 ───────────────────────────────────────────────────────────── */
@ -397,6 +411,7 @@ tbody th {
.hero-photo {
max-width: 24rem;
justify-content: start;
justify-self: start;
}
.visit-grid {
grid-template-columns: 1fr;
@ -405,6 +420,7 @@ tbody th {
display: none;
}
.hero {
min-block-size: auto;
padding-block: var(--space-6);
}
}
@ -416,11 +432,13 @@ tbody th {
}
.nav {
width: 100%;
justify-content: space-between;
justify-content: flex-start;
}
.item {
flex-direction: column;
align-items: flex-start;
grid-template-columns: 1fr;
gap: var(--space-1);
}
.item-price {
white-space: normal;
}
}

View file

@ -28,7 +28,7 @@
--surface-raised: #efe9dc;
--ink: #2b2724;
--ink-muted: #6d655c;
--ink-faint: #9a9083;
--ink-faint: #746c60;
--line: #ddd4c3;
--accent: #7c3b24; /* 적갈 — 링크와 핵심 동작만 */
--accent-hover: #5f2c1a;
@ -44,8 +44,10 @@
--radius: 2px;
--container: 1040px;
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
/* 향의 문장은 좁게, 장부와 실물 기록은 넓게 둔다. */
--container: 1160px;
--container-wide: 1440px;
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4.5rem);
--dur-quick: 160ms;
--dur-normal: 560ms; /* 느린 페이지 — 등장도 느리게 */