# Pico CSS Styling โ€” Implementation Plan **Goal:** Rewrite `spine-render-index` and `spine-render-empty-state` to emit Pico CSS-styled HTML via CDN, replacing the mockup-A `
` structure with Pico-native semantic elements plus a small inline `\n") (insert "\n\n") (insert "
\n") (insert "
\n") (insert (format "
spine" "%d books ยท %d reading
\n" total reading-count)) ;; Each group in order (dolist (status order) (let ((group-books (nreverse (gethash status grouped)))) (when group-books (insert (format "

%s

%d
\n" status (length group-books))) (dolist (book group-books) (let ((selected (equal (plist-get book :id) selected-id))) (insert (format "
\n" (if selected " spine-selected" ""))) ;; Format glyph (emoji) (let ((fmt (plist-get book :format))) (insert (format "%s\n" (cond ((equal fmt "hardcover") "๐Ÿ“–") ((equal fmt "ebook") "๐Ÿ“ฑ") ((equal fmt "audiobook") "๐ŸŽง") (t ""))))) ;; Title + author (insert "") (insert (spine--h (plist-get book :title))) (let ((author (plist-get book :author))) (when author (insert (format " %s" (spine--h author))))) (insert "\n") ;; Tags (let ((tags (plist-get book :tags))) (when tags (insert (format "%s\n" (mapconcat (lambda (tag) (concat ":" tag ":")) tags " "))))) ;; Recommendation trail (let ((rec-by (plist-get book :rec_by)) (rec-note (plist-get book :rec_note))) (when (and rec-by (> (length rec-by) 0)) (insert (format "%s%s\n" (spine--h rec-by) (if (and rec-note (> (length rec-note) 0)) (concat ": " (spine--h rec-note)) ""))))) ;; Status pill (let ((status (or (plist-get book :status) "WANT"))) (insert (format "%s\n" (downcase status) (downcase status)))) ;; Expanded detail (when selected (insert "
\n") (dolist (note (plist-get book :notes)) (insert (format "

[%s] %s

\n" (spine--h (car note)) (spine--h (cadr note))))) (let ((parts nil)) (let ((rating (plist-get book :rating))) (when (and rating (> (length rating) 0)) (push (format "Rating: %s/5" (spine--h rating)) parts))) (let ((isbn (plist-get book :isbn))) (when (and isbn (> (length isbn) 0)) (push (format "ISBN: %s" (spine--h isbn)) parts))) (let ((fmt (plist-get book :format))) (when (and fmt (> (length fmt) 0)) (push (format "Format: %s" (spine--h fmt)) parts))) (let ((added (plist-get book :added))) (when (and added (> (length added) 0)) (push (format "Added: %s" (spine--h added)) parts))) (when parts (insert (format "
%s
\n" (mapconcat #'identity (nreverse parts) " ยท "))))) (insert "
\n")) (insert "
\n")))))) (insert "
\n") (insert "
\n\n\n") (buffer-string)))) ``` - [ ] **Step 2: Verify batch load** ```bash emacs --quick --batch --eval "(setq spine-skip-server-start t)" --load spine.el ``` Expected: exits cleanly, no errors. - [ ] **Step 3: Verify rendering against sample data** ```bash emacs --quick --batch \ --eval "(setq spine-skip-server-start t spine-org-file \"$(pwd)/sample-books.org\")" \ --load spine.el \ --eval "(progn (message \"render-index length: %d\" (length (spine-render-index (spine-books) nil))))" ``` Expected: non-zero length, no errors. - [ ] **Step 4: Commit** ```bash git add spine.el git commit -m "feat: restyle index page with Pico CSS and emoji glyphs" ``` --- ### Task 3: Smoke test end-to-end **Files:** - No changes โ€” verification only. - [ ] **Step 1: Kill existing server and start fresh** ```bash emacsclient --socket-name=spine --eval '(kill-emacs)' 2>/dev/null pkill -f "emacs --quick" 2>/dev/null sleep 1 emacs --quick \ --eval "(setq spine-org-file \"$(pwd)/sample-books.org\")" \ --load spine.el & EMACS_PID=$! sleep 5 ``` - [ ] **Step 2: Verify Pico CSS is linked** ```bash curl -s http://localhost:8080/index | grep -c "picocss" ``` Expected: `1` (the CDN `` is present). - [ ] **Step 3: Verify no Tabler Icons reference** ```bash curl -s http://localhost:8080/index | grep -c "tabler" ``` Expected: `0`. - [ ] **Step 4: Verify groups in correct order** ```bash curl -s http://localhost:8080/index | grep -oP '

[^<]+' ``` Expected: ``` WANT READING READ ``` No ABANDONED. - [ ] **Step 5: Verify emoji glyphs and status pills** ```bash curl -s http://localhost:8080/index | grep -E "๐Ÿ“–|๐Ÿ“ฑ|๐ŸŽง" | wc -l ``` Expected: `3` (three format glyphs present โ€” Piranesi has no format). ```bash curl -s http://localhost:8080/index | grep -oP '[^<]+' ``` Expected: `want`, `reading`, `read` pills visible. - [ ] **Step 6: Verify expanded detail** ```bash curl -s "http://localhost:8080/index?id=8c1e-uow" | grep -oP 'spine-detail' ``` Expected: `spine-detail` present (2 occurrences โ€” opening and closing). ```bash curl -s "http://localhost:8080/index?id=8c1e-uow" | grep -c "logline\|spine-detail" ``` Expected: detail section renders. - [ ] **Step 7: Verify recommendation trail** ```bash curl -s http://localhost:8080/index | grep "Priya" ``` Expected: output contains `Priya: If you liked Player of Games, this one will wreck you`. - [ ] **Step 8: Verify empty state** ```bash kill $EMACS_PID 2>/dev/null sleep 1 emacs --quick \ --eval "(setq spine-org-file \"/tmp/nonexistent-books.org\")" \ --load spine.el & EMACS_PID=$! sleep 5 curl -s http://localhost:8080/index | grep -c "No books yet" ``` Expected: `1`. - [ ] **Step 9: Stop server** ```bash kill $EMACS_PID 2>/dev/null ``` --- ### Task 4: Run ERT tests and final verification **Files:** - No changes โ€” verification only. - [ ] **Step 1: Run full ERT test suite** ```bash emacs --quick --batch \ --directory "$(pwd)" \ --load test/spine-books-test.el \ -f ert-run-tests-batch-and-exit ``` Expected: all 6 tests pass. - [ ] **Step 2: Verify git status** ```bash git status --short ``` Expected: no modified tracked files (only pre-existing untracked files). - [ ] **Step 3: Review commit log** ```bash git log --oneline -4 ``` Expected: 2-3 new commits on top of previous work.