Migrate to popover element and use two columns

Amolith created

Implements: https://todo.sr.ht/~amolith/willow/48

Change summary

ws/static/dashboard.html.tmpl | 236 ++++++++++++++++++++++--------------
ws/static/styles.css          | 108 ++++++++++++----
2 files changed, 224 insertions(+), 120 deletions(-)

Detailed changes

ws/static/dashboard.html.tmpl 🔗

@@ -1,98 +1,154 @@
 {{- template "head" }}
 {{- template "header" .IsDashboard }}
 <main>
-  <div class="wrapper two_column">
-    <div class="projects">
-      <!-- Range through projects that aren't yet up-to-date -->
-      {{- range .Projects -}}
-        {{- if ne .Running (index .Releases 0).Tag -}}
-          <h2>Outdated projects</h2>
-          {{- break -}}
-        {{- end -}}
-      {{- end -}}
-      {{- range .Projects -}}
-        {{- if ne .Running (index .Releases 0).Tag -}}
-          <div id="{{ .ID }}" class="project card">
-            <h3>
-              <a href="{{ .URL }}">{{ .Name }}</a>&nbsp;&nbsp;&nbsp;<span
-                class="delete"
-                ><a href="/new?action=delete&id={{ .ID }}">Delete?</a></span
-              >
-            </h3>
-            <p>
-              You've selected {{ .Running }}.
-              <a
-                href="/new?action=update&url={{ .URL }}&forge={{ .Forge }}&name={{ .Name }}"
-                >Modify?</a
-              >
-            </p>
-            <p>
-              Latest:
-              <a href="{{ (index .Releases 0).URL }}"
-                >{{ (index .Releases 0).Tag }}</a
-              >
-            </p>
-            <p>
-              <a href="#{{ (index .Releases 0).ID }}">View release notes</a>
-            </p>
-          </div>
-        {{- end -}}
-      {{- end -}}
+  <div class="wrapper">
+    {{- $hasOutdated := false }}
+    {{- range .Projects }}
+      {{- if ne .Running (index .Releases 0).Tag }}
+        {{ $hasOutdated = true }}
+      {{ end }}
+    {{ end }}
 
+    {{- if $hasOutdated }}
+      <h2>Outdated projects</h2>
+      <div class="projects-grid outdated">
+        {{- range .Projects }}
+          {{- if ne .Running (index .Releases 0).Tag }}
+            <div id="{{ .ID }}" class="project card">
+              <h3>
+                <a href="{{ .URL }}">{{ .Name }}</a
+                ><span class="delete"
+                  ><a href="/new?action=delete&id={{ .ID }}">Delete?</a></span
+                >
+              </h3>
+              <p>
+                You've selected&nbsp;
+                {{ $project := . }}
+                {{ range .Releases }}
+                  {{ if eq .Tag $project.Running }}
+                    {{ if .URL }}
+                      <a href="{{ .URL }}">{{ $project.Running }}</a>
+                    {{ else }}
+                      {{ $project.Running }}
+                    {{ end }}
+                    {{ break }}
+                  {{ end }}
+                {{ else }}
+                  {{ $project.Running }}
+                {{ end }}.
+                <a
+                  href="/new?action=update&url={{ .URL }}&forge={{ .Forge }}&name={{ .Name }}"
+                  >Modify?</a
+                >
+              </p>
+              <p>
+                Latest:&nbsp;
+                {{ if (index .Releases 0).URL }}
+                  <a href="{{ (index .Releases 0).URL }}"
+                    >{{ (index .Releases 0).Tag }}</a
+                  >
+                {{ else }}
+                  {{ (index .Releases 0).Tag }}
+                {{ end }}
+              </p>
+              <button popovertarget="popover-{{ (index .Releases 0).ID }}">
+                View release notes
+              </button>
+              <div id="popover-{{ (index .Releases 0).ID }}" popover>
+                <div class="popover-content">
+                  <h3>
+                    {{ .Name }}: release notes for
+                    <a href="{{ (index .Releases 0).URL }}"
+                      >{{ (index .Releases 0).Tag }}</a
+                    >
+                  </h3>
+                  {{- if eq .Forge "github" "gitea" "forgejo" }}
+                    {{ (index .Releases 0).Content }}
+                  {{ else }}
+                    <pre>{{ (index .Releases 0).Content }}</pre>
+                  {{ end }}
+                  <button
+                    popovertarget="popover-{{ (index .Releases 0).ID }}"
+                    popovertargetaction="hide"
+                  >
+                    Dismiss
+                  </button>
+                </div>
+              </div>
+            </div>
+          {{ end }}
+        {{ end }}
+      </div>
+    {{ end }}
 
