Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 8 additions & 16 deletions frontend/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,14 @@ html[data-mobile-sidebar-open] [data-mobile-sidebar] { visibility: visible; tran
@media (min-width: 768px) {
[data-mobile-sidebar],
html[data-mobile-sidebar-open] [data-mobile-sidebar] { visibility: visible; height: auto; transform: none; }
html[data-sidebar-collapsed] .app-shell > aside,
#sidebar-toggle:checked ~ .app-shell > aside { width: 4.25rem; }
html[data-sidebar-collapsed] .app-shell .wide-only,
#sidebar-toggle:checked ~ .app-shell .wide-only { display: none; }
html[data-sidebar-collapsed] .app-shell .sidebar-head,
#sidebar-toggle:checked ~ .app-shell .sidebar-head { justify-content: center; gap: 0; padding-left: 0.5rem; padding-right: 0.5rem; }
html[data-sidebar-collapsed] .app-shell [data-nav-link],
#sidebar-toggle:checked ~ .app-shell [data-nav-link] { justify-content: center; gap: 0; }
html[data-sidebar-collapsed] .app-shell [data-sidebar-action],
#sidebar-toggle:checked ~ .app-shell [data-sidebar-action] { justify-content: center; gap: 0; }
html[data-sidebar-collapsed] .app-shell [data-sidebar-favorite],
#sidebar-toggle:checked ~ .app-shell [data-sidebar-favorite] { justify-content: center; gap: 0; padding-left: 0.5rem; padding-right: 0.5rem; }
html[data-sidebar-collapsed] .app-shell [data-member-summary],
#sidebar-toggle:checked ~ .app-shell [data-member-summary] { justify-content: center; gap: 0; padding-left: 0.25rem; padding-right: 0.25rem; }
html[data-sidebar-collapsed] .app-shell [data-member-menu],
#sidebar-toggle:checked ~ .app-shell [data-member-menu] { bottom: 0.5rem; left: calc(100% + 0.5rem); right: auto; width: 12rem; }
html[data-sidebar-collapsed] .app-shell > aside { width: 4.25rem; }
html[data-sidebar-collapsed] .app-shell .wide-only { display: none; }
html[data-sidebar-collapsed] .app-shell .sidebar-head { justify-content: center; gap: 0; padding-left: 0.5rem; padding-right: 0.5rem; }
html[data-sidebar-collapsed] .app-shell [data-nav-link] { justify-content: center; gap: 0; }
html[data-sidebar-collapsed] .app-shell [data-sidebar-action] { justify-content: center; gap: 0; }
html[data-sidebar-collapsed] .app-shell [data-sidebar-favorite] { justify-content: center; gap: 0; padding-left: 0.5rem; padding-right: 0.5rem; }
html[data-sidebar-collapsed] .app-shell [data-member-summary] { justify-content: center; gap: 0; padding-left: 0.25rem; padding-right: 0.25rem; }
html[data-sidebar-collapsed] .app-shell [data-member-menu] { bottom: 0.5rem; left: calc(100% + 0.5rem); right: auto; width: 12rem; }
}
.scrollbar-none { scrollbar-width: none; }
.scrollbar-none::-webkit-scrollbar { display: none; }
Expand Down
2 changes: 1 addition & 1 deletion internal/server/static/app.css

Large diffs are not rendered by default.

20 changes: 14 additions & 6 deletions internal/server/static/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -785,22 +785,30 @@
const active = ["bg-indigo-50", "text-indigo-700", "ring-1", "ring-indigo-100", "dark:bg-indigo-950/50", "dark:text-indigo-200", "dark:ring-indigo-900"];
const inactive = ["text-slate-600", "dark:text-slate-300"];
const links = () => document.querySelectorAll("[data-sidebar-link]");
const sidebarToggle = document.getElementById("sidebar-toggle");
const sidebarToggle = document.querySelector("[data-sidebar-collapse-toggle]");
const sidebarStorageKey = "track-slash.sidebar.collapsed";
const applySidebarCollapsed = (collapsed) => {
document.documentElement.toggleAttribute("data-sidebar-collapsed", collapsed);
if (sidebarToggle) sidebarToggle.checked = collapsed;
if (!sidebarToggle) return;
sidebarToggle.setAttribute("aria-expanded", collapsed ? "false" : "true");
sidebarToggle.setAttribute("aria-label", collapsed ? "Expand sidebar" : "Collapse sidebar");
const icon = sidebarToggle.querySelector("[data-sidebar-collapse-icon]");
if (icon) {
icon.setAttribute("data-lucide", collapsed ? "panel-left-open" : "panel-left-close");
createIcons();
}
};
if (sidebarToggle) {
let collapsed = false;
let collapsed = document.documentElement.hasAttribute("data-sidebar-collapsed");
try {
collapsed = window.localStorage.getItem(sidebarStorageKey) === "true";
} catch (_) {}
applySidebarCollapsed(collapsed);
sidebarToggle.addEventListener("change", () => {
applySidebarCollapsed(sidebarToggle.checked);
sidebarToggle.addEventListener("click", () => {
const collapsed = !document.documentElement.hasAttribute("data-sidebar-collapsed");
applySidebarCollapsed(collapsed);
try {
window.localStorage.setItem(sidebarStorageKey, sidebarToggle.checked ? "true" : "false");
window.localStorage.setItem(sidebarStorageKey, collapsed ? "true" : "false");
} catch (_) {}
});
}
Expand Down
1 change: 0 additions & 1 deletion internal/server/templates/shell.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
<script defer src="/static/lucide.min.js"></script>
</head>
<body data-authenticated="{{if .Anonymous}}false{{else}}true{{end}}" class="flex flex-col overflow-hidden bg-slate-50 text-slate-950 antialiased dark:bg-slate-950 dark:text-slate-100">
<input id="sidebar-toggle" type="checkbox" class="hidden">
<header data-mobile-app-bar class="flex h-14 shrink-0 items-center gap-3 border-b border-slate-200 bg-white px-4 dark:border-slate-800 dark:bg-slate-900 md:hidden">
<button type="button" data-mobile-sidebar-toggle aria-label="Open navigation" aria-controls="app-sidebar" aria-expanded="false" class="grid h-9 w-9 shrink-0 place-items-center rounded-md text-slate-600 hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:text-slate-300 dark:hover:bg-slate-800">
<i data-lucide="menu" class="h-4 w-4" aria-hidden="true"></i>
Expand Down
4 changes: 3 additions & 1 deletion internal/server/templates/shell_sidebar.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{{define "shell-sidebar"}}
<aside id="app-sidebar" data-mobile-sidebar aria-hidden="true" inert class="fixed inset-y-0 left-0 z-40 flex w-72 max-w-[calc(100vw-2rem)] shrink-0 flex-col overflow-visible border-r border-slate-200 bg-white transition-transform duration-200 ease-out dark:border-slate-800 dark:bg-slate-900 md:static md:z-auto md:max-w-none md:transition-all">
<div class="sidebar-head flex h-14 items-center gap-3 border-b border-slate-200 px-4 dark:border-slate-800">
<label for="sidebar-toggle" aria-label="Toggle sidebar" class="hidden h-6 w-6 shrink-0 cursor-pointer place-items-center text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200 md:grid"><i data-lucide="menu" class="h-3.5 w-3.5" aria-hidden="true"></i></label>
<button type="button" data-sidebar-collapse-toggle aria-label="Collapse sidebar" aria-controls="app-sidebar" aria-expanded="true" class="hidden h-8 w-8 shrink-0 place-items-center rounded-md text-slate-500 hover:bg-slate-100 hover:text-slate-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-200 md:grid">
<i data-sidebar-collapse-icon data-lucide="panel-left-close" class="h-4 w-4" aria-hidden="true"></i>
</button>
<div class="wide-only min-w-0">
<div class="truncate text-sm font-semibold">trackslash</div>
</div>
Expand Down
27 changes: 18 additions & 9 deletions internal/server/ui_components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,13 +400,20 @@ func TestUIShellRendersResponsiveAccessibleSidebar(t *testing.T) {
`height: 100dvh`,
`.modal-panel { max-height: calc(100vh - 3rem); max-height: calc(100dvh - 3rem); }`,
`overflow-x-hidden overflow-y-auto`,
"#sidebar-toggle:checked ~ .app-shell > aside",
`data-sidebar-collapse-toggle`,
`aria-label="Collapse sidebar"`,
`aria-controls="app-sidebar" aria-expanded="true"`,
`data-sidebar-collapse-icon data-lucide="panel-left-close"`,
`html[data-sidebar-collapsed] .app-shell > aside`,
`html[data-sidebar-collapsed] .app-shell .wide-only`,
`#sidebar-toggle:checked ~ .app-shell .wide-only { display: none; }`,
`html[data-sidebar-collapsed] .app-shell .wide-only { display: none; }`,
`track-slash.sidebar.collapsed`,
`document.documentElement.toggleAttribute("data-sidebar-collapsed", collapsed)`,
`sidebarToggle.addEventListener("change"`,
`sidebarToggle.setAttribute("aria-expanded", collapsed ? "false" : "true")`,
`sidebarToggle.setAttribute("aria-label", collapsed ? "Expand sidebar" : "Collapse sidebar")`,
`icon.setAttribute("data-lucide", collapsed ? "panel-left-open" : "panel-left-close")`,
`sidebarToggle.addEventListener("click"`,
`const collapsed = !document.documentElement.hasAttribute("data-sidebar-collapsed")`,
`[data-member-menu] { bottom: 0.5rem; left: calc(100% + 0.5rem); right: auto; width: 12rem; }`,
`overflow-visible border-r`,
`data-member-summary`,
Expand Down Expand Up @@ -438,8 +445,8 @@ func TestUIShellRendersResponsiveAccessibleSidebar(t *testing.T) {
if strings.Contains(body[mobileStateStart:mobileStateEnd], `localStorage`) {
t.Fatalf("mobile drawer state must not share desktop collapse persistence: %s", body[mobileStateStart:mobileStateEnd])
}
if strings.Contains(body, "#sidebar-toggle:checked ~ .app-shell aside { width") {
t.Fatalf("sidebar collapse selector targets nested asides: %s", body)
if strings.Contains(body, `id="sidebar-toggle"`) || strings.Contains(body, "#sidebar-toggle") {
t.Fatalf("shell retains the hidden-checkbox sidebar toggle: %s", body)
}
if strings.Contains(body, "data-main-view") {
t.Fatalf("shell still contains legacy sidebar state markers: %s", body)
Expand Down Expand Up @@ -538,12 +545,13 @@ func TestUIShellRemovesCollapsedLabelsFromLoggedOutSidebar(t *testing.T) {
}
body := buf.String()
for _, want := range []string{
`data-sidebar-collapse-toggle aria-label="Collapse sidebar"`,
`<a data-nav-link href="/login" aria-label="Sign in"`,
`<a data-nav-link href="/signup" aria-label="Create account"`,
`<span class="wide-only font-medium">Sign in</span>`,
`<span class="wide-only font-medium">Create account</span>`,
`#sidebar-toggle:checked ~ .app-shell .wide-only { display: none; }`,
`#sidebar-toggle:checked ~ .app-shell [data-nav-link] { justify-content: center; gap: 0; }`,
`html[data-sidebar-collapsed] .app-shell .wide-only { display: none; }`,
`html[data-sidebar-collapsed] .app-shell [data-nav-link] { justify-content: center; gap: 0; }`,
} {
if !strings.Contains(body+string(css), want) {
t.Fatalf("logged-out sidebar missing collapsed-label behavior %q: %s", want, body)
Expand Down Expand Up @@ -594,7 +602,7 @@ func TestUIShellProvidesAccessibleIconTooltips(t *testing.T) {
}
body := strings.Join([]string{buf.String(), string(css), string(appJS), string(projectPanel), string(projectsPanel)}, "\n")
for _, want := range []string{
`aria-label="Toggle sidebar"`,
`aria-label="Collapse sidebar"`,
`aria-label="Me"`,
`aria-label="Projects"`,
`aria-label="Account menu"`,
Expand Down Expand Up @@ -629,7 +637,8 @@ func TestUIShellProvidesAccessibleIconTooltips(t *testing.T) {
}
}
for _, forbidden := range []string{
`title="Toggle sidebar"`,
`title="Collapse sidebar"`,
`title="Expand sidebar"`,
`title="Account menu"`,
`title="New project"`,
`title="{{if .Favorite}}Unfavorite project`,
Expand Down
12 changes: 9 additions & 3 deletions internal/server/ui_home_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ func TestUIRendersWorkSidebar(t *testing.T) {
`data-mobile-sidebar-backdrop`,
`id="app-sidebar" data-mobile-sidebar`,
`data-mobile-sidebar-close`,
`data-sidebar-collapse-toggle`,
`aria-label="Collapse sidebar"`,
`data-sidebar-collapse-icon data-lucide="panel-left-close"`,
`data-member-menu`,
`data-close-on-outside`,
`overflow-visible border-r`,
Expand All @@ -57,7 +60,7 @@ func TestUIRendersWorkSidebar(t *testing.T) {
for _, want := range []string{
`[data-mobile-sidebar]{visibility:hidden;transform:translateX(-100%)}`,
`@media (min-width:768px)`,
"#sidebar-toggle:checked~.app-shell>aside",
`html[data-sidebar-collapsed] .app-shell>aside`,
} {
if !strings.Contains(css, want) {
t.Fatalf("app stylesheet missing %q: %s", want, css)
Expand All @@ -68,8 +71,8 @@ func TestUIRendersWorkSidebar(t *testing.T) {
if mediaStart < 0 || desktopCollapseStart < mediaStart {
t.Fatalf("desktop sidebar collapse CSS must be scoped to the md breakpoint: %s", css)
}
if strings.Contains(css, "#sidebar-toggle:checked~.app-shell aside{width") {
t.Fatalf("sidebar collapse selector targets nested asides: %s", css)
if strings.Contains(css, "#sidebar-toggle") {
t.Fatalf("stylesheet retains the hidden-checkbox sidebar toggle: %s", css)
}

scripts := e.uiGet(t, "/static/app.js", token) + e.uiGet(t, "/static/preload.js", token)
Expand All @@ -79,6 +82,9 @@ func TestUIRendersWorkSidebar(t *testing.T) {
`closeMobileSidebar`,
`mobileSidebar.inert = !visible`,
`track-slash.sidebar.collapsed`,
`sidebarToggle.addEventListener("click"`,
`collapsed ? "Expand sidebar" : "Collapse sidebar"`,
`collapsed ? "panel-left-open" : "panel-left-close"`,
`closeOpenDropdowns`,
} {
if !strings.Contains(scripts, want) {
Expand Down
Loading