The social RSS reader. Built on AT Protocol. glean.at
3

Configure Feed

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

refactor: improve responsive design

+25 -25
+1 -1
internal/tmpl/article_detail.html
··· 32 32 {{end}} 33 33 </div> 34 34 35 - <div class="flex items-center gap-2 mt-6"> 35 + <div class="flex items-center gap-2 mt-6 flex-wrap"> 36 36 <button hx-post="/articles/{{.Article.ID}}/like?bordered=true" hx-target="this" hx-swap="outerHTML" 37 37 title="{{if .HasLiked}}Unlike{{else}}Like{{end}}" 38 38 class="group inline-flex items-center gap-1.5 text-[10px] uppercase tracking-button px-2.5 py-1 rounded-pill transition {{if .HasLiked}}text-spot-red bg-spot-red/15 hover:bg-spot-red/25{{else}}text-spot-text bg-spot-hover hover:text-spot-red hover:bg-spot-red/15{{end}}">
+9 -9
internal/tmpl/articles.html
··· 6 6 <div class="flex items-center gap-2 mb-4"> 7 7 <a href="/articles" class="text-xs text-spot-secondary hover:text-spot-text transition">&larr; All articles</a> 8 8 </div> 9 - <div class="flex items-center justify-between"> 10 - <div class="flex items-center gap-3.5"> 9 + <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3"> 10 + <div class="flex items-center gap-3.5 min-w-0"> 11 11 {{template "favicon" dict "src" .Feed.FaviconURL.String "size" "w-10 h-10"}} 12 - <div> 12 + <div class="min-w-0"> 13 13 <h1 class="text-2xl font-bold text-spot-text" style="letter-spacing: -0.02em;">{{if .Feed.SiteURL.Valid}}<a href="{{.Feed.SiteURL.String}}" target="_blank" rel="noopener noreferrer" class="hover:text-spot-green transition">{{end}}{{if .Feed.Title.Valid}}{{.Feed.Title.String}}{{else}}{{.Feed.FeedURL}}{{end}}{{if .Feed.SiteURL.Valid}}</a>{{end}}</h1> 14 14 {{if .Feed.SiteURL.Valid}}<a href="{{.Feed.SiteURL.String}}" target="_blank" rel="noopener noreferrer" class="text-xs text-spot-secondary hover:text-spot-green transition">{{.Feed.SiteURL.String}}</a>{{end}} 15 15 </div> 16 16 </div> 17 - <div class="flex items-center gap-3"> 17 + <div class="flex items-center gap-3 shrink-0"> 18 18 {{if not .IsSubscribed}} 19 19 <form hx-post="/feeds/add" hx-swap="none" hx-on::after-request="if(event.detail.successful) window.location.reload()"> 20 20 {{csrfInput .CSRFToken}} ··· 33 33 {{if .Feed.Description.Valid}}<p class="text-sm text-spot-secondary mt-3 leading-relaxed">{{.Feed.Description.String}}</p>{{end}} 34 34 </div> 35 35 36 - <div class="flex items-center gap-3 mb-6 flex-wrap"> 37 - <form method="GET" action="/articles" class="relative flex-1 min-w-[200px]"> 36 + <div class="flex flex-col sm:flex-row sm:items-center gap-3 mb-6"> 37 + <form method="GET" action="/articles" class="relative w-full sm:flex-1 sm:min-w-[200px]"> 38 38 <input type="text" name="q" value="{{.SearchQuery}}" placeholder="Search articles..." 39 39 class="w-full bg-spot-hover text-spot-text rounded-pill pl-10 pr-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-spot-green placeholder:text-spot-secondary" 40 40 hx-get="/articles" hx-trigger="keyup changed delay:300ms, search" hx-target="#article-list" hx-swap="innerHTML" hx-include="[name='q']"> ··· 96 96 </div> 97 97 <p class="text-sm text-spot-secondary mb-6">All your subscribed articles, {{if .SortOldest}}oldest first{{else}}newest first{{end}}.</p> 98 98 99 - <div class="flex items-center gap-3 mb-6 flex-wrap"> 100 - <div class="flex items-center gap-1.5"> 99 + <div class="flex flex-col sm:flex-row sm:items-center gap-3 mb-6"> 100 + <div class="flex items-center gap-1.5 flex-wrap"> 101 101 <a href="/articles?{{if not .SortOldest}}sort=oldest{{end}}{{if .Status}}&status={{.Status}}{{end}}" 102 102 class="px-2.5 py-1.5 rounded-pill text-xs transition 103 103 {{if .SortOldest}}bg-spot-active-pill-bg text-spot-active-pill-text font-bold{{else}}bg-spot-hover text-spot-secondary hover:text-spot-text{{end}}" ··· 113 113 <a href="/articles?{{if .Status}}status={{.Status}}&{{end}}category=__none__{{if .SortOldest}}&sort=oldest{{end}}" class="text-sm px-3 py-1 rounded-pill font-bold {{if eq .Category "__none__"}}bg-spot-active-pill-bg text-spot-active-pill-text{{else}}bg-spot-hover text-spot-secondary hover:text-spot-text{{end}} transition">Uncategorized</a> 114 114 {{end}} 115 115 </div> 116 - <form method="GET" action="/articles" class="relative flex-1 min-w-[180px]"> 116 + <form method="GET" action="/articles" class="relative w-full sm:flex-1 sm:min-w-[180px]"> 117 117 <input type="text" name="q" value="{{.SearchQuery}}" placeholder="Search articles..." 118 118 class="w-full bg-spot-hover text-spot-text rounded-pill pl-8 pr-3 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-spot-green placeholder:text-spot-secondary" 119 119 hx-get="/articles" hx-trigger="keyup changed delay:300ms, search" hx-target="#article-list" hx-swap="innerHTML" hx-include="[name='q']">
+1 -1
internal/tmpl/dashboard.html
··· 1 1 {{define "dashboard.html"}} 2 - <div class="flex items-center justify-between mb-2"> 2 + <div class="flex items-center justify-between mb-2 flex-wrap gap-2"> 3 3 <h1 class="text-2xl font-bold text-spot-text" style="letter-spacing: -0.02em;">Dashboard</h1> 4 4 {{if gt .SubscriptionCount 0}} 5 5 <div class="flex gap-4 text-sm">
+1 -1
internal/tmpl/feeds.html
··· 1 1 {{define "feeds.html"}} 2 2 <div class="flex items-center justify-between gap-3 mb-2 flex-wrap"> 3 3 <h1 class="text-2xl font-bold text-spot-text" style="letter-spacing: -0.02em;">Feeds <span class="text-base font-normal text-spot-secondary">({{.SubscriptionCount}})</span></h1> 4 - <div class="flex items-center gap-3"> 4 + <div class="flex items-center gap-3 flex-wrap"> 5 5 <button id="refresh-btn" 6 6 hx-post="/feeds/refresh" hx-target="#feed-list" hx-swap="innerHTML" 7 7 hx-on::before-request="gleanRefreshStart()"
+1 -1
internal/tmpl/partials/annotation-card.html
··· 1 1 {{define "annotation-card.html"}} 2 - <div id="annotation-{{.annotation.ID}}" class="bg-spot-surface rounded-xl p-5 border border-spot-divider"> 2 + <div id="annotation-{{.annotation.ID}}" class="bg-spot-surface rounded-xl p-4 sm:p-5 border border-spot-divider"> 3 3 {{if .annotation.ArticleURL}} 4 4 <div class="text-xs mb-3 flex items-center gap-2"> 5 5 {{if .annotation.ArticleID.Valid}}<a href="/articles/{{.annotation.ArticleID.Int64}}" class="text-spot-green hover:text-spot-text transition truncate inline-flex items-center gap-1">
+1 -1
internal/tmpl/partials/article-card-expanded.html
··· 1 1 {{define "article-card-expanded.html"}} 2 - <article data-article-id="{{.ID}}" data-feed-url="{{.FeedURL}}" data-article-url="{{if .URL.Valid}}{{.URL.String}}{{end}}" data-expanded="true" class="bg-spot-surface rounded-xl px-5 py-4 shadow-spot relative {{if not .IsRead.Bool}}border-l border-spot-green/80{{end}}"> 2 + <article data-article-id="{{.ID}}" data-feed-url="{{.FeedURL}}" data-article-url="{{if .URL.Valid}}{{.URL.String}}{{end}}" data-expanded="true" class="bg-spot-surface rounded-xl px-4 sm:px-5 py-4 shadow-spot relative {{if not .IsRead.Bool}}border-l border-spot-green/80{{end}}"> 3 3 <div class="flex items-start justify-between gap-4"> 4 4 <div class="min-w-0 flex-1"> 5 5 <div class="flex items-start gap-2.5">
+3 -3
internal/tmpl/partials/article-card.html
··· 1 1 {{define "article-card.html"}} 2 - <article data-article-id="{{.ID}}" data-feed-url="{{.FeedURL}}" data-article-url="{{if .URL.Valid}}{{.URL.String}}{{end}}" class="bg-spot-surface rounded-xl px-5 py-4 hover:bg-spot-hover-50 transition shadow-spot relative group {{if not .IsRead.Bool}}border-l border-spot-green/80{{end}}"> 2 + <article data-article-id="{{.ID}}" data-feed-url="{{.FeedURL}}" data-article-url="{{if .URL.Valid}}{{.URL.String}}{{end}}" class="bg-spot-surface rounded-xl px-4 sm:px-5 py-4 hover:bg-spot-hover-50 transition shadow-spot relative group {{if not .IsRead.Bool}}border-l border-spot-green/80{{end}}"> 3 3 <div class="flex items-start justify-between gap-4"> 4 4 <div class="min-w-0 flex-1"> 5 5 <div class="flex items-start gap-2.5"> ··· 17 17 </div> 18 18 <div class="grid grid-cols-1 gap-2 shrink-0 pt-0.5"> 19 19 {{template "like-button.html" .}} 20 - <button id="read-btn-{{.ID}}" hx-post="/articles/{{.ID}}/{{if .IsRead.Bool}}unread{{else}}read{{end}}" hx-target="#read-btn-{{.ID}}" hx-swap="outerHTML" title="{{if .IsRead.Bool}}Mark as unread{{else}}Mark as read{{end}}" class="group inline-flex items-center justify-center gap-1 text-[10px] text-spot-text hover:text-spot-green hover:bg-spot-green/10 hover:border-spot-green/20 uppercase tracking-button px-2 py-0.5 rounded-pill bg-spot-surface border border-spot-divider transition w-full"> 20 + <button id="read-btn-{{.ID}}" hx-post="/articles/{{.ID}}/{{if .IsRead.Bool}}unread{{else}}read{{end}}" hx-target="#read-btn-{{.ID}}" hx-swap="outerHTML" title="{{if .IsRead.Bool}}Mark as unread{{else}}Mark as read{{end}}" class="group inline-flex items-center justify-center gap-1 text-[10px] text-spot-text hover:text-spot-green hover:bg-spot-green/10 hover:border-spot-green/20 uppercase tracking-button px-2 py-0.5 rounded-pill bg-spot-surface border border-spot-divider transition"> 21 21 <svg class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg> 22 22 <span>{{if .IsRead.Bool}}Unread{{else}}Read{{end}}</span> 23 23 </button> 24 24 {{if .DismissURL}} 25 25 <button hx-post="{{.DismissURL}}" hx-target="closest article" hx-swap="delete" hx-vals='{"{{.DismissField}}": "{{.DismissValue}}"}' title="Remove from recommendations" 26 - class="group inline-flex items-center justify-center gap-1 text-[10px] text-spot-text hover:text-spot-red hover:bg-spot-red/10 hover:border-spot-red/20 uppercase tracking-button px-2 py-0.5 rounded-pill bg-spot-surface border border-spot-divider transition w-full"> 26 + class="group inline-flex items-center justify-center gap-1 text-[10px] text-spot-text hover:text-spot-red hover:bg-spot-red/10 hover:border-spot-red/20 uppercase tracking-button px-2 py-0.5 rounded-pill bg-spot-surface border border-spot-divider transition"> 27 27 <svg class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg> 28 28 <span>Hide</span> 29 29 </button>
+3 -3
internal/tmpl/partials/feed-item.html
··· 1 1 {{define "feed-item.html"}} 2 2 <div id="feed-{{.ID}}" class="contents"> 3 - <div class="feed-item px-5 py-4 flex items-center justify-between gap-3 hover:bg-spot-hover-50 transition"> 3 + <div class="feed-item px-4 sm:px-5 py-4 flex items-center justify-between gap-3 hover:bg-spot-hover-50 transition"> 4 4 <a href="/articles?feed={{.FeedURL}}" class="min-w-0 flex-1 flex items-center gap-3"> 5 5 {{template "favicon" dict "src" .FaviconURL.String "size" "w-5 h-5"}} 6 6 <div class="min-w-0 flex-1"> ··· 29 29 </form> 30 30 </div> 31 31 </div> 32 - <div id="feed-edit-{{.ID}}" class="px-5 py-4 bg-spot-hover/50 hidden"> 32 + <div id="feed-edit-{{.ID}}" class="px-4 sm:px-5 py-4 bg-spot-hover/50 hidden"> 33 33 <form hx-post="/feeds/edit" hx-target="#feed-{{.ID}}" hx-swap="outerHTML" 34 - class="flex items-center gap-2"> 34 + class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2"> 35 35 {{csrfInput .CSRFToken}} 36 36 <input type="hidden" name="feed_url" value="{{.FeedURL}}"> 37 37 <input type="text" name="category" value="{{if .Category.Valid}}{{.Category.String}}{{end}}"
+1 -1
internal/tmpl/partials/trending-card.html
··· 1 1 {{define "trending-card.html"}} 2 - <div class="bg-spot-surface rounded-xl px-5 py-4 hover:bg-spot-hover-50 transition shadow-spot"> 2 + <div class="bg-spot-surface rounded-xl px-4 sm:px-5 py-4 hover:bg-spot-hover-50 transition shadow-spot"> 3 3 <div class="flex items-start justify-between gap-4"> 4 4 <div class="min-w-0 flex-1"> 5 5 <a href="/articles/{{.ArticleID}}" class="font-semibold text-spot-text hover:text-spot-green transition text-[17px] leading-snug">{{.Title}}</a>
+3 -3
internal/tmpl/profile.html
··· 1 1 {{define "profile.html"}} 2 2 <div class="max-w-2xl mx-auto"> 3 - <div class="bg-spot-surface rounded-2xl p-6 mb-6"> 4 - <div class="flex items-center gap-5"> 3 + <div class="bg-spot-surface rounded-2xl p-4 sm:p-6 mb-6"> 4 + <div class="flex flex-col sm:flex-row items-center sm:items-start gap-4 sm:gap-5 text-center sm:text-left"> 5 5 {{if .ProfileUser.AvatarURL}}<img src="{{.ProfileUser.AvatarURL}}" class="w-20 h-20 rounded-full ring-2 ring-spot-divider">{{else}}<div class="w-20 h-20 rounded-full bg-spot-hover flex items-center justify-center text-spot-muted"><svg class="w-10 h-10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 4-7 8-7s8 3 8 7"/></svg></div>{{end}} 6 6 <div class="min-w-0 flex-1"> 7 7 <h1 class="text-2xl font-bold text-spot-text truncate" style="letter-spacing: -0.02em;"> 8 8 {{if .ProfileUser.DisplayName}}{{.ProfileUser.DisplayName}}{{end}} 9 9 </h1> 10 10 <p class="flex items-center gap-1.5 text-spot-secondary mt-1">@{{.ProfileUser.Handle}}<a href="https://bsky.app/profile/{{.ProfileUser.Handle}}" target="_blank" rel="noopener" class="text-spot-secondary hover:text-spot-green transition"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">{{template "icon-bluesky"}}</svg></a></p> 11 - <div class="flex gap-4 mt-3"> 11 + <div class="flex gap-4 mt-3 justify-center sm:justify-start"> 12 12 <a href="/feeds" class="group"> 13 13 <span class="text-xl font-bold text-spot-text">{{.SubscriptionCount}}</span> 14 14 <span class="text-sm text-spot-secondary ml-1 group-hover:text-spot-text transition">feeds</span>
+1 -1
internal/tmpl/stats.html
··· 16 16 </div> 17 17 <div class="divide-y divide-spot-divider"> 18 18 {{range $metrics}} 19 - <div class="px-5 py-3.5 flex items-start justify-between hover:bg-spot-hover transition"> 19 + <div class="px-4 sm:px-5 py-3.5 flex flex-col sm:flex-row sm:items-start justify-between hover:bg-spot-hover transition gap-1 sm:gap-0"> 20 20 <div class="flex-1 min-w-0"> 21 21 <div class="flex items-center gap-2"> 22 22 <span class="text-sm font-medium text-spot-text">{{.Name}}</span>