-      <!-- Range through projects that _are_ up-to-date -->
-      {{- range .Projects -}}
-        {{- if eq .Running (index .Releases 0).Tag -}}
-          <h2>Up-to-date projects</h2>
-          {{- break -}}
-        {{- end -}}
-      {{- end -}}
-      {{- range .Projects -}}
-        {{- if eq .Running (index .Releases 0).Tag -}}
-          <div class="project card">
-            <h3>
-              <a href="{{ .URL }}">{{ .Name }}</a>&nbsp;&nbsp;&nbsp;<span
-                class="delete"
-                ><a href="/new?action=delete&id={{ .ID }}">Delete?</a></span
-              >
-            </h3>
-            <p>
-              You've selected
-              <a href="#{{ (index .Releases 0).ID }}">{{ .Running }}</a>.
-              <a
-                href="/new?action=update&url={{ .URL }}&forge={{ .Forge }}&name={{ .Name }}"
-                >Modify?</a
-              >
-            </p>
-          </div>
-        {{- end -}}
-      {{- end -}}
-    </div>
-    <div class="release_notes">
-      <h2>Release notes</h2>
-      {{- range .Projects -}}
-        <div id="{{ (index .Releases 0).ID }}" class="release_note card">
-          <h3>
-            {{ .Name }}: release notes for
-            <a href="{{ (index .Releases 0).URL }}"
-              >{{ (index .Releases 0).Tag }}</a
-            >
-          </h3>
-          {{- if eq .Forge "github" "gitea" "forgejo" -}}
-            {{- (index .Releases 0).Content -}}
-          {{- else -}}
-            <pre>
-                        {{- (index .Releases 0).Content -}}
-                        </pre
-            >
-          {{- end -}}
-          <p>
-            <a class="return_to_project" href="#{{ .ID }}">Back to project</a>
-          </p>
-          <div class="close"><a href="#">&#x2716;</a></div>
-        </div>
-      {{- end -}}
-    </div>
+    {{- $hasUpToDate := false }}
+    {{- range .Projects }}
+      {{- if eq .Running (index .Releases 0).Tag }}
+        {{ $hasUpToDate = true }}
+      {{ end }}
+    {{ end }}
+
+    {{- if $hasUpToDate }}
+      <h2>Up-to-date projects</h2>
+      <div class="projects-grid uptodate">
+        {{- range .Projects }}
+          {{- if eq .Running (index .Releases 0).Tag }}
+            <div class="project card">
+              <h3>
+                <a href="{{ .URL }}">{{ .Name }}</a
+                ><span class="delete"
+                  ><a href="/new?action=delete&id={{ .ID }}">Delete?</a></span
+                >
+              </h3>
+              <p>
+                You've selected&nbsp;
+                {{ $project := . }}
+                {{ range .Releases }}
+                  {{ if eq .Tag $project.Running }}
+                    {{ if .URL }}
+                      <a href="{{ .URL }}">{{ $project.Running }}</a>
+                    {{ else }}
+                      {{ $project.Running }}
+                    {{ end }}
+                    {{ break }}
+                  {{ end }}
+                {{ else }}
+                  {{ $project.Running }}
+                {{ end }}.
+                <a
+                  href="/new?action=update&url={{ .URL }}&forge={{ .Forge }}&name={{ .Name }}"
+                  >Modify?</a
+                >
+              </p>
+              <button popovertarget="popover-{{ (index .Releases 0).ID }}">
+                View release notes
+              </button>
+              <div id="popover-{{ (index .Releases 0).ID }}" popover>
+                <div class="popover-content">
+                  <h3>
+                    {{ .Name }}: release notes for
+                    <a href="{{ (index .Releases 0).URL }}"
+                      >{{ (index .Releases 0).Tag }}</a
+                    >
+                  </h3>
+                  {{- if eq .Forge "github" "gitea" "forgejo" }}
+                    {{ (index .Releases 0).Content }}
+                  {{ else }}
+                    <pre>{{ (index .Releases 0).Content }}</pre>
+                  {{ end }}
+                  <button
+                    popovertarget="popover-{{ (index .Releases 0).ID }}"
+                    popovertargetaction="hide"
+                  >
+                    Dismiss
+                  </button>
+                </div>
+              </div>
+            </div>
+          {{ end }}
+        {{ end }}
+      </div>
+    {{ end }}
   </div>
 </main>
 {{- template "footer" .Version }}

