serve: dark board chrome with light task cards, fix keyboard nav
Focus target moved from <details> to <summary> so Enter/Space
uses native browser toggle rather than manual JS open/close.

Assisted-by: Claude Opus 4.6 via pi
change lkvxuxmsvotvqrslorzpmvkruluqttpt
commit 363b11465d05deaebeb1c3df89a54f3ba3e04667
author Alpha Chen <alpha@kejadlen.dev>
date
parent nllwlrpk
diff --git a/src/bin/ranger/commands/serve.rs b/src/bin/ranger/commands/serve.rs
index bbc3b1f..3ebd03f 100644
--- a/src/bin/ranger/commands/serve.rs
+++ b/src/bin/ranger/commands/serve.rs
@@ -184,8 +184,8 @@ fn render_task(task: &TaskView) -> Markup {
     let has_details = task.description.is_some() || task.has_subtasks;
     html! {
         @if has_details {
-            details.task tabindex="0" {
-                summary.task-header {
+            details.task {
+                summary.task-header tabindex="0" {
                     span.key {
                         span.key-prefix { (task.key_prefix) }
                         span.key-rest { (task.key_rest) }
@@ -237,29 +237,30 @@ fn keyboard_nav_script() -> Markup {
         script {
             (PreEscaped(r#"
             (function() {
-                function getTasks() {
-                    return Array.from(document.querySelectorAll('.task'));
+                function getFocusables() {
+                    return Array.from(document.querySelectorAll(
+                        'details.task > summary, div.task'
+                    ));
                 }
-                function focusTask(tasks, idx) {
-                    if (idx >= 0 && idx < tasks.length) {
-                        tasks[idx].focus();
-                        tasks[idx].scrollIntoView({ block: 'nearest' });
+                function focusEl(els, idx) {
+                    if (idx >= 0 && idx < els.length) {
+                        els[idx].focus();
+                        els[idx].scrollIntoView({ block: 'nearest' });
                     }
                 }
                 document.addEventListener('keydown', function(e) {
                     if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
-                    var tasks = getTasks();
-                    var current = tasks.indexOf(document.activeElement);
+                    var els = getFocusables();
+                    var current = els.indexOf(document.activeElement);
                     if (e.key === 'j' || e.key === 'ArrowDown') {
                         e.preventDefault();
-                        focusTask(tasks, current < 0 ? 0 : current + 1);
+                        focusEl(els, current < 0 ? 0 : current + 1);
                     } else if (e.key === 'k' || e.key === 'ArrowUp') {
                         e.preventDefault();
-                        focusTask(tasks, current < 0 ? 0 : current - 1);
-                    } else if ((e.key === 'Enter' || e.key === ' ') && document.activeElement.tagName === 'DETAILS') {
+                        focusEl(els, current < 0 ? 0 : current - 1);
+                    } else if ((e.key === 'Enter' || e.key === ' ') && document.activeElement.tagName === 'SUMMARY') {
                         e.preventDefault();
-                        var details = document.activeElement;
-                        details.open = !details.open;
+                        document.activeElement.click();
                     }
                 });
             })();
diff --git a/static/style.css b/static/style.css
index 3eb5b2b..274c0ea 100644
--- a/static/style.css
+++ b/static/style.css
@@ -10,17 +10,17 @@
 
 :root {
   /* ── Color tokens: surfaces ── */
-  --color-bg-header:   #3c3d3f;
-  --color-bg-panel:    #eeeddf;
-  --color-border:      #d5d3c5;
+  --color-bg-header:   #1a1b1e;
+  --color-bg-panel:    #24262a;
+  --color-border:      #3a3c42;
 
   /* ── Color tokens: text ── */
-  --color-text:        #333;
-  --color-text-muted:  #7a7868;
-  --color-text-header: #fff;
+  --color-text:        #d8d8d8;
+  --color-text-muted:  #8a8a8a;
+  --color-text-header: #e0e0e0;
 
   /* ── Color tokens: accent ── */
-  --color-accent:      #3d6098;
+  --color-accent:      #6a9fd8;
 
   /* ── Color tokens: state ── */
   --color-in-progress: #629a3e;
@@ -29,10 +29,10 @@
   --color-icebox:      #8a8878;
 
   /* ── State row tints (12 % opacity) ── */
-  --tint-in-progress:  rgba(98, 154, 62, 0.12);
-  --tint-queued:       rgba(61, 96, 152, 0.12);
-  --tint-done:         rgba(139, 168, 59, 0.12);
-  --tint-icebox:       rgba(138, 136, 120, 0.12);
+  --tint-in-progress:  rgba(210, 170, 30, 0.12);
+  --tint-queued:       rgba(160, 160, 160, 0.12);
+  --tint-done:         rgba(98, 154, 62, 0.24);
+  --tint-icebox:       rgba(61, 96, 152, 0.20);
 
   /* ── Typography ── */
   --font-sans:  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
@@ -46,12 +46,12 @@
 
   /* ── Spacing ── */
   --row-padding-y:  7px;
-  --row-gap:        3px;
+  --row-gap:        0px;
   --panel-padding:  20px;
   --radius:         4px;
 
   /* ── Focus ── */
-  --color-focus: rgba(61, 96, 152, 0.5);
+  --color-focus: rgba(106, 159, 216, 0.5);
 }
 
 
@@ -95,7 +95,7 @@ header h1 .sep {
 
 header .counts {
   font-size: var(--step-mono);
-  color: rgba(255, 255, 255, 0.6);
+  color: rgba(224, 224, 224, 0.5);
 }
 
 
@@ -103,23 +103,24 @@ header .counts {
 .board {
   display: grid;
   grid-template-columns: repeat(3, 1fr);
-  gap: var(--panel-padding);
+  gap: 16px;
   height: calc(100vh - 41px);          /* header height */
-  padding: var(--panel-padding);
+  padding: 16px;
 }
 
 
 /* === Panels === */
 .panel {
   overflow-y: auto;
+  background: rgba(255, 255, 255, 0.05);
+  border-radius: var(--radius);
 }
 
 .panel-header {
   display: flex;
   align-items: center;
   justify-content: space-between;
-  margin-bottom: 12px;
-  padding-bottom: 8px;
+  padding: 8px 12px;
   border-bottom: 1px solid var(--color-border);
 }
 
@@ -165,28 +166,35 @@ header .counts {
 
 /* === Task rows === */
 .task {
-  background: #fff;
-  border: 1px solid var(--color-border);
-  border-radius: var(--radius);
+  background: none;
+  border: 1px solid rgba(0, 0, 0, 0.1);
+  margin-top: -1px;
+  border-radius: 0;
   padding: var(--row-padding-y) 10px;
   margin-bottom: var(--row-gap);
   outline: none;
   transition: box-shadow 0.1s ease;
 }
 
-.task:focus {
+.task:focus-visible,
+details.task > summary:focus-visible {
   box-shadow: 0 0 0 2px var(--color-focus);
   z-index: 1;
   position: relative;
 }
 
-/* details element reset */
-details.task {
-  cursor: pointer;
+.task:focus,
+details.task > summary:focus {
+  outline: none;
 }
 
+/* details element reset */
 details.task > summary {
   list-style: none;
+  cursor: pointer;
+  outline: none;
+  -webkit-user-select: none;
+  user-select: none;
 }
 
 details.task > summary::-webkit-details-marker {
@@ -212,19 +220,19 @@ details.task > summary::marker {
 }
 
 .task .key-prefix {
-  color: var(--color-accent);
+  color: #3d6098;
   font-weight: 700;
 }
 
 .task .key-rest {
-  color: var(--color-text-muted);
+  color: #7a7868;
   font-weight: 400;
 }
 
 .task .title {
   font-size: var(--step-0);
   font-weight: 500;
-  color: var(--color-text);
+  color: #333;
   flex: 1;
   min-width: 0;
 }
@@ -232,7 +240,7 @@ details.task > summary::marker {
 /* === Expand icon === */
 .task .expand-icon {
   font-size: var(--step-mono);
-  color: var(--color-text-muted);
+  color: #7a7868;
   margin-left: auto;
   flex-shrink: 0;
   transition: transform 0.15s ease;
@@ -247,12 +255,12 @@ details.task[open] > summary .expand-icon {
 .task-body {
   padding: 6px 0 2px 0;
   margin-top: 4px;
-  border-top: 1px solid var(--color-border);
+  border-top: 1px solid rgba(0, 0, 0, 0.1);
 }
 
 .task .desc {
   font-size: var(--step-0);
-  color: var(--color-text-muted);
+  color: #666;
   line-height: 1.6;
   white-space: pre-wrap;
   word-break: break-word;
@@ -260,7 +268,7 @@ details.task[open] > summary .expand-icon {
 
 .task .subtask-indicator {
   font-size: var(--step-mono);
-  color: var(--color-text-muted);
+  color: #7a7868;
   margin-top: 4px;
 }
 
@@ -275,7 +283,7 @@ details.task[open] > summary .expand-icon {
 .task .tag {
   font-size: 0.625rem;
   font-family: var(--font-mono);
-  color: var(--color-accent);
+  color: #3d6098;
   background: rgba(61, 96, 152, 0.1);
   padding: 0 5px;
   border-radius: 3px;
@@ -284,11 +292,29 @@ details.task[open] > summary .expand-icon {
 }
 
 
+/* === First/last task rounding === */
+.panel > :last-child .task:last-child,
+.panel > .task:last-child {
+  border-radius: 0 0 var(--radius) var(--radius);
+}
+
+.panel > :nth-child(2) .task:first-child,
+.panel > .task:first-child {
+  border-radius: var(--radius) var(--radius) 0 0;
+  margin-top: 0;
+}
+
+/* When a panel has only one task group with one task */
+.panel > :nth-child(2):last-child .task:only-child,
+.panel > .task:only-child {
+  border-radius: var(--radius);
+}
+
 /* === State-specific row tints === */
-.state-in-progress .task { background: var(--tint-in-progress); }
-.state-queued .task      { background: var(--tint-queued); }
-.state-done .task        { background: var(--tint-done); }
-.state-icebox .task      { background: var(--tint-icebox); }
+.state-in-progress .task { background: #fdf6e3; }
+.state-queued .task      { background: #f5f5f3; }
+.state-done .task        { background: #eef5e6; }
+.state-icebox .task      { background: #e8eef6; }
 
 /* Done tasks are slightly desaturated */
 .state-done .task {