my website at ewancroft.uk
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

rewrite ArchiveHeader to be Tailwind and align YearTabs with it.

Ewan Croft f6202117 d821c654

+36 -189
+31 -184
src/lib/components/archive/ArchiveHeader.svelte
··· 1 1 <script lang="ts"> 2 - import { fly } from "svelte/transition"; 3 - import { quintOut } from "svelte/easing"; 4 2 import { formatNumber } from "$utils/formatters"; 5 3 import { calculateTotalReadTime, calculateTotalWordCount, formatReadTime } from "$utils/tally"; 6 4 import DocumentIcon from "$components/icons/utility/DocumentIcon.svelte"; 7 5 8 6 export let groupedByYear: any[]; 9 7 10 - // Calculate total posts across all years 11 8 $: totalPosts = groupedByYear.reduce((total, yearGroup) => { 12 9 return total + Object.values(yearGroup.months).reduce((yearTotal: number, postsInMonth) => { 13 10 return yearTotal + (postsInMonth as any[]).length; 14 11 }, 0); 15 12 }, 0); 16 13 17 - // Calculate total stats across all posts 18 - $: allPosts = groupedByYear.flatMap(yearGroup => 14 + $: allPosts = groupedByYear.flatMap(yearGroup => 19 15 Object.values(yearGroup.months).flatMap((postsInMonth) => postsInMonth as any[]) 20 16 ); 21 - 17 + 22 18 $: rawTotalReadTime = calculateTotalReadTime(allPosts); 23 19 $: totalReadTime = formatReadTime(rawTotalReadTime); 24 20 $: totalWordCount = calculateTotalWordCount(allPosts); 25 21 26 - // Labels for singular/plural 27 22 $: postLabel = totalPosts === 1 ? "post" : "posts"; 28 23 $: wordLabel = totalWordCount === 1 ? "word" : "words"; 29 24 </script> 30 25 31 26 <header 32 - class="archive-header" 33 - in:fly={{ y: -20, duration: 400, delay: 0, easing: quintOut }} 27 + class="mb-2 pl-4" 34 28 > 35 - <div class="header-content"> 36 - <div class="title-section"> 37 - <div class="icon-wrapper"> 38 - <DocumentIcon size="24" /> 39 - </div> 40 - <h1 class="archive-title">Blog Archive</h1> 41 - </div> 29 + <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6 pb-4"> 42 30 43 - <div class="stats-section"> 44 - <div class="primary-stat"> 45 - <span class="stat-number">{formatNumber(totalPosts)}</span> 46 - <span class="stat-label">{postLabel}</span> 31 + <!-- Left: Title & Subtitle --> 32 + <div class="flex items-start gap-4"> 33 + <div class="flex items-center justify-center w-14 h-14 bg-button text-text rounded-2xl shadow-sm"> 34 + <DocumentIcon size="28" /> 47 35 </div> 48 - 49 - <div class="divider">•</div> 50 - 51 - <div class="secondary-stats"> 52 - <span class="stat-item"> 53 - <span class="stat-value">{totalReadTime}</span> 54 - <span class="stat-sub-label">read time</span> 55 - </span> 56 - <span class="stat-item"> 57 - <span class="stat-value">{formatNumber(totalWordCount)}</span> 58 - <span class="stat-sub-label">{wordLabel}</span> 59 - </span> 36 + <div> 37 + <h1 class="text-3xl font-extrabold text-primary leading-tight tracking-tight">Blog Archive</h1> 38 + <p class="text-sm text-text/80 mt-1">Unapologetically me</p> 60 39 </div> 61 40 </div> 62 - </div> 63 - </header> 64 41 65 - <style> 66 - .archive-header { 67 - margin-bottom: 16px; 68 - padding: 0 16px; 69 - } 70 - 71 - .header-content { 72 - display: flex; 73 - align-items: center; 74 - justify-content: space-between; 75 - gap: 24px; 76 - padding-bottom: 16px; 77 - } 78 - 79 - .title-section { 80 - display: flex; 81 - align-items: center; 82 - gap: 12px; 83 - } 84 - 85 - .icon-wrapper { 86 - display: flex; 87 - align-items: center; 88 - justify-content: center; 89 - width: 48px; 90 - height: 48px; 91 - background: var(--button-bg); 92 - border-radius: 12px; 93 - color: var(--text-color); 94 - transition: all 0.3s ease; 95 - } 42 + <!-- Right: Stats --> 43 + <div class="flex items-center gap-8"> 44 + <div class="flex flex-col items-center"> 45 + <span class="text-2xl font-bold text-primary">{formatNumber(totalPosts)}</span> 46 + <span class="text-xs text-text/70 uppercase tracking-wide">{postLabel}</span> 47 + </div> 96 48 97 - .archive-title { 98 - font-size: 2rem; 99 - font-weight: 700; 100 - color: var(--link-color); 101 - margin: 0; 102 - letter-spacing: -0.02em; 103 - } 49 + <div class="w-px h-8 bg-text/20"></div> 104 50 105 - .stats-section { 106 - display: flex; 107 - align-items: center; 108 - gap: 16px; 109 - font-size: 0.95rem; 110 - } 111 - 112 - .primary-stat { 113 - display: flex; 114 - align-items: baseline; 115 - gap: 6px; 116 - } 117 - 118 - .stat-number { 119 - font-size: 1.5rem; 120 - font-weight: 700; 121 - color: var(--link-color); 122 - line-height: 1; 123 - } 124 - 125 - .stat-label { 126 - font-weight: 500; 127 - color: var(--text-color); 128 - opacity: 0.8; 129 - } 130 - 131 - .divider { 132 - color: var(--text-color); 133 - opacity: 0.4; 134 - font-weight: bold; 135 - } 136 - 137 - .secondary-stats { 138 - display: flex; 139 - flex-direction: column; 140 - gap: 2px; 141 - font-size: 0.85rem; 142 - } 143 - 144 - .stat-item { 145 - display: flex; 146 - align-items: baseline; 147 - gap: 4px; 148 - } 149 - 150 - .stat-value { 151 - font-weight: 600; 152 - color: var(--text-color); 153 - } 154 - 155 - .stat-sub-label { 156 - font-weight: 400; 157 - color: var(--text-color); 158 - opacity: 0.7; 159 - } 160 - 161 - /* Responsive adjustments */ 162 - @media (max-width: 768px) { 163 - .header-content { 164 - flex-direction: column; 165 - align-items: flex-start; 166 - gap: 16px; 167 - } 168 - 169 - .archive-title { 170 - font-size: 1.75rem; 171 - } 172 - 173 - .stats-section { 174 - align-self: stretch; 175 - justify-content: space-between; 176 - } 177 - 178 - .secondary-stats { 179 - flex-direction: row; 180 - gap: 12px; 181 - } 182 - 183 - .stat-item { 184 - flex-direction: column; 185 - gap: 0; 186 - text-align: right; 187 - } 188 - } 189 - 190 - @media (max-width: 640px) { 191 - .archive-header { 192 - padding: 0 8px; 193 - } 194 - 195 - .title-section { 196 - gap: 8px; 197 - } 198 - 199 - .icon-wrapper { 200 - width: 40px; 201 - height: 40px; 202 - } 203 - 204 - .archive-title { 205 - font-size: 1.5rem; 206 - } 207 - 208 - .stat-number { 209 - font-size: 1.25rem; 210 - } 211 - 212 - .secondary-stats { 213 - font-size: 0.8rem; 214 - } 215 - } 216 - </style> 51 + <div class="flex gap-6"> 52 + <div class="flex flex-col items-center"> 53 + <span class="text-lg font-semibold text-text">{totalReadTime}</span> 54 + <span class="text-xs text-text/70 uppercase tracking-wide">Read Time</span> 55 + </div> 56 + <div class="flex flex-col items-center"> 57 + <span class="text-lg font-semibold text-text">{formatNumber(totalWordCount)}</span> 58 + <span class="text-xs text-text/70 uppercase tracking-wide">{wordLabel}</span> 59 + </div> 60 + </div> 61 + </div> 62 + </div> 63 + </header>
+5 -5
src/lib/components/archive/YearTabs.svelte
··· 12 12 </script> 13 13 14 14 <div 15 - class="flex mb-6 ml-4 border-b border-[var(--button-bg)] overflow-x-auto relative tabs-container" 15 + class="flex mb-6 pl-4 border-b border-button overflow-x-auto relative tabs-container" 16 16 > 17 17 <div class="tab-indicator-container absolute bottom-0 left-0 h-0.5 w-full"> 18 18 <div 19 - class="tab-indicator bg-[var(--link-color)] h-full absolute bottom-0 transition-all duration-300 ease-out" 19 + class="tab-indicator bg-primary h-full absolute bottom-0 transition-all duration-300 ease-out" 20 20 style="left: {indicatorLeft}px; width: 100px;" 21 21 ></div> 22 22 </div> ··· 25 25 <button 26 26 class="w-[100px] min-w-[100px] px-4 py-2 font-medium transition-all duration-300 relative z-10 text-center 27 27 {activeYear === year 28 - ? 'text-[var(--link-color)]' 29 - : 'text-[var(--text-color)] opacity-80 hover:text-[var(--link-hover-color)]'}" 28 + ? 'text-primary' 29 + : 'text-text opacity-80 hover:text-primary-hover'}" 30 30 onclick={() => setActiveYear(year)} 31 31 > 32 32 <span 33 - class="relative {activeYear === year 33 + class="{activeYear === year 34 34 ? 'transform transition-transform duration-300 scale-105' 35 35 : ''}" 36 36 >