fix(ui): right slide-out sheet for oversized modals on phones; 360px fixes

Auditing at 360x760 (Firefox's smallest emulated device; Galaxy S25
class) — 390 was too generous a baseline — the Customize Dashboard
pop-up was the worst page in the app: its card measured 986px in a
760px viewport with no internal scroll, so Cancel/Save sat ~90px below
the fold on a fixed, non-scrolling overlay — unreachable.

New reusable shell: .app-modal-panel (input.css) renders a centered
dialog on desktop (>=640px, unchanged) and a full-height right
slide-out sheet on phones: h-dvh, rounded-l-2xl, slide-in animation
(replays on every open because the overlay toggles display), internal
scroll, overscroll containment. Overlay pairs with items-stretch /
justify-end below sm.

Converted the measured overflows:
- Customize Dashboard (986px): sheet + sticky header/footer, rows wrap
  with the Restore/toggle group right-aligned (ml-auto), titles
  truncate, System badge shrink-0.
- Collection create/edit (796px, cut at both ends at 360): sheet +
  sticky header/footer; edit modal shared the file.

Also at 360: admin-library's Archived buttons row (View Archived +
Purge Archived Now, 315px at x=49) inflated the page to 364px — now
flex-wrap. Collection-episodes shows a 1px sub-pixel rounding artifact
(vw=361) — left as-is, invisible.

Verified at 360x760: sheet geometry (footer bottom 739 < 760), rows
fit, desktop 1280 screenshot unchanged; full-route sweep at 360 shows
no page-level horizontal overflow beyond the two notes above.
This commit is contained in:
John O'Keefe committed 2026-11-02 10:55:23 -05:00
1 parent 3e8ac3b9b7
commit 04e855e691
7 files changed
+62 -31

No files matched your search

