v0.1.23 좌측 네비 및 태그 라우트 정리

This commit is contained in:
2026-04-14 10:43:24 +09:00
parent f746480163
commit 615dd8968d
11 changed files with 215 additions and 100 deletions

View File

@@ -171,46 +171,84 @@ body.left-sidebar-collapsed .sidebar--left {
}
.menu-groups {
display: grid;
gap: 2px;
padding-top: 18px;
padding-bottom: 16px;
display: block;
}
.menu-groups__list {
display: flex;
flex-direction: column;
}
.menu-group {
border-bottom: 0;
}
.menu-group__trigger {
.menu-group--nav {
gap: 0;
}
.menu-group__trigger,
.menu-group__link {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 8px 10px;
padding: 6px 0;
border: 0;
border-radius: 15px;
border-radius: 18px;
background: none;
color: inherit;
cursor: pointer;
transition: background-color 0.22s ease, color 0.22s ease, transform 0.22s ease;
}
.menu-group__content {
height: 0;
padding: 2px 0 0;
transition: height 0.32s cubic-bezier(0.22, 1, 0.36, 1);
will-change: height;
overflow: hidden;
.menu-group__trigger {
justify-content: space-between;
}
.menu-group__link {
flex: 1 1 auto;
gap: 8px;
}
.menu-group__link:hover,
.menu-group__link--toggle:hover,
.menu-group__toggle:hover {
background: var(--surface-muted);
}
.menu-group__link:hover,
.menu-group__link--toggle:hover {
padding-left: 12px;
padding-right: 12px;
}
.menu-group__toggle {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 32px;
height: 32px;
border: 0;
background: transparent;
}
.menu-group__panel {
will-change: height, opacity;
}
.menu-group__content > * {
padding: 6px 0 12px;
padding: 3px 0 0;
}
.menu-group .nav {
margin: 0;
padding: 2px 0 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
margin-left: 14px;
margin-top: 3px;
border-left: 1px solid var(--border);
padding-left: 0;
}
.menu-group .nav li + li,
@@ -224,10 +262,12 @@ body.left-sidebar-collapsed .sidebar--left {
}
.menu-group .nav a {
display: block;
padding: 8px 14px 8px 30px;
border-radius: 13px;
transition: background-color 0.22s ease, color 0.22s ease;
display: flex;
align-items: center;
gap: 8px;
padding: 6px 0 6px 16px;
border-radius: 18px;
transition: background-color 0.22s ease, color 0.22s ease, padding 0.22s ease;
font-size: 13px;
}
@@ -235,6 +275,8 @@ body.left-sidebar-collapsed .sidebar--left {
.menu-sub-link:hover {
background: var(--surface-muted);
color: var(--text);
padding-left: 12px;
padding-right: 12px;
}
.link-list {
@@ -245,19 +287,12 @@ body.left-sidebar-collapsed .sidebar--left {
.menu-sub-link {
display: block;
padding: 8px 14px 8px 30px;
border-radius: 13px;
transition: background-color 0.22s ease, color 0.22s ease;
padding: 6px 0 6px 16px;
border-radius: 18px;
transition: background-color 0.22s ease, color 0.22s ease, padding 0.22s ease;
font-size: 13px;
}
.menu-link {
display: inline-flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.menu-link__marker {
position: relative;
flex: 0 0 auto;
@@ -275,22 +310,15 @@ body.left-sidebar-collapsed .sidebar--left {
letter-spacing: -0.015em;
}
.menu-group__trigger:hover,
.menu-group__trigger[aria-expanded="true"] {
background: var(--surface-muted);
}
.menu-group__trigger:hover {
transform: translateX(1px);
}
.menu-group__trigger:hover .menu-link__label,
.menu-group__trigger[aria-expanded="true"] .menu-link__label {
.menu-group__trigger[aria-expanded="true"] .menu-link__label,
.menu-group__link:hover .menu-link__label {
color: var(--text);
}
.menu-group__trigger:hover .menu-link__marker,
.menu-group__trigger[aria-expanded="true"] .menu-link__marker {
.menu-group__trigger[aria-expanded="true"] .menu-link__marker,
.menu-group__link:hover .menu-link__marker {
width: 10px;
height: 10px;
border-radius: 999px;
@@ -314,20 +342,12 @@ body.left-sidebar-collapsed .sidebar--left {
display: block;
}
.menu-group__chevron-icon--up {
display: none;
}
.menu-group.is-open .menu-group__chevron {
color: var(--text-soft);
}
.menu-group.is-open .menu-group__chevron-icon--down {
display: none;
}
.menu-group.is-open .menu-group__chevron-icon--up {
display: block;
.menu-group__content--nav {
padding-top: 0;
}
.sidebar-card {

36
authors-index.hbs Normal file
View File

@@ -0,0 +1,36 @@
{{!< default}}
<main class="content-area">
<section class="stack-section">
<header class="section-header text-center">
<h1 class="section-title">Authors</h1>
<p class="section-description">Browse by author</p>
</header>
{{#get "authors" limit="100" include="count.posts"}}
<div class="author-directory grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
{{#foreach authors}}
<a class="author-directory__card group flex min-h-[128px] flex-col rounded-[14px] border border-[var(--border)] bg-[var(--surface)] p-4 text-left transition-colors hover:bg-[var(--surface-muted)]" href="{{url}}">
<span class="flex items-start gap-3">
{{#if profile_image}}
<img class="h-11 w-11 rounded-full object-cover" src="{{img_url profile_image size="xs"}}" alt="{{name}}">
{{else}}
<span class="flex h-11 w-11 items-center justify-center rounded-full bg-[var(--surface-muted)] text-sm font-semibold">A</span>
{{/if}}
<span class="min-w-0 flex-1">
<strong class="block text-[15px] font-semibold tracking-[-0.02em]">{{name}}</strong>
{{#if bio}}
<span class="mt-1 line-clamp-3 block text-[13px] leading-5 text-[var(--text-soft)]">{{bio}}</span>
{{else}}
<span class="mt-1 block text-[13px] leading-5 text-[var(--text-soft)]">Posts by {{name}}.</span>
{{/if}}
</span>
<img class="h-4 w-4 shrink-0 opacity-70 transition-opacity group-hover:opacity-100" src="{{asset "icons/arrow_outward.svg"}}" alt="">
</span>
<span class="mt-auto pt-3 text-[13px] font-medium text-[var(--text-soft)]">{{plural count.posts empty="0 posts" singular="% post" plural="% posts"}}</span>
</a>
{{/foreach}}
</div>
{{/get}}
</section>
</main>

View File

@@ -1,7 +1,7 @@
# 배포 가이드
## 현재 버전
- `v0.1.21`
- `v0.1.23`
## Git 기본 설정
- 저장소 작성자 정보는 아래 값으로 통일한다.
@@ -55,6 +55,10 @@ npm run build:tailwind
- Alpine 결과물은 `assets/built/alpine.js`에 생성된다.
- Tailwind 결과물은 `assets/built/tailwind.css`에 생성되고, Ghost 테마에서 `screen.css` 다음에 로드된다.
## `/tags/`, `/authors/` 연결 방법
- 가장 쉬운 방법은 Ghost Admin에서 페이지를 만들고 슬러그를 각각 `tags`, `authors`로 지정한 뒤 템플릿을 연결하는 방식이다.
- 페이지 방식 대신 커스텀 라우트를 쓰려면 [routes.yaml.example](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/routes.yaml.example)를 기준으로 Ghost의 `routes.yaml``/tags/`, `/authors/`를 연결한다.
## 로컬 빌드 검증
```bash
npm run build

View File

@@ -1,5 +1,8 @@
# 의사결정 이력
## 2026-04-14 v0.1.23
좌측 네비게이션은 원본의 Tailwind/Alpine 구조를 더 직접적으로 따르되, 기존 구현에서 선호한 열고 닫히는 애니메이션은 유지하기로 했다. 또한 `/tags/`, `/authors/`는 Ghost 기본 라우트가 아니므로, 페이지 슬러그 방식뿐 아니라 `routes.yaml` 커스텀 라우트 방식도 함께 지원할 수 있도록 별도 인덱스 템플릿과 예시 라우트 파일을 추가했다.
## 2026-04-14 v0.1.22
원본 테마와 spacing 차이가 크게 나는 원인 중 하나가 브라우저 기본 리스트 스타일이었다. Tailwind preflight를 끈 상태를 유지하고 있으므로, 필요한 범위의 전역 리셋만 `screen.css`에 수동으로 추가해 기본 `ul`, `ol`, `menu` 패딩과 불릿을 제거했다. 카테고리 화살표는 회전 기반 표현 대신 상태별 아이콘 교체 방식으로 바꿔 원본과 더 가깝게 맞췄다.

View File

@@ -1,13 +1,15 @@
# 파일-화면 매핑 가이드
## 현재 버전
- `v0.1.21`
- `v0.1.23`
## 공통 레이아웃
- [default.hbs](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/default.hbs): 전체 3열 셸과 공통 자산 로드
- [partials/site/sidebar-left.hbs](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/partials/site/sidebar-left.hbs): 좌측 탐색/직접 링크형 Tags·Authors 메뉴/카테고리 아코디언/푸터
- [page-tags.hbs](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/page-tags.hbs): `slug=tags` 페이지용 태그 디렉터리
- [page-authors.hbs](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/page-authors.hbs): `slug=authors` 페이지용 작성자 디렉터리
- [tags-index.hbs](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/tags-index.hbs): `/tags/` 커스텀 라우트용 태그 디렉터리
- [authors-index.hbs](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/authors-index.hbs): `/authors/` 커스텀 라우트용 작성자 디렉터리
- [partials/site/topbar.hbs](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/partials/site/topbar.hbs): 상단 검색/CTA/다크모드
- [partials/site/sidebar-right.hbs](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/partials/site/sidebar-right.hbs): 구독/추천/작성자/푸터
@@ -32,4 +34,5 @@
- [assets/built/theme.js](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/assets/built/theme.js): 인터랙션 스크립트
- [assets/styles/tailwind.css](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/assets/styles/tailwind.css): Tailwind 입력 파일
- [tailwind.config.js](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/tailwind.config.js): Tailwind 스캔 경로 및 테마 설정
- [routes.yaml.example](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/routes.yaml.example): Ghost 커스텀 라우트 예시
- [package.json](/Users/bicute/Desktop/UGREEN/GHOST%20THEME/package.json): Ghost 테마 메타데이터

View File

@@ -1,7 +1,7 @@
# 기술 명세
## 현재 버전
- `v0.1.22`
- `v0.1.23`
## 테마 개요
- Ghost `v5` 대응 커스텀 테마
@@ -20,6 +20,7 @@
- `author.hbs`는 페이지 컨텍스트의 작성자 데이터를 직접 사용
- `page-tags.hbs`, `page-authors.hbs`는 각각 `slug=tags`, `slug=authors` 페이지에 연결 가능
- 태그/작성자 디렉터리 목록은 현재 `limit="100"` 기준
- `tags-index.hbs`, `authors-index.hbs``routes.yaml` 커스텀 라우트로 `/tags/`, `/authors/`에 연결 가능
## 주요 스타일 방향
- 밝은 크림톤 배경 + 오렌지 포인트

View File

@@ -1,5 +1,11 @@
# 업데이트 로그
## v0.1.23 - 2026-04-14
- 좌측 네비게이션 Tailwind/Alpine 구조로 재정리.
- 네비게이션 아코디언 슬라이드 애니메이션 유지.
- `/tags/`, `/authors/` 커스텀 라우트용 템플릿 추가.
- `routes.yaml.example` 추가.
## v0.1.22 - 2026-04-14
- 전역 리스트 리셋 추가.
- 카테고리 아코디언 화살표 상태 아이콘 수정.

View File

@@ -1,6 +1,6 @@
{
"name": "ghost-theme-thred-clone",
"version": "0.1.22",
"version": "0.1.23",
"private": true,
"description": "A Ghost theme inspired by the Thred reference layout.",
"keywords": [

View File

@@ -1,59 +1,57 @@
<aside class="sidebar sidebar--left">
<div class="sidebar__inner">
<nav class="menu-groups">
<section class="menu-group is-open">
<button class="menu-group__trigger" type="button" data-accordion aria-expanded="true">
<span class="menu-link">
<nav class="menu-groups border-b border-brd pl-4 pr-3 sm:pl-5 xl:pl-0 py-3" data-nav="menu" data-primary-nav x-data="{ homePagesOpen: true, membersOpen: false }">
<ul class="menu-groups__list flex flex-col gap-0.75 text-typ text-sm">
<li class="menu-group menu-group--nav nav-toggle is-mainitem flex items-center flex-wrap w-full relative group" :class="{ 'is-open': homePagesOpen }" data-label="Home pages" data-slug="home-pages" data-length="10" aria-haspopup="true">
<a class="menu-group__link menu-group__link--toggle flex gap-2 items-center flex-1 py-1.5 rounded-theme transition-[padding]" href="#" role="button" @click.prevent="homePagesOpen = !homePagesOpen" :aria-expanded="homePagesOpen.toString()" aria-haspopup="true">
<span class="menu-link__marker"></span>
<span class="menu-link__label">Home pages</span>
</span>
<span class="menu-group__chevron" aria-hidden="true">
<img class="menu-group__chevron-icon menu-group__chevron-icon--down" src="{{asset "icons/keyboard_arrow_down.svg"}}" alt="">
<img class="menu-group__chevron-icon menu-group__chevron-icon--up" src="{{asset "icons/keyboard_arrow_up.svg"}}" alt="">
</span>
</button>
<div class="menu-group__content" data-accordion-content>
{{navigation}}
</div>
</section>
</a>
<button class="menu-group__toggle relative cursor-pointer p-2 rounded-r-theme" type="button" title="Menu toggle" aria-label="Toggle submenu" aria-controls="home-pages" :aria-expanded="homePagesOpen.toString()" @click="homePagesOpen = !homePagesOpen" aria-haspopup="true">
<img x-show="!homePagesOpen" x-cloak class="menu-group__chevron-icon menu-group__chevron-icon--down" src="{{asset "icons/keyboard_arrow_down.svg"}}" alt="">
<img x-show="homePagesOpen" x-cloak class="menu-group__chevron-icon menu-group__chevron-icon--up" src="{{asset "icons/keyboard_arrow_up.svg"}}" alt="">
</button>
<div class="menu-group__panel basis-full overflow-hidden transition-[height,opacity] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]" x-ref="homePages" :style="homePagesOpen ? 'height:' + $refs.homePages.scrollHeight + 'px; opacity:1; visibility:visible;' : 'height:0px; opacity:0; visibility:hidden;'">
<div class="menu-group__content menu-group__content--nav" id="home-pages">
{{navigation}}
</div>
</div>
</li>
<section class="menu-group menu-group--link">
<a class="menu-group__link px-3 py-2 flex items-center rounded-[15px] transition-[padding,background-color,color] hover:bg-[var(--surface-muted)] hover:px-4" href="/tags/">
<span class="menu-link">
<li class="menu-group menu-group--nav menu-group--link flex items-center flex-wrap w-full relative group" data-label="Tags" data-slug="tags" data-length="4">
<a class="menu-group__link flex gap-2 items-center flex-1 py-1.5 rounded-theme transition-[padding]" href="/tags/">
<span class="menu-link__marker"></span>
<span class="menu-link__label">Tags</span>
</span>
</a>
</section>
</a>
</li>
<section class="menu-group menu-group--link">
<a class="menu-group__link px-3 py-2 flex items-center rounded-[15px] transition-[padding,background-color,color] hover:bg-[var(--surface-muted)] hover:px-4" href="/authors/">
<span class="menu-link">
<li class="menu-group menu-group--nav menu-group--link flex items-center flex-wrap w-full relative group" data-label="Authors" data-slug="authors" data-length="7">
<a class="menu-group__link flex gap-2 items-center flex-1 py-1.5 rounded-theme transition-[padding]" href="/authors/">
<span class="menu-link__marker"></span>
<span class="menu-link__label">Authors</span>
</span>
</a>
</section>
</a>
</li>
<section class="menu-group">
<button class="menu-group__trigger" type="button" data-accordion aria-expanded="false">
<span class="menu-link">
<li class="menu-group menu-group--nav nav-toggle is-mainitem flex items-center flex-wrap w-full relative group" :class="{ 'is-open': membersOpen }" data-label="Members" data-slug="members" data-length="7" aria-haspopup="true">
<a class="menu-group__link menu-group__link--toggle flex gap-2 items-center flex-1 py-1.5 rounded-theme transition-[padding]" href="#" role="button" @click.prevent="membersOpen = !membersOpen" :aria-expanded="membersOpen.toString()" aria-haspopup="true">
<span class="menu-link__marker"></span>
<span class="menu-link__label">Members</span>
</span>
<span class="menu-group__chevron" aria-hidden="true">
<img class="menu-group__chevron-icon menu-group__chevron-icon--down" src="{{asset "icons/keyboard_arrow_down.svg"}}" alt="">
<img class="menu-group__chevron-icon menu-group__chevron-icon--up" src="{{asset "icons/keyboard_arrow_up.svg"}}" alt="">
</span>
</button>
<div class="menu-group__content" data-accordion-content>
<ul class="link-list">
<li><a class="menu-sub-link" href="#/portal/signup">Sign up</a></li>
<li><a class="menu-sub-link" href="#/portal/signin">Sign in</a></li>
<li><a class="menu-sub-link" href="#/portal/account">Account</a></li>
</ul>
</div>
</section>
</a>
<button class="menu-group__toggle relative cursor-pointer p-2 rounded-r-theme" type="button" title="Menu toggle" aria-label="Toggle submenu" aria-controls="members" :aria-expanded="membersOpen.toString()" @click="membersOpen = !membersOpen" aria-haspopup="true">
<img x-show="!membersOpen" x-cloak class="menu-group__chevron-icon menu-group__chevron-icon--down" src="{{asset "icons/keyboard_arrow_down.svg"}}" alt="">
<img x-show="membersOpen" x-cloak class="menu-group__chevron-icon menu-group__chevron-icon--up" src="{{asset "icons/keyboard_arrow_up.svg"}}" alt="">
</button>
<div class="menu-group__panel basis-full overflow-hidden transition-[height,opacity] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]" x-ref="members" :style="membersOpen ? 'height:' + $refs.members.scrollHeight + 'px; opacity:1; visibility:visible;' : 'height:0px; opacity:0; visibility:hidden;'">
<div class="menu-group__content menu-group__content--nav" id="members">
<ul class="link-list">
<li><a class="menu-sub-link" href="#/portal/signup">Sign up</a></li>
<li><a class="menu-sub-link" href="#/portal/signin">Sign in</a></li>
<li><a class="menu-sub-link" href="#/portal/account">Account</a></li>
</ul>
</div>
</div>
</li>
</ul>
</nav>
<section class="sidebar-card sidebar-card--categories border-b border-brd px-5 sm:px-6 py-4 sm:py-5 xl:pl-0 pr-3 sm:pr-3 flex flex-col gap-1.5" x-data="{ categoriesOpen: window.innerWidth >= 1024 }" x-init="window.addEventListener('resize', () => { categoriesOpen = window.innerWidth >= 1024 })" data-sidebar-categories>

14
routes.yaml.example Normal file
View File

@@ -0,0 +1,14 @@
routes:
/tags/:
template: tags-index
/authors/:
template: authors-index
collections:
/:
permalink: /{slug}/
template: index
taxonomies:
tag: /tag/{slug}/
author: /author/{slug}/

30
tags-index.hbs Normal file
View File

@@ -0,0 +1,30 @@
{{!< default}}
<main class="content-area">
<section class="stack-section">
<header class="section-header text-center">
<h1 class="section-title">Tags</h1>
<p class="section-description">Browse by topic</p>
</header>
{{#get "tags" limit="100" include="count.posts" order="count.posts desc"}}
<div class="tag-directory grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
{{#foreach tags}}
<a class="tag-directory__card group relative flex min-h-[128px] flex-col rounded-[14px] border border-[var(--border)] bg-[var(--surface)] p-4 text-left transition-colors hover:bg-[var(--surface-muted)]" href="{{url}}"{{#if accent_color}} style="--tag-accent: {{accent_color}};"{{/if}}>
<span class="tag-directory__accent absolute inset-y-0 left-0 w-[3px] rounded-l-[14px] bg-[var(--tag-accent,var(--accent))]"></span>
<span class="flex items-start justify-between gap-3">
<strong class="text-[15px] font-semibold tracking-[-0.02em]">{{name}}</strong>
<img class="h-4 w-4 shrink-0 opacity-70 transition-opacity group-hover:opacity-100" src="{{asset "icons/arrow_outward.svg"}}" alt="">
</span>
{{#if description}}
<p class="mt-2 line-clamp-3 text-[13px] leading-5 text-[var(--text-soft)]">{{description}}</p>
{{else}}
<p class="mt-2 line-clamp-3 text-[13px] leading-5 text-[var(--text-soft)]">Posts filed under {{name}}.</p>
{{/if}}
<span class="mt-auto pt-3 text-[13px] font-medium text-[var(--text-soft)]">{{plural count.posts empty="0 posts" singular="% post" plural="% posts"}}</span>
</a>
{{/foreach}}
</div>
{{/get}}
</section>
</main>