[OCaml] High Intensity Training Online
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.
ENHANCEMENTS.org
@@ -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
@@ -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
@@ -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
@@ -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 () ->