ws/static/styles.css 🔗

@@ -104,29 +104,37 @@ p {
 	margin-block-end: 1rem;
 }
 
-.two_column {
-	display: flex;
-	gap: 20px;
-	flex-direction: row;
+.wrapper h2 {
+  margin: 0 0 1rem 0;
 }
 
-.two_column>*>* {
-	margin: 20px 0;
+.wrapper h2:first-child {
+  margin-top: 0;
 }
 
-.projects,
-.release_notes {
-	flex: 1 1 50%;
+.two_column {
+	display: block;
 }
 
-.release_note.card:not(:target) {
-	display: none;
+.projects-grid {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 1rem;
+  margin-bottom: 4rem;
 }
 
-.release_note.card:target {
-	display: block;
+.project.card {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.projects {
+	width: 100%;
 }
 
+.release_note.card:not(:target),
+.release_note.card:target,
 .return_to_project {
 	display: none;
 }
@@ -238,7 +246,6 @@ header nav a[href="/new"]:visited {
 	border: 1px solid var(--action);
 }
 
-
 footer .wrapper {
 	display: flex;
 	flex-wrap: wrap;
@@ -246,30 +253,71 @@ footer .wrapper {
 	align-content: center;
 }
 
-@media only screen and (max-width: 1000px) {
-	div[id] {
-		display: block;
-	}
+.release-notes-btn {
+	background: none;
+	border: none;
+	color: var(--link);
+	cursor: pointer;
+	text-decoration: underline;
+	padding: 0;
+	font: inherit;
+}
 
-	.two_column {
-		flex-direction: column;
-	}
+[popover] {
+	margin: auto;
+	width: min(800px, 90vw);
+	max-height: 90vh;
+	border: 2px solid var(--card-border);
+	border-radius: 5px;
+	padding: 0;
+	background: var(--card-background);
+	color: var(--text);
+	overflow-y: auto;
+}
 
-	.projects,
-	.release_notes {
-		overflow: visible;
-		flex: 1 1 100%;
-	}
+[popover]:not(:popover-open) {
+	display: none;
+}
 
-	.return_to_project {
-		display: block;
+.popover-content {
+	padding: 20px;
+}
+
+button[popovertarget] {
+	padding: 6px 12px;
+	background-color: var(--card-background);
+	border: 1px solid var(--card-border);
+	border-radius: 4px;
+	color: var(--text);
+	width: fit-content;
+	margin: auto 0;
+}
+
+button[popovertarget]:hover {
+	background-color: var(--card-border);
+}
+
+[popover]::backdrop {
+	background: rgba(0, 0, 0, 0.5);
+	backdrop-filter: blur(3px);
+}
+
+/* Mobile responsiveness */
+@media only screen and (max-width: 1000px) {
+	[popover] {
+		width: 95vw;
 	}
 
-	.close {
-		display: none;
+	div[id] {
+		display: block;
 	}
 }
 
+@media (max-width: 850px) {
+  .projects-grid {
+    grid-template-columns: 1fr;
+  }
+}
 
 /* Forms */
 form {