feat remove the hamburger menu

Drop the details/summary disclosure and all its styles. Navigation is now the inline primary nav on desktop and the fixed bottom nav on mobile. The header sign-out shows at every width beside the brand, so sign-out stays reachable without the menu that used to hold it.

Commit
7c7c5aa79839bc9d7f3e9426d65248d7647544b0
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
ENHANCEMENTS.org
index cf778600..bf197856 100644..100644
@@ -9,7 +9,7 @@
9 9 Use `Home`, `Routine` when the user is not logging (changing to `Current Workout` while logging), and `<username>` for the workout log. Keep all three actions available in both modes.
10 10 ** DONE Simplify the top bar brand
11 11 Replace the top bar brand with `hito` in Work Sans, without a subtitle.
12 Removed: ** TODO Remove the mobile hamburger menu
12 Added: ** DONE Remove the mobile hamburger menu
13 13 Do not show a hamburger menu on mobile.
14 14 ** TODO Add placeholders to load and reps fields
15 15 Add placeholder values to the load and reps fields on the workout logging page.
@@ -19,3 +19,5 @@
19 19 Add animations to the routine accordions.
20 20 ** TODO Style workout accordions with CSS
21 21 Style the workout accordions with CSS.
22 Added: ** TODO Use brass for secondary buttons
23 Added: Use a secondary brass color for secondary buttons.
lib/web/assets/hito.css
index 7ad36dfc..2532b23d 100644..100644
@@ -133,93 +133,17 @@
133 133 font-weight: 800;
134 134 }
135 135
136 Removed: /* The compact-desktop / mobile hamburger menu. It is a native details/summary
137 Removed: disclosure carrying the same primary links and a CSRF sign-out form. It is
138 Removed: hidden on wide desktop, where the inline .primary-nav and .account sign-out
139 Removed: are shown instead. A responsive rule below reveals it once those crowd. */
140 Removed: .menu { position: relative; display: none; }
141 Removed:
142 Removed: .menu-toggle {
143 Removed: display: inline-flex;
144 Removed: align-items: center;
145 Removed: justify-content: center;
146 Removed: width: 2.75rem;
147 Removed: height: 2.75rem;
148 Removed: border: 1px solid var(--rule-strong);
149 Removed: border-radius: 0.3rem;
150 Removed: background: var(--paper);
151 Removed: cursor: pointer;
152 Removed: list-style: none;
153 Removed: }
154 Removed:
155 Removed: /* Suppress the native disclosure triangle across engines. */
156 Removed: .menu-toggle::-webkit-details-marker { display: none; }
157 Removed: .menu-toggle::marker { content: ""; }
158 Removed:
159 Removed: .menu-toggle:hover { background: #ece4d5; }
160 Removed:
161 Removed: .menu-toggle-bars,
162 Removed: .menu-toggle-bars::before,
163 Removed: .menu-toggle-bars::after {
164 Removed: display: block;
165 Removed: width: 1.25rem;
166 Removed: height: 2px;
167 Removed: background: var(--ink);
168 Removed: }
169 Removed:
170 Removed: .menu-toggle-bars { position: relative; }
171 Removed: .menu-toggle-bars::before,
172 Removed: .menu-toggle-bars::after {
173 Removed: position: absolute;
174 Removed: left: 0;
175 Removed: content: "";
176 Removed: }
177 Removed: .menu-toggle-bars::before { top: -0.4rem; }
178 Removed: .menu-toggle-bars::after { top: 0.4rem; }
179 Removed:
180 Removed: .menu-panel {
181 Removed: position: absolute;
182 Removed: z-index: 30;
183 Removed: top: calc(100% + 0.4rem);
184 Removed: right: 0;
185 Removed: display: flex;
186 Removed: flex-direction: column;
187 Removed: min-width: 12rem;
188 Removed: border: 1px solid var(--rule-strong);
189 Removed: border-top: 3px solid var(--oxblood);
190 Removed: background: var(--paper-raised);
191 Removed: border-radius: 0.35rem;
192 Removed: box-shadow: 0 0.4rem 1.5rem rgba(26, 26, 22, 0.15);
193 Removed: padding: 0.4rem;
194 Removed: }
195 Removed:
196 Removed: .menu-panel > a {
197 Removed: display: flex;
198 Removed: align-items: center;
136 Added: /* The header sign-out sits at the right of the masthead at every width. It is
137 Added: a compact, content-sized control, not the full-width submit used in forms. */
138 Added: .account { display: block; }
139 Added: .account .logout { margin: 0; }
140 Added: .account .logout input[type="submit"] {
141 Added: width: auto;
199 142 min-height: 2.75rem;
200 Removed: padding: 0.55rem 0.7rem;
201 Removed: color: var(--ink);
202 Removed: border-radius: 0.25rem;
203 Removed: font-size: 0.95rem;
204 Removed: text-decoration: none;
143 Added: margin: 0;
144 Added: padding: 0.5rem 0.9rem;
205 145 }
206 146
207 Removed: .menu-panel > a:hover {
208 Removed: background: #ece4d5;
209 Removed: color: var(--oxblood-dark);
210 Removed: }
211 Removed:
212 Removed: .menu-panel .logout { margin: 0.3rem 0 0; }
213 Removed: .menu-panel .logout input[type="submit"] { margin-top: 0; }
214 Removed:
215 Removed: .page-home .menu-panel > a[href="/"],
216 Removed: .page-routine .menu-panel > a[href="/routine"],
217 Removed: .page-workout .menu-panel > a[href="/workout"],
218 Removed: .page-history .menu-panel > a[href="/history"] {
219 Removed: color: var(--paper-raised);
220 Removed: background: var(--oxblood);
221 Removed: }
222 Removed:
223 147 .page-home .bottom-nav > a[href="/"],
224 148 .page-routine .bottom-nav > a[href="/routine"],
225 149 .page-workout .bottom-nav > a[href="/workout"],
@@ -502,12 +426,11 @@
502 426 }
503 427
504 428 /* Mobile-first navigation with a single desktop breakpoint at 42.0625rem.
505 Removed: Below it, the layout is mobile: a fixed bottom nav and the hamburger menu.
506 Removed: At or above it, the layout is desktop: the inline primary nav and header
507 Removed: sign-out replace both. */
508 Removed: .primary-nav,
509 Removed: .account { display: none; }
510 Removed: .menu { display: block; }
429 Added: Below it, the layout is mobile: a fixed bottom nav for the three actions,
430 Added: with the header sign-out kept beside the brand. At or above it, the inline
431 Added: primary nav joins the header sign-out and the bottom nav is hidden. */
432 Added: .primary-nav { display: none; }
433 Added: .account { display: block; }
511 434 .bottom-nav {
512 435 position: fixed;
513 436 z-index: 20;
@@ -544,5 +467,4 @@
544 467 .bottom-nav { display: none; }
545 468 .primary-nav { display: flex; }
546 469 .account { display: block; }
547 Removed: .menu { display: none; }
548 470 }
lib/web/pages.ml
index 40263096..0f943396 100644..100644
@@ -81,56 +81,6 @@
81 81 ]
82 82 | _ -> []
83 83 in
84 Removed: (* The compact-desktop and mobile fallback: a native details/summary
85 Removed: disclosure. It carries the same primary links as [primary_nav] plus a
86 Removed: CSRF-protected sign-out form, so sign-out stays reachable at any width
87 Removed: where the inline [account_area] is hidden. [details]/[summary] is a
88 Removed: built-in, keyboard-operable, screen-reader-announced disclosure — no
89 Removed: client script and no ARIA to hand-maintain. CSS shows it only when the
90 Removed: inline navigation would crowd or is hidden. *)
91 Removed: let menu =
92 Removed: match (trainee, request) with
93 Removed: | Some (trainee : Trainee.t), Some request ->
94 Removed: [
95 Removed: tag "details"
96 Removed: [ class_ "menu" ]
97 Removed: [
98 Removed: tag "summary"
99 Removed: [
100 Removed: class_ "menu-toggle";
101 Removed: Dream_html.string_attr "aria-label" "Menu";
102 Removed: ]
103 Removed: [
104 Removed: tag "span"
105 Removed: [
106 Removed: class_ "menu-toggle-bars";
107 Removed: Dream_html.string_attr "aria-hidden" "true";
108 Removed: ]
109 Removed: [];
110 Removed: ];
111 Removed: tag "nav"
112 Removed: [
113 Removed: class_ "menu-panel";
114 Removed: Dream_html.string_attr "aria-label" "Menu navigation";
115 Removed: ]
116 Removed: (navigation_links (Trainee.username_to_string trainee.username)
117 Removed: @ [
118 Removed: tag "form"
119 Removed: [
120 Removed: action Routes.logout;
121 Removed: post_form;
122 Removed: class_ "logout";
123 Removed: Dream_html.attr "data-hito-app-form";
124 Removed: ]
125 Removed: [
126 Removed: Dream_html.csrf_tag request;
127 Removed: void "input" [ type_ "submit"; value "Sign out" ];
128 Removed: ];
129 Removed: ]);
130 Removed: ];
131 Removed: ]
132 Removed: | _ -> []
133 Removed: in
134 84 let bottom_nav =
135 85 match (trainee, request) with
136 86 | Some (trainee : Trainee.t), Some _ ->
@@ -187,7 +137,7 @@
187 137 else [])
188 138 [ txt "hito" ];
189 139 ]
190 Removed: @ primary_nav @ account_area @ menu);
140 Added: @ primary_nav @ account_area);
191 141 tag "main"
192 142 [ id "main-content"; tabindex "-1" ]
193 143 [
test/test_web.ml
index 9e8d1929..4a61ca5e 100644..100644
@@ -191,72 +191,56 @@
191 191 Alcotest.(check bool)
192 192 "shows bottom navigation" true
193 193 (contains ~substring:"bottom-nav" (body overview)) );
194 Removed: ( "authenticated pages carry both inline and hamburger affordances",
194 Added: ( "authenticated pages carry inline nav and a header sign-out",
195 195 `Quick,
196 196 fun () ->
197 197 let c = client () in
198 198 let _ = sign_in_new c in
199 199 let page = body (get c "/") in
200 Removed: (* Inline affordances: the wide-desktop primary nav and the header
201 Removed: sign-out form both exist in the markup, ready for responsive CSS
202 Removed: to reveal or hide them. *)
200 Added: (* Inline affordances: the primary nav and the header sign-out form
201 Added: both exist in the markup, ready for responsive CSS to reveal or
202 Added: hide them. *)
203 203 Alcotest.(check bool)
204 204 "keeps the inline primary navigation" true
205 205 (contains ~substring:"class=\"primary-nav\"" page);
206 206 Alcotest.(check bool)
207 Removed: "keeps the inline header sign-out" true
207 Added: "keeps the header sign-out" true
208 208 (contains ~substring:"class=\"account\"" page);
209 Removed: (* The hamburger menu: a native details/summary disclosure with its
210 Removed: toggle and panel classes, present at every width so responsive
211 Removed: CSS decides when to show it. *)
209 Added: (* No hamburger menu at any width: the disclosure and its classes
210 Added: are gone. *)
212 211 Alcotest.(check bool)
213 Removed: "renders the details/summary menu" true
212 Added: "renders no hamburger menu" false
214 213 (contains ~substring:"class=\"menu\"" page);
215 214 Alcotest.(check bool)
216 Removed: "renders the menu toggle summary" true
217 Removed: (contains ~substring:"class=\"menu-toggle\"" page);
215 Added: "renders no menu toggle" false
216 Added: (contains ~substring:"menu-toggle" page);
218 217 Alcotest.(check bool)
219 Removed: "renders the menu panel" true
220 Removed: (contains ~substring:"class=\"menu-panel\"" page);
221 Removed: Alcotest.(check bool)
222 Removed: "uses a native disclosure element" true
223 Removed: (contains ~substring:"<details" page) );
224 Removed: ( "the hamburger menu holds the primary links and a CSRF sign-out",
218 Added: "renders no menu panel" false
219 Added: (contains ~substring:"menu-panel" page) );
220 Added: ( "the header sign-out is a CSRF-protected control",
225 221 `Quick,
226 222 fun () ->
227 223 let c = client () in
228 224 let _ = sign_in_new c in
229 225 let page = body (get c "/") in
230 Removed: (* Isolate the menu panel so the assertions do not pass on the
231 Removed: inline nav's copies of the same links. *)
232 Removed: let panel =
226 Added: (* Isolate the account area so the assertions read the sign-out
227 Added: control rather than another form on the page. *)
228 Added: let account =
233 229 let start =
234 Removed: Option.get (index_from ~needle:"class=\"menu-panel\"" page 0)
230 Added: Option.get (index_from ~needle:"class=\"account\"" page 0)
235 231 in
236 Removed: let stop = Option.get (index_from ~needle:"</nav>" page start) in
232 Added: let stop = Option.get (index_from ~needle:"</div>" page start) in
237 233 String.sub page start (stop - start)
238 234 in
239 235 Alcotest.(check bool)
240 Removed: "menu links to the home overview" true
241 Removed: (contains ~substring:"href=\"/\"" panel);
236 Added: "carries a sign-out form" true
237 Added: (contains ~substring:"action=\"/logout\"" account);
242 238 Alcotest.(check bool)
243 Removed: "menu links to the routine when not logging" true
244 Removed: (contains ~substring:"href=\"/routine\"" panel);
239 Added: "the sign-out is CSRF-protected" true
240 Added: (contains ~substring:"dream.csrf" account);
245 241 Alcotest.(check bool)
246 Removed: "menu omits the workout link when not logging" false
247 Removed: (contains ~substring:"href=\"/workout\"" panel);
248 Removed: Alcotest.(check bool)
249 Removed: "menu links to the history" true
250 Removed: (contains ~substring:"href=\"/history\"" panel);
251 Removed: Alcotest.(check bool)
252 Removed: "menu carries a sign-out form" true
253 Removed: (contains ~substring:"action=\"/logout\"" panel);
254 Removed: Alcotest.(check bool)
255 Removed: "the menu sign-out is CSRF-protected" true
256 Removed: (contains ~substring:"dream.csrf" panel);
257 Removed: Alcotest.(check bool)
258 Removed: "the menu sign-out is labelled" true
259 Removed: (contains ~substring:"Sign out" panel) );
242 Added: "the sign-out is labelled" true
243 Added: (contains ~substring:"Sign out" account) );
260 244 ( "navigation offers three actions that toggle with logging",
261 245 `Quick,
262 246 fun () ->