removed numbers + card redesign
This commit is contained in:
@@ -42,19 +42,6 @@ function formatMonth(date: string) {
|
||||
return new Date(date).toLocaleDateString('en-US', { month: 'short' }).toUpperCase();
|
||||
}
|
||||
|
||||
function toRoman(n: number): string {
|
||||
const map: [number, string][] = [
|
||||
[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'],
|
||||
[100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'],
|
||||
[10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'],
|
||||
];
|
||||
let out = '';
|
||||
for (const [val, sym] of map) {
|
||||
while (n >= val) { out += sym; n -= val; }
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Deterministic salon-hang layout. Each tile gets a col-span (out of 12) and an aspect ratio.
|
||||
// The cycle is chosen so the room reads asymmetric but balanced.
|
||||
const LAYOUT_CYCLE: Array<{ col: number; aspect: string; tilt: number }> = [
|
||||
@@ -123,7 +110,6 @@ export default function PostList({ posts: initialPosts, isAdmin = false }: Props
|
||||
const displayTitle = post.title || formatSlug(post.slug);
|
||||
const isDeleting = deleting === post.slug;
|
||||
const layout = LAYOUT_CYCLE[idx % LAYOUT_CYCLE.length];
|
||||
const exhibitNumber = toRoman(idx + 1);
|
||||
const hasCover = !!post.cover_image?.url;
|
||||
|
||||
return (
|
||||
@@ -141,8 +127,6 @@ export default function PostList({ posts: initialPosts, isAdmin = false }: Props
|
||||
style={{ transform: `rotate(${layout.tilt}deg)` }}
|
||||
aria-label={`View ${displayTitle}`}
|
||||
>
|
||||
<span className="plate-tag">№ {exhibitNumber}</span>
|
||||
|
||||
<div
|
||||
className="plate-image"
|
||||
style={{ aspectRatio: layout.aspect }}
|
||||
@@ -178,18 +162,16 @@ export default function PostList({ posts: initialPosts, isAdmin = false }: Props
|
||||
</div>
|
||||
|
||||
<div className="plate-caption">
|
||||
<div className="min-w-0">
|
||||
<div className="plate-caption-title truncate">{displayTitle}</div>
|
||||
<div className="plate-caption-title">{displayTitle}</div>
|
||||
{post.summary && (
|
||||
<div className="mt-1 text-xs text-[var(--subtext0)] font-sans italic line-clamp-1">
|
||||
{post.summary}
|
||||
</div>
|
||||
<div className="plate-caption-summary">{post.summary}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="plate-caption-meta">
|
||||
<span>{formatMonth(post.date)}</span>
|
||||
<span className="opacity-50 mx-1">·</span>
|
||||
<span className="plate-caption-sep" aria-hidden="true">·</span>
|
||||
<span>{formatYear(post.date)}</span>
|
||||
<span className="plate-caption-sep" aria-hidden="true">·</span>
|
||||
<span>{post.reading_time} min</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -28,19 +28,6 @@ function formatDate(d: string) {
|
||||
return new Date(d).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
|
||||
}
|
||||
|
||||
function toRoman(n: number): string {
|
||||
const map: [number, string][] = [
|
||||
[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'],
|
||||
[100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'],
|
||||
[10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'],
|
||||
];
|
||||
let out = '';
|
||||
for (const [val, sym] of map) {
|
||||
while (n >= val) { out += sym; n -= val; }
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function formatSlug(s: string) {
|
||||
if (!s) return '';
|
||||
return s.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
||||
@@ -49,7 +36,7 @@ function formatSlug(s: string) {
|
||||
let post: PostDetail | null = null;
|
||||
let html = '';
|
||||
let error = '';
|
||||
let neighbors: { prev?: PostInfo; next?: PostInfo; index: number; total: number } = { index: -1, total: 0 };
|
||||
let neighbors: { prev?: PostInfo; next?: PostInfo } = {};
|
||||
|
||||
try {
|
||||
const [postRes, listRes] = await Promise.all([
|
||||
@@ -67,8 +54,6 @@ try {
|
||||
const i = list.findIndex(p => p.slug === slug);
|
||||
if (i >= 0) {
|
||||
neighbors = {
|
||||
index: i,
|
||||
total: list.length,
|
||||
prev: i > 0 ? list[i - 1] : undefined,
|
||||
next: i < list.length - 1 ? list[i + 1] : undefined,
|
||||
};
|
||||
@@ -82,7 +67,6 @@ try {
|
||||
|
||||
const isAdmin = Astro.cookies.get('admin_session')?.value === '1';
|
||||
const displayTitle = post ? (post.title || formatSlug(post.slug)) : 'Work';
|
||||
const exhibitNumber = neighbors.index >= 0 ? toRoman(neighbors.index + 1) : '';
|
||||
---
|
||||
|
||||
<Layout
|
||||
@@ -123,12 +107,6 @@ const exhibitNumber = neighbors.index >= 0 ? toRoman(neighbors.index + 1) : '';
|
||||
|
||||
{/* Plaque header */}
|
||||
<header class="max-w-3xl mx-auto text-center mb-12 md:mb-16">
|
||||
{exhibitNumber && (
|
||||
<div class="font-display italic text-[var(--mauve)] tracking-[0.3em] text-sm mb-5">
|
||||
№ {exhibitNumber} <span class="text-[var(--subtext0)] not-italic">/ {neighbors.total}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h1 class="font-display italic font-semibold text-[var(--text)] text-4xl md:text-6xl lg:text-7xl leading-[0.95] tracking-tight mb-6">
|
||||
{displayTitle}
|
||||
</h1>
|
||||
|
||||
@@ -553,46 +553,56 @@ code, pre, kbd, samp {
|
||||
filter: saturate(1.05) contrast(1.04);
|
||||
}
|
||||
.plate .plate-caption {
|
||||
padding: 12px 4px 14px 4px;
|
||||
padding: 14px 6px 16px 6px;
|
||||
margin-top: 2px;
|
||||
border-top: 1px solid color-mix(in srgb, var(--surface2) 50%, transparent);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.plate .plate-caption-title {
|
||||
font-family: var(--font-display);
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.2;
|
||||
font-size: 1.18rem;
|
||||
line-height: 1.18;
|
||||
color: var(--text);
|
||||
letter-spacing: -0.005em;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
transition: color 0.25s ease;
|
||||
}
|
||||
.plate:hover .plate-caption-title {
|
||||
color: var(--mauve);
|
||||
}
|
||||
.plate .plate-caption-summary {
|
||||
font-family: var(--font-sans);
|
||||
font-style: italic;
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.4;
|
||||
color: var(--subtext0);
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.plate .plate-caption-meta {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.72rem;
|
||||
font-size: 0.68rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.18em;
|
||||
letter-spacing: 0.22em;
|
||||
color: var(--subtext0);
|
||||
white-space: nowrap;
|
||||
align-self: flex-start;
|
||||
padding-top: 0.35rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
}
|
||||
|
||||
/* The little exhibit number stuck to the corner of a plate */
|
||||
.plate-tag {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 14px;
|
||||
background: var(--mauve);
|
||||
color: var(--rosewater);
|
||||
font-family: var(--font-display);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.18em;
|
||||
padding: 4px 8px;
|
||||
text-transform: uppercase;
|
||||
box-shadow: 0 2px 6px -2px rgba(20, 16, 12, 0.45);
|
||||
.plate .plate-caption-sep {
|
||||
color: var(--mauve);
|
||||
opacity: 0.55;
|
||||
}
|
||||
.plate-tag-mini {
|
||||
position: absolute;
|
||||
|
||||
Reference in New Issue
Block a user