timconspicuous.neocities.org

Better CSS, slight refactor, added reading widget to index.page.tsx

+238 -45
+3 -3
_config.ts
··· 5 5 6 6 const site = lume({ 7 7 src: "./src", 8 - dest: "./public" 8 + dest: "./public", 9 9 }); 10 10 11 11 site.use(jsx()); 12 12 site.use(postcss()); 13 13 site.add("styles.css"); 14 - site.copy([".jpg", ".svg"]); 14 + site.copy([".jpg", ".svg", ".js"]); 15 15 site.use(simpleIcons()); 16 16 17 - export default site; 17 + export default site;
src/_components/Button.css src/styles/button.css
+11 -10
src/_includes/layout.tsx
··· 42 42 <script 43 43 dangerouslySetInnerHTML={{ 44 44 __html: ` 45 - let theme = localStorage.getItem("theme") || (window.matchMedia("(prefers-color-scheme: dark)").matches 46 - ? "dark" 47 - : "light"); 48 - document.documentElement.dataset.theme = theme; 49 - function changeTheme() { 50 - theme = theme === "dark" ? "light" : "dark"; 51 - localStorage.setItem("theme", theme); 52 - document.documentElement.dataset.theme = theme; 53 - } 54 - `, 45 + let theme = localStorage.getItem("theme") || (window.matchMedia("(prefers-color-scheme: dark)").matches 46 + ? "dark" 47 + : "light"); 48 + document.documentElement.dataset.theme = theme; 49 + function changeTheme() { 50 + theme = theme === "dark" ? "light" : "dark"; 51 + localStorage.setItem("theme", theme); 52 + document.documentElement.dataset.theme = theme; 53 + } 54 + `, 55 55 }} 56 56 /> 57 57 <button ··· 84 84 {footer && ( 85 85 <footer dangerouslySetInnerHTML={{ __html: footer }} /> 86 86 )} 87 + <script src="/scripts/reading-progress.js"></script> 87 88 </body> 88 89 </html> 89 90 );
+16 -4
src/index.page.tsx
··· 1 1 import { marked } from "npm:marked"; 2 2 3 - export const title = "timconspicuous"; 3 + export const title = "tim's neocities page"; 4 4 export const header = { 5 5 title: "timconspicuous", 6 6 description: "", ··· 11 11 { 12 12 type: "bluesky", 13 13 text: "Bluesky", 14 - href: "https://bsky.app/profile/timconspicuous.neocities.org", 14 + href: "https://bsky.app/profile/timtinkers.online", 15 15 }, 16 16 { 17 17 type: "letterboxd", ··· 46 46 ]; 47 47 48 48 export const footer = marked.parseInline( 49 - "Powered by [Lume](https://lume.land)", 49 + "Powered by [Lume](https://lume.land), [source code on Tangled](https://tangled.sh/@timtinkers.online/neocities)", 50 50 ); 51 51 52 52 // Layout to use for this page ··· 54 54 55 55 export default ({ comp }: Lume.Data) => { 56 56 return ( 57 - <comp.Linktree links={links} /> 57 + <div> 58 + <div 59 + id="reading-progress-widget" 60 + data-reading-progress="true" 61 + style={{ 62 + padding: "1rem", 63 + }} 64 + > 65 + </div> 66 + <div> 67 + <comp.Linktree links={links} /> 68 + </div> 69 + </div> 58 70 ); 59 71 };
+30 -26
src/scripts/reading-progress.js
··· 58 58 isbn13: mostRecent.value.identifiers.isbn13, 59 59 progress: mostRecent.value.bookProgress.percent, 60 60 updatedAt: mostRecent.value.bookProgress.updatedAt, 61 + totalPages: mostRecent.value.bookProgress.totalPages, 62 + currentPage: mostRecent.value.bookProgress.currentPage, 61 63 }; 62 64 } 63 65 ··· 82 84 83 85 showLoading() { 84 86 this.container.innerHTML = ` 85 - <div class="reading-progress loading"> 87 + <div class="reading-progress-container reading-progress-loading"> 86 88 <div class="progress-skeleton"> 87 89 <div class="skeleton-text"></div> 88 90 <div class="skeleton-bar"></div> ··· 93 95 94 96 showError(message) { 95 97 this.container.innerHTML = ` 96 - <div class="reading-progress error"> 98 + <div class="reading-progress-container reading-progress-error"> 97 99 <p>📚 Unable to load current reading progress</p> 98 100 <small>${message}</small> 99 101 </div> ··· 111 113 render() { 112 114 if (!this.currentBook) { 113 115 this.container.innerHTML = ` 114 - <div class="reading-progress empty"> 116 + <div class="reading-progress-container reading-progress-empty"> 115 117 <p>📚 No books currently in progress</p> 116 118 </div> 117 119 `; ··· 125 127 : '<div class="book-cover-placeholder">📖</div>'; 126 128 127 129 this.container.innerHTML = ` 128 - <div class="reading-progress"> 129 - <h3>📚 Currently Reading</h3> 130 + <div class="reading-progress-container"> 131 + <div class="reading-progress-header"> 132 + <span>📚</span> Currently Reading 133 + </div> 130 134 <div class="book-info"> 131 - <div class="book-header"> 132 - ${coverImage} 133 - <div class="book-text"> 134 - <div class="book-title"> 135 - ${this.escapeHtml(this.currentBook.title)}</div> 136 - <div class="book-author"> 137 - by ${this.escapeHtml(this.currentBook.author)}</div> 135 + ${coverImage} 136 + <div class="book-details"> 137 + <div class="book-title"> 138 + ${this.escapeHtml(this.currentBook.title)} 138 139 </div> 139 - </div> 140 - <div class="progress-container"> 141 - <div class="progress-bar"> 142 - <div 143 - class="progress-fill" 144 - style="width: ${this.currentBook.progress}%" 145 - ></div> 140 + <div class="book-author"> 141 + by ${this.escapeHtml(this.currentBook.author)} 146 142 </div> 147 - <div class="progress-text"> 148 - ${this.currentBook.progress}% complete 143 + <div class="book-meta"> 144 + <span class="progress-badge">In progress</span> 145 + <span class="last-updated"> 146 + Updated ${this.formatDate(this.currentBook.updatedAt)} 147 + </span> 149 148 </div> 150 149 </div> 151 - <div class="last-updated"> 152 - Updated ${this.formatDate(this.currentBook.updatedAt)} 150 + </div> 151 + <div class="progress-container"> 152 + <div class="progress-bar"> 153 + <div class="progress-fill" style="width: ${this.currentBook.progress}%"></div> 153 154 </div> 154 - </div> 155 + <div class="progress-details"> 156 + <span class="progress-percent">${this.currentBook.progress}%</span> 157 + <span class="progress-pages">${this.currentBook.currentPage} / ${this.currentBook.totalPages} pages</span> 158 + </div> 155 159 </div> 156 - `; 160 + </div> 161 + `; 157 162 } 158 - 159 163 escapeHtml(text) { 160 164 const div = document.createElement("div"); 161 165 div.textContent = text;
+4 -2
src/styles.css
··· 1 1 /* Lume's design system */ 2 2 @import "https://unpkg.com/@lumeland/ds@0.5.2/ds.css"; 3 - @import "./_components/Button.css"; 3 + 4 + @import "./styles/button.css"; 5 + @import "./styles/readingProgress.css"; 4 6 5 7 body { 6 8 display: grid; ··· 38 40 aspect-ratio: 1; 39 41 object-fit: cover; 40 42 object-position: center center; 41 - width: 200px; 43 + width: 150px; 42 44 max-width: 50vw; 43 45 } 44 46
+174
src/styles/readingProgress.css
··· 1 + /* Reading Progress Widget Styles */ 2 + .reading-progress-container { 3 + width: clamp(260px, 90vw, 320px); 4 + border-radius: 12px; 5 + border: 1px solid #3f3f46; 6 + padding: 16px; 7 + background-color: #27272a; 8 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); 9 + margin: 0 auto; 10 + } 11 + 12 + .reading-progress-header { 13 + font-size: 18px; 14 + font-weight: 700; 15 + margin-bottom: 16px; 16 + color: #f4f4f5; 17 + display: flex; 18 + align-items: center; 19 + gap: 8px; 20 + } 21 + 22 + .book-info { 23 + display: flex; 24 + align-items: flex-start; 25 + gap: 12px; 26 + margin-bottom: 16px; 27 + } 28 + 29 + .book-cover { 30 + width: 56px; 31 + height: 84px; 32 + border-radius: 8px; 33 + object-fit: cover; 34 + border: 1px solid #3f3f46; 35 + flex-shrink: 0; 36 + } 37 + 38 + .book-cover-placeholder { 39 + width: 56px; 40 + height: 84px; 41 + border-radius: 8px; 42 + background-color: #3f3f46; 43 + display: flex; 44 + align-items: center; 45 + justify-content: center; 46 + font-size: 24px; 47 + border: 1px solid #52525b; 48 + flex-shrink: 0; 49 + } 50 + 51 + .book-details { 52 + flex: 1; 53 + } 54 + 55 + .book-title { 56 + font-size: 16px; 57 + font-weight: 700; 58 + color: #f4f4f5; 59 + margin-bottom: 4px; 60 + line-height: 1.3; 61 + display: -webkit-box; 62 + -webkit-box-orient: vertical; 63 + overflow: hidden; 64 + } 65 + 66 + .book-author { 67 + font-size: 14px; 68 + color: #a1a1aa; 69 + margin-bottom: 8px; 70 + } 71 + 72 + .book-meta { 73 + display: flex; 74 + align-items: center; 75 + gap: 8px; 76 + flex-wrap: wrap; 77 + } 78 + 79 + .progress-badge { 80 + display: inline-flex; 81 + align-items: center; 82 + border-radius: 9999px; 83 + border: 1px solid rgba(228, 228, 231, 0.9); 84 + padding: 4px 10px; 85 + font-size: 12px; 86 + font-weight: 700; 87 + color: #f4f4f5; 88 + background-color: transparent; 89 + } 90 + 91 + .last-updated { 92 + font-size: 12px; 93 + color: #a1a1aa; 94 + } 95 + 96 + .progress-container { 97 + margin-top: 12px; 98 + } 99 + 100 + .progress-bar { 101 + height: 10px; 102 + overflow: hidden; 103 + border-radius: 9999px; 104 + background-color: #3f3f46; 105 + } 106 + 107 + .progress-fill { 108 + height: 100%; 109 + border-radius: 9999px; 110 + background-color: #10b981; 111 + transition: width 0.5s ease; 112 + } 113 + 114 + .progress-details { 115 + display: flex; 116 + justify-content: space-between; 117 + align-items: baseline; 118 + margin-top: 8px; 119 + } 120 + 121 + .progress-percent { 122 + font-size: 14px; 123 + font-weight: 600; 124 + color: #f4f4f5; 125 + } 126 + 127 + .progress-pages { 128 + font-size: 12px; 129 + color: #a1a1aa; 130 + } 131 + 132 + /* Loading state */ 133 + .reading-progress-loading .progress-skeleton { 134 + padding: 16px; 135 + } 136 + 137 + .skeleton-text { 138 + height: 16px; 139 + background-color: #3f3f46; 140 + border-radius: 4px; 141 + margin-bottom: 12px; 142 + width: 70%; 143 + } 144 + 145 + .skeleton-bar { 146 + height: 10px; 147 + background-color: #3f3f46; 148 + border-radius: 9999px; 149 + width: 100%; 150 + } 151 + 152 + /* Error state */ 153 + .reading-progress-error { 154 + padding: 20px; 155 + text-align: center; 156 + color: #f4f4f5; 157 + } 158 + 159 + .reading-progress-error p { 160 + margin-bottom: 8px; 161 + font-size: 16px; 162 + } 163 + 164 + .reading-progress-error small { 165 + color: #a1a1aa; 166 + font-size: 14px; 167 + } 168 + 169 + /* Empty state */ 170 + .reading-progress-empty { 171 + padding: 20px; 172 + text-align: center; 173 + color: #a1a1aa; 174 + }