+1 -1
View File
@@ -49,7 +49,7 @@ templ AdminLibrary(user User, libraries []LibraryData, users []User, archivedCou
unless purged; items return automatically if their files come back. unless purged; items return automatically if their files come back.
</p> </p>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<a href="/admin/archived" class="btn btn-secondary">View Archived</a> <a href="/admin/archived" class="btn btn-secondary">View Archived</a>
<button type="button" @click="purgeArchivedItems()" class="btn btn-secondary"> <button type="button" @click="purgeArchivedItems()" class="btn btn-secondary">
@Icon("trash", "h-4 w-4") @Icon("trash", "h-4 w-4")
+1 -1
View File
@@ -95,7 +95,7 @@ func AdminLibrary(user User, libraries []LibraryData, users []User, archivedCoun
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</a></p><p class=\"text-sm\" style=\"color: var(--text-secondary)\">Files missing from disk for two consecutive scans. Reading history is kept unless purged; items return automatically if their files come back.</p></div><div class=\"flex items-center gap-2\"><a href=\"/admin/archived\" class=\"btn btn-secondary\">View Archived</a> <button type=\"button\" @click=\"purgeArchivedItems()\" class=\"btn btn-secondary\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</a></p><p class=\"text-sm\" style=\"color: var(--text-secondary)\">Files missing from disk for two consecutive scans. Reading history is kept unless purged; items return automatically if their files come back.</p></div><div class=\"flex flex-wrap items-center gap-2\"><a href=\"/admin/archived\" class=\"btn btn-secondary\">View Archived</a> <button type=\"button\" @click=\"purgeArchivedItems()\" class=\"btn btn-secondary\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
+5 -5
View File
@@ -1,9 +1,9 @@
package templates package templates
templ CollectionModal(collection CollectionData) { templ CollectionModal(collection CollectionData) {
<div x-data="collections" class="fixed inset-0 z-[65] flex items-center justify-center p-4" style="background-color: var(--surface-overlay);"> <div x-data="collections" class="fixed inset-0 z-[65] flex items-stretch justify-end sm:items-center sm:justify-center sm:p-4" style="background-color: var(--surface-overlay);">
<div class="card p-6 w-full max-w-md" style="box-shadow: var(--shadow-pop);"> <div class="app-modal-panel card p-5 sm:p-6 sm:max-w-md">
<div class="flex justify-between items-center mb-6"> <div class="sticky top-0 z-10 flex justify-between items-center -mx-5 px-5 pb-3 mb-5 border-b sm:-mx-6 sm:px-6" style="background-color: var(--bg-secondary); border-color: var(--border);">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="grid place-items-center h-10 w-10 rounded-xl shrink-0" style="background-color: var(--accent-muted); color: var(--accent);"> <span class="grid place-items-center h-10 w-10 rounded-xl shrink-0" style="background-color: var(--accent-muted); color: var(--accent);">
@Icon("folder", "h-5 w-5") @Icon("folder", "h-5 w-5")
@@ -85,7 +85,7 @@ templ CollectionModal(collection CollectionData) {
</label> </label>
</div> </div>
</div> </div>
<div class="flex justify-end space-x-3"> <div class="sticky bottom-0 z-10 flex justify-end space-x-3 -mx-5 px-5 py-3 border-t sm:-mx-6 sm:px-6" style="background-color: var(--bg-secondary); border-color: var(--border);">
<button type="button" @click="closeCollectionModal($el)" class="btn btn-secondary">Cancel</button> <button type="button" @click="closeCollectionModal($el)" class="btn btn-secondary">Cancel</button>
<button type="submit" class="btn btn-primary">Update Collection</button> <button type="submit" class="btn btn-primary">Update Collection</button>
</div> </div>
@@ -185,7 +185,7 @@ templ CollectionModal(collection CollectionData) {
</div> </div>
<input type="hidden" name="color" id="collection-color" value="blue"/> <input type="hidden" name="color" id="collection-color" value="blue"/>
</div> </div>
<div class="flex justify-end space-x-3"> <div class="sticky bottom-0 z-10 flex justify-end space-x-3 -mx-5 px-5 py-3 border-t sm:-mx-6 sm:px-6" style="background-color: var(--bg-secondary); border-color: var(--border);">
<button type="button" @click="closeCollectionModal($el)" class="btn btn-secondary">Cancel</button> <button type="button" @click="closeCollectionModal($el)" class="btn btn-secondary">Cancel</button>
<button type="submit" class="btn btn-primary">Create Collection</button> <button type="submit" class="btn btn-primary">Create Collection</button>
</div> </div>
+3 -3
View File
@@ -29,7 +29,7 @@ func CollectionModal(collection CollectionData) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent templ_7745c5c3_Var1 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div x-data=\"collections\" class=\"fixed inset-0 z-[65] flex items-center justify-center p-4\" style=\"background-color: var(--surface-overlay);\"><div class=\"card p-6 w-full max-w-md\" style=\"box-shadow: var(--shadow-pop);\"><div class=\"flex justify-between items-center mb-6\"><div class=\"flex items-center gap-3\"><span class=\"grid place-items-center h-10 w-10 rounded-xl shrink-0\" style=\"background-color: var(--accent-muted); color: var(--accent);\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div x-data=\"collections\" class=\"fixed inset-0 z-[65] flex items-stretch justify-end sm:items-center sm:justify-center sm:p-4\" style=\"background-color: var(--surface-overlay);\"><div class=\"app-modal-panel card p-5 sm:p-6 sm:max-w-md\"><div class=\"sticky top-0 z-10 flex justify-between items-center -mx-5 px-5 pb-3 mb-5 border-b sm:-mx-6 sm:px-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex items-center gap-3\"><span class=\"grid place-items-center h-10 w-10 rounded-xl shrink-0\" style=\"background-color: var(--accent-muted); color: var(--accent);\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -163,7 +163,7 @@ func CollectionModal(collection CollectionData) templ.Component {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " class=\"h-4 w-4 accent-current\"> <span>Show Up Next strip</span></label></div></div><div class=\"flex justify-end space-x-3\"><button type=\"button\" @click=\"closeCollectionModal($el)\" class=\"btn btn-secondary\">Cancel</button> <button type=\"submit\" class=\"btn btn-primary\">Update Collection</button></div></form>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " class=\"h-4 w-4 accent-current\"> <span>Show Up Next strip</span></label></div></div><div class=\"sticky bottom-0 z-10 flex justify-end space-x-3 -mx-5 px-5 py-3 border-t sm:-mx-6 sm:px-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><button type=\"button\" @click=\"closeCollectionModal($el)\" class=\"btn btn-secondary\">Cancel</button> <button type=\"submit\" class=\"btn btn-primary\">Update Collection</button></div></form>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -181,7 +181,7 @@ func CollectionModal(collection CollectionData) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\"><div id=\"icon-grid\" class=\"grid grid-cols-8 gap-1 max-h-32 overflow-y-auto p-2 rounded-lg border\" style=\"background-color: var(--bg-primary); border-color: var(--border);\"></div></div><div class=\"mb-6\"><label class=\"block text-xs font-semibold uppercase tracking-wide mb-2\" style=\"color: var(--text-secondary)\">Color</label><div class=\"flex gap-2\"><button type=\"button\" @click=\"selectColor('blue')\" data-color=\"blue\" class=\"w-8 h-8 rounded-full color-option bg-blue-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-blue-400\" aria-label=\"Blue\"></button> <button type=\"button\" @click=\"selectColor('red')\" data-color=\"red\" class=\"w-8 h-8 rounded-full color-option bg-red-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-red-400\" aria-label=\"Red\"></button> <button type=\"button\" @click=\"selectColor('yellow')\" data-color=\"yellow\" class=\"w-8 h-8 rounded-full color-option bg-yellow-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-yellow-400\" aria-label=\"Yellow\"></button> <button type=\"button\" @click=\"selectColor('green')\" data-color=\"green\" class=\"w-8 h-8 rounded-full color-option bg-green-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-green-400\" aria-label=\"Green\"></button> <button type=\"button\" @click=\"selectColor('purple')\" data-color=\"purple\" class=\"w-8 h-8 rounded-full color-option bg-purple-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-purple-400\" aria-label=\"Purple\"></button></div><input type=\"hidden\" name=\"color\" id=\"collection-color\" value=\"blue\"></div><div class=\"flex justify-end space-x-3\"><button type=\"button\" @click=\"closeCollectionModal($el)\" class=\"btn btn-secondary\">Cancel</button> <button type=\"submit\" class=\"btn btn-primary\">Create Collection</button></div></form>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\"><div id=\"icon-grid\" class=\"grid grid-cols-8 gap-1 max-h-32 overflow-y-auto p-2 rounded-lg border\" style=\"background-color: var(--bg-primary); border-color: var(--border);\"></div></div><div class=\"mb-6\"><label class=\"block text-xs font-semibold uppercase tracking-wide mb-2\" style=\"color: var(--text-secondary)\">Color</label><div class=\"flex gap-2\"><button type=\"button\" @click=\"selectColor('blue')\" data-color=\"blue\" class=\"w-8 h-8 rounded-full color-option bg-blue-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-blue-400\" aria-label=\"Blue\"></button> <button type=\"button\" @click=\"selectColor('red')\" data-color=\"red\" class=\"w-8 h-8 rounded-full color-option bg-red-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-red-400\" aria-label=\"Red\"></button> <button type=\"button\" @click=\"selectColor('yellow')\" data-color=\"yellow\" class=\"w-8 h-8 rounded-full color-option bg-yellow-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-yellow-400\" aria-label=\"Yellow\"></button> <button type=\"button\" @click=\"selectColor('green')\" data-color=\"green\" class=\"w-8 h-8 rounded-full color-option bg-green-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-green-400\" aria-label=\"Green\"></button> <button type=\"button\" @click=\"selectColor('purple')\" data-color=\"purple\" class=\"w-8 h-8 rounded-full color-option bg-purple-500 hover:ring-2 hover:ring-offset-2 hover:ring-offset-transparent hover:ring-purple-400\" aria-label=\"Purple\"></button></div><input type=\"hidden\" name=\"color\" id=\"collection-color\" value=\"blue\"></div><div class=\"sticky bottom-0 z-10 flex justify-end space-x-3 -mx-5 px-5 py-3 border-t sm:-mx-6 sm:px-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><button type=\"button\" @click=\"closeCollectionModal($el)\" class=\"btn btn-secondary\">Cancel</button> <button type=\"submit\" class=\"btn btn-primary\">Create Collection</button></div></form>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
+11 -11
View File
@@ -220,11 +220,11 @@ templ BookCard(item handlers.BookInfo) {
templ DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections []string, itemsPerSection int) { templ DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections []string, itemsPerSection int) {
<div <div
id="dashboard-settings-modal" id="dashboard-settings-modal"
class="hidden fixed inset-0 z-[70] flex items-center justify-center p-4" class="hidden fixed inset-0 z-[70] flex items-stretch justify-end sm:items-center sm:justify-center sm:p-4"
style="background-color: var(--surface-overlay);" style="background-color: var(--surface-overlay);"
> >
<div class="card p-6 w-full max-w-2xl shadow-2xl" style="box-shadow: var(--shadow-pop);"> <div class="app-modal-panel card p-5 sm:p-6 sm:max-w-2xl">
<div class="flex justify-between items-center mb-6"> <div class="sticky top-0 z-10 flex justify-between items-start mb-6 pb-2" style="background-color: var(--bg-secondary);">
<div> <div>
<h2 class="text-xl font-bold" style="color: var(--text-primary)">Customize Dashboard</h2> <h2 class="text-xl font-bold" style="color: var(--text-primary)">Customize Dashboard</h2>
<p class="text-sm mt-1" style="color: var(--text-secondary)">Drag to reorder, toggle to show or hide.</p> <p class="text-sm mt-1" style="color: var(--text-secondary)">Drag to reorder, toggle to show or hide.</p>
@@ -237,22 +237,22 @@ templ DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections
<div id="collection-list" class="space-y-2 mb-6"> <div id="collection-list" class="space-y-2 mb-6">
for _, section := range sections { for _, section := range sections {
<div <div
class="collection-item flex items-center justify-between p-3 rounded-xl cursor-move select-none card" class="collection-item flex flex-wrap items-center justify-between gap-x-3 gap-y-2 p-3 rounded-xl cursor-move select-none card"
data-collection-id={ section.ID } data-collection-id={ section.ID }
data-is-system={ fmt.Sprintf("%v", section.IsSystem) } data-is-system={ fmt.Sprintf("%v", section.IsSystem) }
draggable="true" draggable="true"
> >
<div class="flex items-center gap-3"> <div class="flex items-center gap-3 min-w-0">
@Icon("grip", "h-5 w-5 shrink-0") @Icon("grip", "h-5 w-5 shrink-0")
<span class="text-xl">{ section.Icon }</span> <span class="text-xl shrink-0">{ section.Icon }</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2 min-w-0">
<span class="font-medium" style="color: var(--text-primary)">{ section.Title }</span> <span class="font-medium truncate" style="color: var(--text-primary)">{ section.Title }</span>
if section.IsSystem { if section.IsSystem {
<span class="badge" style="background-color: var(--accent-muted); color: var(--accent);">System</span> <span class="badge shrink-0" style="background-color: var(--accent-muted); color: var(--accent);">System</span>
} }
</div> </div>
</div> </div>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3 shrink-0 ml-auto">
if section.IsSystem { if section.IsSystem {
<button <button
data-action="restore-system-collection" data-action="restore-system-collection"
@@ -296,7 +296,7 @@ templ DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections
target="items-count-display" target="items-count-display"
/> />
</div> </div>
<div class="flex justify-end gap-3"> <div class="sticky bottom-0 z-10 flex justify-end gap-3 -mx-5 px-5 py-3 border-t sm:-mx-6 sm:px-6" style="background-color: var(--bg-secondary); border-color: var(--border);">
<button data-action="close-dashboard-settings" class="btn btn-secondary">Cancel</button> <button data-action="close-dashboard-settings" class="btn btn-secondary">Cancel</button>
<button data-action="save-dashboard-settings" class="btn btn-primary">Save Changes</button> <button data-action="save-dashboard-settings" class="btn btn-primary">Save Changes</button>
</div> </div>
+10 -10
View File
@@ -747,7 +747,7 @@ func DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections [
templ_7745c5c3_Var34 = templ.NopComponent templ_7745c5c3_Var34 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<div id=\"dashboard-settings-modal\" class=\"hidden fixed inset-0 z-[70] flex items-center justify-center p-4\" style=\"background-color: var(--surface-overlay);\"><div class=\"card p-6 w-full max-w-2xl shadow-2xl\" style=\"box-shadow: var(--shadow-pop);\"><div class=\"flex justify-between items-center mb-6\"><div><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Customize Dashboard</h2><p class=\"text-sm mt-1\" style=\"color: var(--text-secondary)\">Drag to reorder, toggle to show or hide.</p><p class=\"text-xs mt-1 font-medium\" id=\"dashboard-scope-label\" style=\"color: var(--accent)\"></p></div><button data-action=\"close-dashboard-settings\" class=\"icon-btn\" aria-label=\"Close\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<div id=\"dashboard-settings-modal\" class=\"hidden fixed inset-0 z-[70] flex items-stretch justify-end sm:items-center sm:justify-center sm:p-4\" style=\"background-color: var(--surface-overlay);\"><div class=\"app-modal-panel card p-5 sm:p-6 sm:max-w-2xl\"><div class=\"sticky top-0 z-10 flex justify-between items-start mb-6 pb-2\" style=\"background-color: var(--bg-secondary);\"><div><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Customize Dashboard</h2><p class=\"text-sm mt-1\" style=\"color: var(--text-secondary)\">Drag to reorder, toggle to show or hide.</p><p class=\"text-xs mt-1 font-medium\" id=\"dashboard-scope-label\" style=\"color: var(--accent)\"></p></div><button data-action=\"close-dashboard-settings\" class=\"icon-btn\" aria-label=\"Close\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -760,7 +760,7 @@ func DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections [
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
for _, section := range sections { for _, section := range sections {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<div class=\"collection-item flex items-center justify-between p-3 rounded-xl cursor-move select-none card\" data-collection-id=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<div class=\"collection-item flex flex-wrap items-center justify-between gap-x-3 gap-y-2 p-3 rounded-xl cursor-move select-none card\" data-collection-id=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -786,7 +786,7 @@ func DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections [
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" draggable=\"true\"><div class=\"flex items-center gap-3\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" draggable=\"true\"><div class=\"flex items-center gap-3 min-w-0\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -794,27 +794,27 @@ func DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections [
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "<span class=\"text-xl\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "<span class=\"text-xl shrink-0\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var37 string var templ_7745c5c3_Var37 string
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(section.Icon) templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(section.Icon)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `dashboard.templ`, Line: 247, Col: 43} return templ.Error{Err: templ_7745c5c3_Err, FileName: `dashboard.templ`, Line: 247, Col: 52}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</span><div class=\"flex items-center gap-2\"><span class=\"font-medium\" style=\"color: var(--text-primary)\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</span><div class=\"flex items-center gap-2 min-w-0\"><span class=\"font-medium truncate\" style=\"color: var(--text-primary)\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var38 string var templ_7745c5c3_Var38 string
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(section.Title) templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(section.Title)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `dashboard.templ`, Line: 249, Col: 84} return templ.Error{Err: templ_7745c5c3_Err, FileName: `dashboard.templ`, Line: 249, Col: 93}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@@ -825,12 +825,12 @@ func DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections [
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
if section.IsSystem { if section.IsSystem {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<span class=\"badge\" style=\"background-color: var(--accent-muted); color: var(--accent);\">System</span>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<span class=\"badge shrink-0\" style=\"background-color: var(--accent-muted); color: var(--accent);\">System</span>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "</div></div><div class=\"flex items-center gap-3\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "</div></div><div class=\"flex items-center gap-3 shrink-0 ml-auto\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -907,7 +907,7 @@ func DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections [
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "\" class=\"w-full h-2 rounded-lg appearance-none cursor-pointer\" style=\"background-color: var(--border-strong);\" data-input-action=\"update-items-count\" target=\"items-count-display\"></div><div class=\"flex justify-end gap-3\"><button data-action=\"close-dashboard-settings\" class=\"btn btn-secondary\">Cancel</button> <button data-action=\"save-dashboard-settings\" class=\"btn btn-primary\">Save Changes</button></div></div></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "\" class=\"w-full h-2 rounded-lg appearance-none cursor-pointer\" style=\"background-color: var(--border-strong);\" data-input-action=\"update-items-count\" target=\"items-count-display\"></div><div class=\"sticky bottom-0 z-10 flex justify-end gap-3 -mx-5 px-5 py-3 border-t sm:-mx-6 sm:px-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><button data-action=\"close-dashboard-settings\" class=\"btn btn-secondary\">Cancel</button> <button data-action=\"save-dashboard-settings\" class=\"btn btn-primary\">Save Changes</button></div></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
+31
View File
@@ -618,6 +618,15 @@
} }
} }
@keyframes app-sheet-in {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@layer components { @layer components {
/* ---------- Surfaces ---------- */ /* ---------- Surfaces ---------- */
@@ -628,6 +637,28 @@
box-shadow: var(--shadow-card); box-shadow: var(--shadow-card);
} }
/* ---------- Modal shell ---------- */
/* Reusable oversized-pop-up shell (.app-modal-panel): centered dialog on
desktop (>=640px), full-height right slide-out sheet on phones. Pair
with an overlay of `flex items-stretch justify-end sm:items-center
sm:justify-center sm:p-4`. The slide-in animation replays on every
open because the overlay toggles display none -> flex. Long content
scrolls inside the panel; give the header/footer `sticky` + the card
background so actions stay reachable. */
.app-modal-panel {
@apply w-full h-dvh overflow-y-auto overscroll-contain rounded-l-2xl;
box-shadow: var(--shadow-pop);
animation: app-sheet-in 240ms cubic-bezier(0.32, 0.72, 0, 1);
}
@media (min-width: 640px) {
.app-modal-panel {
@apply h-auto max-h-[calc(100vh-2rem)] rounded-2xl;
animation: none;
}
}
/* ---------- Buttons ---------- */ /* ---------- Buttons ---------- */
.btn { .btn {