소개 페이지 전면 재디자인 — Ethereal Glass
design-taste-frontend, high-end-visual-design, redesign-existing-projects 를 로드해 리디자인 모드로 진행했다. 이전 페이지는 디자인 스킬을 파는 자리가 문서처럼 생겼다는 지적을 받았다. Design Read - redesign-overhaul / 개발자 대상 / 정밀기기 언어 - DESIGN_VARIANCE 8 · MOTION_INTENSITY 6 · VISUAL_DENSITY 3 방향 - 다크 잉크 바탕에 three.js 셰이더 광원. 유리 패널이 그 위에 뜬다 - 리퀴드 글래스는 이 브리프에서 장식이 아니라 제품 시연이다. SVG 필터와 three.js 의 결합 그 자체이기 때문이다 - 밝은 밴드는 조판 섹션 하나뿐. 한글이 종이 위에 있어야 하는 자리다 제품 증명 - 히어로 배경에 셰이더(초기 번들 밖, 첫 페인트 후 지연 로드) - 재료 섹션에 SVG 필터 3종을 실제로 먹인 타일: 그레인, 굴절, 듀오톤 - 도그푸딩 산출물 2개를 실제 스크린샷으로. 가짜 목업이 아니다 감사에서 걸려 고친 것 - backdrop-filter 에 SVG 를 얹으면 Chrome 이 선언 전체를 버린다. CSS.supports 는 참을 주면서 computed 는 none 이 된다 - main 에서 그리드 클래스가 빠져 subgrid 가 붕괴, 가로 444px 오버플로 - 진입 연출이 두 번 콘텐츠를 삼켰다. opacity 를 버리고 transform 만 남겼다 - eyebrow 4개, 제목 em-dash 2건 수치는 불리해진 것도 그대로 싣는다 - 반투명 패널 1개 -> 29개. 유리가 주제가 됐으므로 - three.js 185KB(gzip). 데모라서 JS 예산을 올렸고 그 사실을 페이지에 적었다
This commit is contained in:
parent
6e05e69ca4
commit
be343cdab5
22 changed files with 1695 additions and 439 deletions
|
|
@ -16,23 +16,41 @@ const FONT_CSS = {
|
|||
} as const;
|
||||
|
||||
const alt = lang === "ko" ? "/en/" : "/";
|
||||
|
||||
// 파비콘: 페이지의 스파인 모티프를 그대로 쓴 단순 기하 마크
|
||||
const FAVICON =
|
||||
"data:image/svg+xml," +
|
||||
encodeURIComponent(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<rect width="32" height="32" rx="6" fill="#0E6B63"/>
|
||||
<path d="M11 7v18" stroke="#FAF9F6" stroke-width="2" stroke-linecap="round"/>
|
||||
<circle cx="11" cy="10" r="3" fill="#FAF9F6"/>
|
||||
<circle cx="11" cy="16" r="3" fill="#FAF9F6"/>
|
||||
<circle cx="11" cy="22" r="3" fill="#FAF9F6"/>
|
||||
<path d="M18 10h7M18 16h5M18 22h7" stroke="#FAF9F6" stroke-width="2" stroke-linecap="round" opacity=".55"/>
|
||||
</svg>`,
|
||||
);
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang={lang}>
|
||||
<html lang={lang} class="no-js">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={description} />
|
||||
<link rel="icon" href={FAVICON} />
|
||||
|
||||
<link rel="canonical" href={new URL(Astro.url.pathname, Astro.site)} />
|
||||
<link rel="alternate" hreflang="ko" href={new URL("/", Astro.site)} />
|
||||
<link rel="alternate" hreflang="en" href={new URL("/en/", Astro.site)} />
|
||||
<link rel="alternate" hreflang="x-default" href={new URL("/", Astro.site)} />
|
||||
|
||||
{/* 본문 폰트만 preconnect·preload 한다. 모노는 코드 블록에만 쓰인다 */}
|
||||
<link rel="preconnect" href={lang === "ko" ? "https://cdn.jsdelivr.net" : "https://api.fontshare.com"} crossorigin />
|
||||
<link
|
||||
rel="preconnect"
|
||||
href={lang === "ko" ? "https://cdn.jsdelivr.net" : "https://api.fontshare.com"}
|
||||
crossorigin
|
||||
/>
|
||||
<link rel="stylesheet" href={FONT_CSS[lang]} />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
|
|
@ -44,20 +62,36 @@ const alt = lang === "ko" ? "/en/" : "/";
|
|||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:locale" content={lang === "ko" ? "ko_KR" : "en_US"} />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
|
||||
{/* JS 가 살아 있으면 no-js 를 떼고 진입 연출을 켠다 */}
|
||||
<script is:inline>
|
||||
document.documentElement.classList.remove("no-js");
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a class="skip" href="#main">{lang === "ko" ? "본문으로 건너뛰기" : "Skip to content"}</a>
|
||||
|
||||
<header class="page site-header">
|
||||
<a class="wordmark" href={lang === "ko" ? "/" : "/en/"}>designpaca</a>
|
||||
<nav aria-label={lang === "ko" ? "언어" : "Language"}>
|
||||
{/* 언어는 국가가 아니다 — 국기 아이콘을 쓰지 않는다 */}
|
||||
<a href={alt} hreflang={lang === "ko" ? "en" : "ko"} lang={lang === "ko" ? "en" : "ko"}>
|
||||
{lang === "ko" ? "English" : "한국어"}
|
||||
<header class="site-header">
|
||||
<div class="header-inner glass">
|
||||
<a class="wordmark" href={lang === "ko" ? "/" : "/en/"} aria-label="designpaca">
|
||||
<svg class="mark" viewBox="0 0 32 32" width="20" height="20" aria-hidden="true">
|
||||
<path d="M11 7v18" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" />
|
||||
<circle cx="11" cy="10" r="3" fill="currentColor" />
|
||||
<circle cx="11" cy="16" r="3" fill="currentColor" />
|
||||
<circle cx="11" cy="22" r="3" fill="currentColor" />
|
||||
</svg>
|
||||
<span>designpaca</span>
|
||||
</a>
|
||||
</nav>
|
||||
<nav aria-label={lang === "ko" ? "언어" : "Language"}>
|
||||
{/* 언어는 국가가 아니다. 국기 아이콘을 쓰지 않는다 */}
|
||||
<a href={alt} hreflang={lang === "ko" ? "en" : "ko"} lang={lang === "ko" ? "en" : "ko"}>
|
||||
{lang === "ko" ? "English" : "한국어"}
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main" class="page">
|
||||
|
|
@ -67,6 +101,29 @@ const alt = lang === "ko" ? "/en/" : "/";
|
|||
<footer class="page site-footer">
|
||||
<slot name="footer" />
|
||||
</footer>
|
||||
|
||||
<script is:inline>
|
||||
// 진입 연출. 한 번 보이면 계속 보인다.
|
||||
// scroll 리스너를 쓰지 않는다(하드 게이트 10).
|
||||
(function () {
|
||||
var els = document.querySelectorAll(".reveal");
|
||||
if (!("IntersectionObserver" in window) || !els.length) {
|
||||
els.forEach(function (el) { el.classList.add("is-in"); });
|
||||
return;
|
||||
}
|
||||
var io = new IntersectionObserver(
|
||||
function (entries) {
|
||||
entries.forEach(function (e) {
|
||||
if (!e.isIntersecting) return;
|
||||
e.target.classList.add("is-in");
|
||||
io.unobserve(e.target);
|
||||
});
|
||||
},
|
||||
{ rootMargin: "200px 0px 200px 0px", threshold: 0 },
|
||||
);
|
||||
els.forEach(function (el) { io.observe(el); });
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -82,31 +139,51 @@ const alt = lang === "ko" ? "/en/" : "/";
|
|||
z-index: 10;
|
||||
transition: transform var(--dur-quick) var(--ease-out);
|
||||
}
|
||||
.skip:focus {
|
||||
transform: translateY(calc(var(--space-3) * 5));
|
||||
}
|
||||
.skip:focus { transform: translateY(calc(var(--space-3) * 5)); }
|
||||
|
||||
/* 상단에 붙은 풀블리드 바 대신 떠 있는 헤더 */
|
||||
/* 상단에 붙은 바가 아니라 떠 있는 유리 알약 */
|
||||
.site-header {
|
||||
padding-block: var(--space-4);
|
||||
position: sticky;
|
||||
top: var(--space-3);
|
||||
z-index: 3;
|
||||
padding-inline: var(--space-4);
|
||||
margin-top: var(--space-3);
|
||||
}
|
||||
.site-header > * {
|
||||
grid-column: main;
|
||||
}
|
||||
.site-header {
|
||||
align-items: baseline;
|
||||
.header-inner {
|
||||
max-width: 1180px;
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
|
||||
border-radius: 999px;
|
||||
}
|
||||
.wordmark {
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
font-weight: 560;
|
||||
letter-spacing: -0.01em;
|
||||
text-decoration: none;
|
||||
}
|
||||
.site-header nav {
|
||||
justify-self: end;
|
||||
grid-row: 1;
|
||||
grid-column: main;
|
||||
.mark { color: var(--accent); flex: none; }
|
||||
.site-header nav a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 36px;
|
||||
padding-inline: var(--space-3);
|
||||
border-radius: 999px;
|
||||
background: var(--glass-tint-2);
|
||||
border: 1px solid var(--glass-edge);
|
||||
font-size: var(--step-0);
|
||||
color: var(--ink-muted);
|
||||
text-decoration: none;
|
||||
transition: color var(--dur-instant) var(--ease-out),
|
||||
background var(--dur-instant) var(--ease-out);
|
||||
}
|
||||
.site-header nav a:hover { color: var(--ink); background: var(--glass-tint); }
|
||||
|
||||
.site-footer {
|
||||
padding-block: var(--space-6);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue