v0.0.51: 사이드바 열 높이 고정·발행일 YYYY.MM.DD 통일
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,7 +22,7 @@ const { data: navigation } = await useFetch('/api/navigation', {
|
||||
|
||||
<template>
|
||||
<aside
|
||||
class="left-sidebar site-sidebar hidden overflow-hidden border-r border-[var(--site-line)] transition-[width,opacity,border-color] duration-300 ease-out lg:sticky lg:top-[57px] lg:z-10 lg:max-h-[calc(100vh-57px)] lg:self-start lg:flex lg:flex-col"
|
||||
class="left-sidebar site-sidebar hidden overflow-hidden transition-[width,opacity,border-color] duration-300 ease-out lg:sticky lg:top-[57px] lg:z-10 lg:h-[calc(100vh-57px)] lg:max-h-[calc(100vh-57px)] lg:self-start lg:flex lg:flex-col"
|
||||
:class="menuOpen ? 'w-[287px] opacity-100' : 'w-0 opacity-0 border-transparent'"
|
||||
>
|
||||
<div class="left-sidebar__scroll site-sidebar-scroll min-h-0 flex-1">
|
||||
@@ -99,7 +99,7 @@ const { data: navigation } = await useFetch('/api/navigation', {
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
<button
|
||||
class="left-sidebar__theme-dot site-panel-hover site-interactive grid h-7 w-7 place-items-center rounded-full border border-[var(--site-line)]"
|
||||
class="left-sidebar__theme-dot site-panel-hover site-interactive grid h-7 w-7 place-items-center rounded-full"
|
||||
type="button"
|
||||
:aria-label="isDarkMode ? '라이트 모드로 전환' : '다크 모드로 전환'"
|
||||
:title="isDarkMode ? '라이트 모드' : '다크 모드'"
|
||||
|
||||
@@ -19,7 +19,7 @@ const { data: siteSettings } = await useFetch('/api/site-settings', {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside class="right-sidebar site-sidebar hidden w-[287px] overflow-hidden border-l border-[var(--site-line)] lg:sticky lg:top-[57px] lg:z-10 lg:max-h-[calc(100vh-57px)] lg:self-start lg:flex lg:flex-col">
|
||||
<aside class="right-sidebar site-sidebar hidden w-[287px] overflow-hidden border-l border-[var(--site-line)] lg:sticky lg:top-[57px] lg:z-10 lg:h-[calc(100vh-57px)] lg:max-h-[calc(100vh-57px)] lg:self-start lg:flex lg:flex-col">
|
||||
<div class="right-sidebar__scroll site-sidebar-scroll min-h-0 flex-1">
|
||||
<div class="right-sidebar__block site-sidebar-section py-5 pl-5 pr-0">
|
||||
<div class="right-sidebar__profile flex items-center gap-3">
|
||||
|
||||
22
composables/formatPostDate.js
Normal file
22
composables/formatPostDate.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 공개 화면용 게시 날짜를 YYYY.MM.DD 형식으로 변환한다.
|
||||
* @param {string | null | undefined} value - ISO 8601 등 파싱 가능한 날짜 문자열
|
||||
* @returns {string} 빈 문자열 또는 YYYY.MM.DD
|
||||
*/
|
||||
export function formatPostDate(value) {
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const date = new Date(value)
|
||||
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
|
||||
return `${year}.${month}.${day}`
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
# 의사결정 이력
|
||||
|
||||
## 2026-05-08 v0.0.51
|
||||
|
||||
### 사이드바 고정 높이와 발행일 포맷
|
||||
|
||||
`lg+` 그리드에서 `items-start` 때문에 사이드바 박스 높이가 콘텐츠만큼만 잡히면 내부 `flex-1` 스크롤 영역이 늘어나지 않아 푸터가 상단 블록 바로 아래에 붙는다. 데스크톱에서 열 높이를 `h-[calc(100vh-57px)]`(및 동일 `max-h`)로 고정해 flex 컬럼 안에서 푸터를 열 하단에 두었다. 공개 피드·상세의 발행일은 `formatPostDate`로 `YYYY.MM.DD`를 통일하고 `<time datetime>`에 원본 ISO를 넣어 접근성을 맞춘다.
|
||||
|
||||
## 2026-05-08 v0.0.50
|
||||
|
||||
### 문서 스크롤과 스티키 사이드바
|
||||
|
||||
10
docs/map.md
10
docs/map.md
@@ -11,13 +11,19 @@
|
||||
| layouts/admin.vue | 관리자 전체, 글 작성/수정 화면의 전체 화면 편집 모드와 문서 스크롤 잠금 |
|
||||
| layouts/page.vue | 고정 페이지 전체 화면 |
|
||||
|
||||
## Composables
|
||||
|
||||
| 파일 | 용도 |
|
||||
|------|------|
|
||||
| composables/formatPostDate.js | 공개 화면 게시일 `YYYY.MM.DD` 포맷 |
|
||||
|
||||
## 사이트 컴포넌트
|
||||
|
||||
| 파일 | 화면 위치 |
|
||||
|------|-----------|
|
||||
| components/site/SiteHeader.vue | 모든 공개 페이지 상단 |
|
||||
| components/site/LeftSidebar.vue | 왼쪽 사이드바, `sticky`+내부 무스크롤바 스크롤, 하단 푸터 고정 |
|
||||
| components/site/RightSidebar.vue | 오른쪽 사이드바, 동일 패턴, 카피라이트 하단 고정 |
|
||||
| components/site/LeftSidebar.vue | 왼쪽 사이드바, `sticky`+`h/max-h: calc(100vh-57px)`+내부 무스크롤바 스크롤, 하단 푸터 고정 |
|
||||
| components/site/RightSidebar.vue | 오른쪽 사이드바, 동일 패턴(고정 열 높이), 카피라이트 하단 고정 |
|
||||
| components/site/MainColumn.vue | 메인 화면 중앙 |
|
||||
| components/site/PostCard.vue | 목록의 게시물 카드, 대표 이미지 썸네일, 카드 hover 인터랙션 |
|
||||
| components/site/TagHeader.vue | 태그 페이지 헤더 |
|
||||
|
||||
10
docs/spec.md
10
docs/spec.md
@@ -20,9 +20,9 @@
|
||||
| Header | 높이 57px, `sticky top-0`, `shrink-0` |
|
||||
| Shell | `min-height: 100vh`, `flex` 세로 컬럼 |
|
||||
| 그리드(데스크톱 `lg+`) | `items-start`, 본문(중앙) 높이에 맞춰 행이 늘어남 — **문서(`html`/`body`) 스크롤**로 긴 본문 처리(스크롤바는 브라우저 오른쪽) |
|
||||
| Left Aside | 너비 287px, `sticky top-[57px]`, `max-h-[calc(100vh-57px)]`, 내부 상단은 `.site-sidebar-scroll`(스크롤바 숨김), 하단 푸터 `shrink-0` |
|
||||
| Left Aside | 너비 287px, `sticky top-[57px]`, `h-[calc(100vh-57px)]`와 `max-h` 동일(뷰포트 기준 고정 높이), 내부 상단은 `.site-sidebar-scroll`(스크롤바 숨김), 하단 푸터 `shrink-0`·상단 보더로 스크롤 영역과 구분 |
|
||||
| Main | 너비 720px, 별도 `overflow-y` 없음 — 뷰포트와 동일한 문서 스크롤에 포함 |
|
||||
| Right Aside | Left와 동일 패턴(스티키·최대 높이·내부 무스크롤바 스크롤·하단 카피라이트) |
|
||||
| Right Aside | Left와 동일 패턴(스티키·고정 높이·내부 무스크롤바 스크롤·하단 카피라이트) |
|
||||
|
||||
### 메뉴 토글
|
||||
|
||||
@@ -45,6 +45,12 @@
|
||||
- Main 좌우 패딩: 24px → 20px
|
||||
- 공개 게시물 본문은 콘텐츠 타입별 컴포넌트로 분리해 추후 스타일 변경이 쉽도록 구성
|
||||
|
||||
### 공개 목록·상세의 발행일 표시
|
||||
|
||||
- API의 ISO 8601 `publishedAt`를 공개 UI에서는 로컬 날짜 기준 `YYYY.MM.DD`로 표시한다.
|
||||
- 변환은 `composables/formatPostDate.js`의 `formatPostDate`를 사용한다.
|
||||
- `<time>`에는 표시용 문자열과 함께 가능한 경우 원본 시각을 `datetime` 속성으로 둔다.
|
||||
|
||||
### Page 페이지
|
||||
|
||||
- About, Projects, Links, Contact, 서비스 소개 페이지 등 고정 콘텐츠에 사용
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## v0.0.51
|
||||
|
||||
- 좌·우 사이드바 데스크톱 열 높이를 `calc(100vh - 57px)`로 고정해 내부 스크롤·하단 푸터 배치가 뷰포트 기준으로 맞도록 수정.
|
||||
- 사이드 푸터에 상단 보더 추가(스크롤 영역과 시각적 구분).
|
||||
- 공개 피드·게시 상세·아카이브 발행일 `YYYY.MM.DD` 통일, `composables/formatPostDate.js` 사용·`<time datetime>` 보강.
|
||||
- `pages/tag/[slug].vue` 동일 날짜·datetime 처리.
|
||||
|
||||
## v0.0.50
|
||||
|
||||
- 데스크톱(`lg+`)에서 긴 본문은 **문서 스크롤**(브라우저 오른쪽 스크롤바)로 처리하고, `main` 단독 스크롤은 제거.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "0.0.50",
|
||||
"version": "0.0.51",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -47,22 +47,6 @@ const onDocumentPointerDown = (event) => {
|
||||
closePostFeedStyleMenu()
|
||||
}
|
||||
|
||||
/**
|
||||
* 날짜 표시 형식 변환
|
||||
* @param {string | null} value - ISO 날짜 문자열
|
||||
* @returns {string} 화면 표시 날짜
|
||||
*/
|
||||
const formatPostDate = (value) => {
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
}).format(new Date(value))
|
||||
}
|
||||
|
||||
/**
|
||||
* 태그 슬러그로 태그 정보 조회
|
||||
* @param {string | undefined} slug - 태그 슬러그
|
||||
@@ -94,6 +78,7 @@ const mapLatestPost = (post, index) => {
|
||||
tagName: tagMeta.name,
|
||||
tagColor: tagMeta.color,
|
||||
publishedAt: formatPostDate(post.publishedAt),
|
||||
publishedAtIso: post.publishedAt || '',
|
||||
to: `/post/${post.slug}`,
|
||||
isFeatured: index === 0
|
||||
}
|
||||
@@ -366,7 +351,7 @@ const scrollFeatured = (direction) => {
|
||||
<h2 class="max-w-[90%] text-sm font-medium leading-tight">
|
||||
<NuxtLink :to="post.to" class="transition-opacity duration-200 hover:opacity-75">
|
||||
<span v-if="post.isFeatured" class="mr-1 inline-flex text-[var(--site-accent)] [&_svg]:-mt-0.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M13 3v7h6l-8 11v-7H5l8-11" />
|
||||
</svg>
|
||||
</span>
|
||||
@@ -382,7 +367,7 @@ const scrollFeatured = (direction) => {
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs site-muted sm:gap-1.5">
|
||||
<time>{{ post.publishedAt }}</time>
|
||||
<time v-if="post.publishedAt" :datetime="post.publishedAtIso">{{ post.publishedAt }}</time>
|
||||
<span class="text-[var(--site-line)]">/</span>
|
||||
<span
|
||||
class="rounded-sm px-1.5 py-px font-medium text-[var(--site-text)]"
|
||||
@@ -391,7 +376,7 @@ const scrollFeatured = (direction) => {
|
||||
{{ post.tagName }}
|
||||
</span>
|
||||
<span class="text-[var(--site-line)]">/</span>
|
||||
<span class="flex items-center gap-0.75">
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="-mt-px">
|
||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
||||
</svg>
|
||||
|
||||
@@ -21,23 +21,6 @@ if (!post.value) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 게시물 날짜 표시 형식 변환 (Thred 참고)
|
||||
* @param {string | null} value - ISO 날짜 문자열
|
||||
* @returns {string} 화면 표시 날짜
|
||||
*/
|
||||
const formatPostDate = (value) => {
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
}).format(new Date(value))
|
||||
}
|
||||
|
||||
const primaryTagSlug = computed(() => post.value.tags?.[0] || '')
|
||||
const primaryTagMeta = computed(() => {
|
||||
const matchedTag = tags.value.find((item) => item.slug === primaryTagSlug.value)
|
||||
|
||||
@@ -3,24 +3,6 @@ const { data: posts } = await useFetch('/api/posts', {
|
||||
default: () => []
|
||||
})
|
||||
|
||||
/**
|
||||
* 날짜 표시 형식 변환
|
||||
* @param {string | null} value - ISO 날짜 문자열
|
||||
* @returns {string} 화면 표시 날짜
|
||||
*/
|
||||
const formatPostDate = (value) => {
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const date = new Date(value)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
|
||||
return `${year}.${month}.${day}`
|
||||
}
|
||||
|
||||
const postCards = computed(() => posts.value.map((post) => ({
|
||||
title: post.title,
|
||||
excerpt: post.excerpt,
|
||||
|
||||
@@ -10,24 +10,6 @@ const { data: posts } = await useFetch('/api/posts', {
|
||||
default: () => []
|
||||
})
|
||||
|
||||
/**
|
||||
* 날짜 표시 형식 변환
|
||||
* @param {string | null} value - ISO 날짜 문자열
|
||||
* @returns {string} 화면 표시 날짜
|
||||
*/
|
||||
const formatPostDate = (value) => {
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const date = new Date(value)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
|
||||
return `${year}.${month}.${day}`
|
||||
}
|
||||
|
||||
const tag = computed(() => tags.value.find((item) => item.slug === slug.value))
|
||||
|
||||
const tagPosts = computed(() => posts.value
|
||||
@@ -40,6 +22,7 @@ const tagPosts = computed(() => posts.value
|
||||
tagColor: tags.value.find((item) => item.slug === (post.tags?.[0] || slug.value))?.color || '#4d4d4d',
|
||||
isFeatured: index === 0,
|
||||
publishedAt: formatPostDate(post.publishedAt),
|
||||
publishedAtIso: post.publishedAt || '',
|
||||
to: `/post/${post.slug}`
|
||||
})))
|
||||
</script>
|
||||
@@ -102,7 +85,7 @@ const tagPosts = computed(() => posts.value
|
||||
{{ post.excerpt }}
|
||||
</p>
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs site-muted sm:gap-1.5">
|
||||
<time>{{ post.publishedAt }}</time>
|
||||
<time v-if="post.publishedAt" :datetime="post.publishedAtIso">{{ post.publishedAt }}</time>
|
||||
<span class="text-[var(--site-line)]">/</span>
|
||||
<span
|
||||
class="rounded-sm px-1.5 py-px font-medium text-[var(--site-text)]"
|
||||
|
||||
Reference in New Issue
Block a user