diff --git a/content/about/_index.ko.md b/content/about/_index.ko.md
@@ -2,7 +2,11 @@
title = "소개"
description = "sqzer.com 뒤에 누가 있고 여기서 무엇을 만드는지."
weight = 30
-template = "page.html"
+template = "about.html"
+
+[extra]
+portrait = "images/profile.jpg"
+portrait_alt = "sqzer-x"
# 이름, 소속, 사는 곳, 이력 같은 개인적인 세부는 일부러 비워 두었다.
# 지어내는 것보다 없는 편이 낫고, 채우는 건 본인만 할 수 있다.
diff --git a/content/about/_index.md b/content/about/_index.md
@@ -2,7 +2,11 @@
title = "About"
description = "Who is behind sqzer.com, and what gets built here."
weight = 30
-template = "page.html"
+template = "about.html"
+
+[extra]
+portrait = "images/profile.jpg"
+portrait_alt = "sqzer-x"
# 개인적인 세부(이름, 소속, 사는 곳, 이력)는 일부러 비워 두었다. 지어내는 것보다
# 없는 편이 낫고, 채우는 건 본인만 할 수 있다.
diff --git a/content/projects/_index.ko.md b/content/projects/_index.ko.md
@@ -15,6 +15,7 @@ page_template = "project.html"
url = "/ko/projects/sqzass/"
name = "sqzass"
lang = "Rust"
+mark = "images/projects/sqzass.png"
cover = ""
blurb = "깨진 참조를 발행하지 않는 정적 사이트 생성기."
site = "https://sqzass.sqzer.com"
@@ -24,7 +25,8 @@ repo = "https://github.com/sqzer-x/sqzass"
url = "/ko/projects/ommp/"
name = "ommp"
lang = "Rust"
-cover = ""
+mark = ""
+cover = "images/projects/ommp-cover.png"
blurb = "띄워 둘 데몬도, 끄고 남는 것도 없는 터미널 음악 재생기."
site = "https://ommp.sqzer.com"
repo = "https://github.com/sqzer-x/ommp"
diff --git a/content/projects/_index.md b/content/projects/_index.md
@@ -23,6 +23,7 @@ page_template = "project.html"
url = "/projects/sqzass/"
name = "sqzass"
lang = "Rust"
+mark = "images/projects/sqzass.png"
cover = ""
blurb = "A static site generator that refuses to publish a broken reference."
site = "https://sqzass.sqzer.com"
@@ -32,7 +33,8 @@ repo = "https://github.com/sqzer-x/sqzass"
url = "/projects/ommp/"
name = "ommp"
lang = "Rust"
-cover = ""
+mark = ""
+cover = "images/projects/ommp-cover.png"
blurb = "A terminal music player with no daemon to start and nothing left running."
site = "https://ommp.sqzer.com"
repo = "https://github.com/sqzer-x/ommp"
diff --git a/content/projects/doomsday.ko.md b/content/projects/doomsday.ko.md
@@ -13,6 +13,7 @@ lang = "JavaScript"
license = "GPL-3.0"
version = ""
platform = "GNOME 45 · 46 · 47"
+mark = ""
logo = ""
actions = [
diff --git a/content/projects/doomsday.md b/content/projects/doomsday.md
@@ -13,6 +13,7 @@ lang = "JavaScript"
license = "GPL-3.0"
version = ""
platform = "GNOME 45 · 46 · 47"
+mark = ""
logo = ""
actions = [
diff --git a/content/projects/ommp.ko.md b/content/projects/ommp.ko.md
@@ -13,7 +13,8 @@ lang = "Rust"
license = "Apache-2.0"
version = ""
platform = "Linux"
-logo = "images/projects/ommp.png"
+mark = ""
+logo = ""
actions = [
{ label = "웹사이트", url = "https://ommp.sqzer.com", style = "primary" },
diff --git a/content/projects/ommp.md b/content/projects/ommp.md
@@ -13,7 +13,8 @@ lang = "Rust"
license = "Apache-2.0"
version = ""
platform = "Linux"
-logo = "images/projects/ommp.png"
+mark = ""
+logo = ""
actions = [
{ label = "Website", url = "https://ommp.sqzer.com", style = "primary" },
diff --git a/content/projects/sqzass.ko.md b/content/projects/sqzass.ko.md
@@ -13,6 +13,7 @@ lang = "Rust"
license = "MIT"
version = "0.1.0"
platform = "Linux · macOS"
+mark = "images/projects/sqzass.png"
logo = ""
actions = [
diff --git a/content/projects/sqzass.md b/content/projects/sqzass.md
@@ -13,6 +13,7 @@ lang = "Rust"
license = "MIT"
version = "0.1.0"
platform = "Linux · macOS"
+mark = "images/projects/sqzass.png"
logo = ""
actions = [
diff --git a/content/projects/uaf.ko.md b/content/projects/uaf.ko.md
@@ -13,6 +13,7 @@ lang = "Python"
license = ""
version = ""
platform = "Linux"
+mark = ""
logo = ""
actions = [
diff --git a/content/projects/uaf.md b/content/projects/uaf.md
@@ -13,6 +13,7 @@ lang = "Python"
license = ""
version = ""
platform = "Linux"
+mark = ""
logo = ""
actions = [
diff --git a/content/projects/udfx.ko.md b/content/projects/udfx.ko.md
@@ -13,6 +13,7 @@ lang = "PowerShell"
license = ""
version = ""
platform = "Windows"
+mark = ""
logo = ""
actions = [
diff --git a/content/projects/udfx.md b/content/projects/udfx.md
@@ -13,6 +13,7 @@ lang = "PowerShell"
license = ""
version = ""
platform = "Windows"
+mark = ""
logo = ""
actions = [
diff --git a/content/projects/usfx.ko.md b/content/projects/usfx.ko.md
@@ -13,6 +13,7 @@ lang = "Python"
license = ""
version = "1.2.0"
platform = "크로스 플랫폼"
+mark = ""
logo = ""
actions = [
diff --git a/content/projects/usfx.md b/content/projects/usfx.md
@@ -13,6 +13,7 @@ lang = "Python"
license = ""
version = "1.2.0"
platform = "Cross-platform"
+mark = ""
logo = ""
actions = [
diff --git a/static/css/main.css b/static/css/main.css
@@ -205,9 +205,25 @@ main { display: block; padding-bottom: clamp(4rem, 10vw, 8rem); }
font-weight: 700;
letter-spacing: -0.03em;
}
-/* 마크는 그림이 아니라 글자다 — 프롬프트 기호. 로고 파일이 없어도 성립하고
- currentColor 를 타므로 테마를 따라간다. */
-.brand-mark { color: var(--live); }
+/* 마크는 잉크로 그린다. 원본이 투명 배경의 검은 선화라 <img> 로 넣으면 다크
+ 배경에서 사라지는데, 마스크로 두면 배경색이 currentColor 가 되어 글자와 같이
+ 변한다. 원본 비율 240:131 을 박아 두어 로딩 중에도 자리가 안 움직인다. */
+.brand-mark {
+ width: 1.6em;
+ aspect-ratio: 128 / 123;
+ flex: none;
+ align-self: center;
+ background-color: var(--live);
+ -webkit-mask: url("/images/mark.png") center / contain no-repeat;
+ mask: url("/images/mark.png") center / contain no-repeat;
+ transition: background-color var(--tr-ink);
+}
+.brand:hover .brand-mark { background-color: var(--ink); }
+/* 마스크를 모르는 브라우저에서는 색만 칠한 네모가 남는다. 워드마크가 이미
+ 이름을 말하고 있으므로 마크를 지우는 편이 낫다. */
+@supports not ((mask: url("")) or (-webkit-mask: url(""))) {
+ .brand-mark { display: none; }
+}
.brand-name { color: var(--ink); }
.nav {
@@ -324,6 +340,37 @@ main { display: block; padding-bottom: clamp(4rem, 10vw, 8rem); }
color: var(--ink-3);
}
+/* 소개의 머리. 초상과 제목이 나란히 서고, 좁아지면 초상이 위로 간다.
+ align-items 가 end 인 이유는 제목의 베이스라인이 초상의 아래변에 맞아야
+ 둘이 같은 바닥을 딛고 선 것으로 보이기 때문이다. */
+.about-head {
+ display: grid;
+ grid-template-columns: 11rem minmax(0, 1fr);
+ gap: clamp(1.25rem, 3vw, 2.25rem);
+ align-items: end;
+}
+.about-intro { min-width: 0; }
+
+.portrait { margin: 0; }
+.portrait img {
+ display: block;
+ width: 100%;
+ aspect-ratio: 4 / 5;
+ object-fit: cover;
+ border: 1px solid var(--rule);
+ border-radius: var(--radius);
+ /* 사진의 주황 배경이 이 페이지에서 제일 센 색이다. 쉬는 동안 한 걸음 물러나
+ 있다가 다가가면 제 색으로 돌아온다. */
+ filter: saturate(0.75) brightness(0.94);
+ transition: filter var(--tr-light);
+}
+.about-head:hover .portrait img { filter: none; }
+
+@media (max-width: 34rem) {
+ .about-head { grid-template-columns: minmax(0, 1fr); }
+ .portrait { max-width: 9rem; }
+}
+
.crumbs { margin-bottom: 0.9rem; font-family: var(--mono); font-size: 0.75rem; }
.crumbs a { color: var(--ink-3); transition: color var(--tr-ink); }
.crumbs a:hover { color: var(--live); }
@@ -631,9 +678,23 @@ main { display: block; padding-bottom: clamp(4rem, 10vw, 8rem); }
overflow: hidden;
}
.bento-media img { width: 100%; height: 100%; object-fit: cover; }
-/* 이미지가 없으면 낮춘다. 워드마크 하나를 담자고 16:7 을 비워 두면 카드가
- 비어 있는 것으로 읽힌다. 이미지가 들어오면 원래 비율로 돌아온다. */
-.bento-media:not(:has(img)) { aspect-ratio: 16 / 4.5; }
+
+/* 선화 마크. 메달리온과 같은 방식이라 상세 페이지와 카드가 같은 그림을 같은
+ 규칙으로 그린다. */
+.bento-mark {
+ width: 46%;
+ aspect-ratio: 1;
+ background-color: var(--ink-4);
+ -webkit-mask: var(--mark-src) center / contain no-repeat;
+ mask: var(--mark-src) center / contain no-repeat;
+ transition: background-color var(--tr-light);
+}
+.bento-card:hover .bento-mark { background-color: var(--ink-3); }
+
+/* 그림이 아무것도 없을 때만 낮춘다. 이름 하나를 담자고 16:7 을 비워 두면 카드가
+ 비어 있는 것으로 읽힌다. 커버든 마크든 들어오면 원래 비율이고, 그래야 카드
+ 여러 장의 본문이 같은 높이에서 시작한다. */
+.bento-media:not(:has(img)):not(:has(.bento-mark)) { aspect-ratio: 16 / 4.5; }
.bento-glyph {
font-family: var(--mono);
@@ -785,6 +846,21 @@ main { display: block; padding-bottom: clamp(4rem, 10vw, 8rem); }
overflow: hidden;
}
.medallion img { width: 76%; height: 76%; object-fit: contain; }
+
+/* 선화 마크. 인라인으로 오는 것은 --mark-src 하나뿐이고 그건 장식이 아니라
+ 데이터다 — 어떤 파일인지는 프로젝트마다 다르고, 어떻게 그릴지는 전부 여기 있다.
+ currentColor 를 타므로 테마를 따라가고, 원본이 무슨 색이든 페이지와 싸우지 않는다. */
+.medallion-mark::before {
+ content: "";
+ width: 76%;
+ height: 76%;
+ background-color: var(--ink-3);
+ -webkit-mask: var(--mark-src) center / contain no-repeat;
+ mask: var(--mark-src) center / contain no-repeat;
+ transition: background-color var(--tr-light);
+}
+.hero:hover .medallion-mark::before { background-color: var(--ink-2); }
+
.medallion-glyph {
font-family: var(--mono);
font-weight: 700;
diff --git a/static/images/apple-touch-icon.png b/static/images/apple-touch-icon.png
Binary files differ.
diff --git a/static/images/favicon-dark.png b/static/images/favicon-dark.png
Binary files differ.
diff --git a/static/images/favicon.png b/static/images/favicon.png
Binary files differ.
diff --git a/static/images/mark.png b/static/images/mark.png
Binary files differ.
diff --git a/static/images/profile.jpg b/static/images/profile.jpg
Binary files differ.
diff --git a/static/images/projects/ommp-cover.png b/static/images/projects/ommp-cover.png
Binary files differ.
diff --git a/static/images/projects/ommp.png b/static/images/projects/ommp.png
Binary files differ.
diff --git a/static/images/projects/sqzass.png b/static/images/projects/sqzass.png
Binary files differ.
diff --git a/templates/about.html b/templates/about.html
@@ -0,0 +1,30 @@
+{#- 소개. 초상이 붙는다는 점만 page.html 과 다르다.
+
+ 초상을 마크다운 본문에 넣지 않는 이유: 본문 안에 있으면 문단 사이에 끼어
+ 흐름을 끊고, 폭과 비율을 문서마다 다시 정해야 한다. 머리에 두면 자리가
+ 한 곳이고 규격도 하나다. #}
+{% extends "base.html" %}
+
+{% block content %}
+<div class="wrap wrap-read">
+ <header class="page-head about-head" data-reveal>
+ {%- if page.extra.portrait %}
+ {#- alt 는 사람을 말한다. 장식이 아니라 "누구인지"가 이 페이지의 내용이다. #}
+ <figure class="portrait">
+ <img src="{{ asset(page.extra.portrait) }}" alt="{{ page.extra.portrait_alt }}"
+ width="720" height="900" loading="eager" decoding="async">
+ </figure>
+ {%- endif %}
+ <div class="about-intro">
+ <h1 class="page-title">{{ page.title }}</h1>
+ {%- if page.description %}
+ <p class="page-lede">{{ page.description }}</p>
+ {%- endif %}
+ </div>
+ </header>
+
+ <div class="prose">
+ {{ page.content | safe }}
+ </div>
+</div>
+{% endblock %}
diff --git a/templates/base.html b/templates/base.html
@@ -29,6 +29,13 @@
<link rel="alternate" hreflang="{{ lang.code }}" href="{{ site.origin }}{{ lang.url }}">
{%- endfor %}
+{#- 파비콘에는 CSS 가 닿지 않는다. 검은 선화는 어두운 탭에서 안 보이므로 흰 선
+ 버전을 따로 준다. media 를 모르는 브라우저는 마지막 것을 쓰므로, 밝은 탭이
+ 기본인 쪽을 뒤에 둔다. #}
+<link rel="icon" type="image/png" media="(prefers-color-scheme: dark)" href="{{ asset("images/favicon-dark.png") }}">
+<link rel="icon" type="image/png" href="{{ asset("images/favicon.png") }}">
+<link rel="apple-touch-icon" href="{{ asset("images/apple-touch-icon.png") }}">
+
{#- 소셜 카드. sqzass 는 <head> 에 아무것도 넣지 않으므로 전부 우리 마크업이다.
summary_large_image 는 이미지가 있을 때만 뜻이 있다 — 없이 선언하면 빈 상자가 나온다. #}
<meta property="og:type" content="{{ "website" if page.is_section else "article" }}">
@@ -59,10 +66,13 @@
<header class="masthead">
<div class="masthead-in">
- {#- 로고 이미지는 없다. 워드마크가 곧 마크다 — mono 700 에 음수 트래킹.
- alt 를 비우지 않는 이유는 이게 이미지가 아니라 텍스트이기 때문이다. #}
+ {#- 마크는 <img> 가 아니라 CSS 마스크다. 원본이 투명 배경의 검은 선화라
+ 그대로 넣으면 다크 배경에서 사라진다. 마스크로 두면 currentColor 를
+ 타서 테마와 호버를 글자와 똑같이 따라간다.
+ aria-hidden 인 이유는 바로 옆 텍스트가 이미 같은 이름을 말하기 때문이다 —
+ 여기 이름을 또 넣으면 스크린리더가 두 번 읽는다. #}
<a class="brand" href="{{ home }}">
- <span class="brand-mark" aria-hidden="true">$</span>
+ <span class="brand-mark" aria-hidden="true"></span>
<span class="brand-name">{{ site.title }}</span>
</a>
diff --git a/templates/project.html b/templates/project.html
@@ -50,10 +50,17 @@
{%- endif %}
</div>
- {#- 옆칸. 로고가 있으면 메달리온, 없으면 이름을 크게 깐다. 규격이 같아서
- 나중에 SVG 를 넣어도 레이아웃이 움직이지 않는다. #}
+ {#- 옆칸. 세 가지가 같은 규격의 액자 안에 들어간다.
+
+ `mark` 는 선화다. 마스크로 깔아 currentColor 를 타게 하므로 테마를
+ 따라가고, 프로젝트마다 다른 원본 색이 페이지와 싸우지 않는다.
+ `logo` 는 색이 의미를 지는 그림이라 <img> 로 그대로 둔다.
+ 둘 다 없으면 이름이 그 자리를 지킨다 — 나중에 그림을 넣어도 레이아웃이
+ 움직이지 않는 이유가 이것이다. #}
<div class="hero-aside" aria-hidden="true">
- {%- if page.extra.logo %}
+ {%- if page.extra.mark %}
+ <span class="medallion medallion-mark" style="--mark-src: url('/{{ page.extra.mark }}')"></span>
+ {%- elif page.extra.logo %}
<span class="medallion"><img src="{{ asset(page.extra.logo) }}" alt="" loading="eager" decoding="async"></span>
{%- else %}
<span class="medallion medallion-glyph">{{ page.title }}</span>
diff --git a/templates/projects.html b/templates/projects.html
@@ -29,12 +29,14 @@
<section class="bento" aria-label="{{ t("selected") }}">
{%- for p in page.extra.featured %}
<article class="bento-card" data-reveal>
+ {#- 카드 그림은 세 갈래이고 상세 페이지의 메달리온과 같은 규칙이다.
+ 커버 사진 → 마크(선화) → 이름. 뒤로 갈수록 조용해질 뿐 자리는 같다. #}
<div class="bento-media">
{%- if p.cover %}
<img src="{{ asset(p.cover) }}" alt="" loading="lazy" decoding="async">
+ {%- elif p.mark %}
+ <span class="bento-mark" aria-hidden="true" style="--mark-src: url('/{{ p.mark }}')"></span>
{%- else %}
- {#- 커버가 없을 때 회색 상자를 놓지 않는다. 이름 자체가 그림이 된다 —
- mono 로 크게 깔고 뒤로 물린다. 나중에 이미지를 넣으면 이게 물러난다. #}
<span class="bento-glyph" aria-hidden="true">{{ p.name }}</span>
{%- endif %}
</div>
|