style one standard button group, centered bold titles

Align every modal (delete, feedback, cancel workout, override, profile edits) on a single bottom button group: primary action left, secondary Cancel right. Remove the top-right discard/close controls. Titles are bold and centered at the top. Style all button-group controls (button, submit input, anchor) uniformly, so the feedback submit is no longer unstyled. Labels default to Submit/Cancel, overridden with meaningful verbs where clearer.

Commit
92b6139d63eb8c25f4c5791536a7956e4f0e0700
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
lib/presenter/pages_logbook.ml
index 328a1b6b..f31c829c 100644..100644
@@ -60,8 +60,17 @@
60 60 feedback_factors
61 61 @ [
62 62 tag "div"
63 Removed: [ class_ "feedback-actions" ]
64 Removed: [ tag "button" [ type_ "submit" ] [ txt "Submit feedback" ] ];
63 Added: [ class_ "button-group" ]
64 Added: [
65 Added: tag "button" [ type_ "submit" ] [ txt "Submit feedback" ];
66 Added: tag "a"
67 Added: [
68 Added: href Routes.logbook;
69 Added: class_ "button secondary";
70 Added: Dream_html.attr "data-hito-feedback-dismiss";
71 Added: ]
72 Added: [ txt "Cancel" ];
73 Added: ];
65 74 ]);
66 75 ]
67 76
@@ -72,24 +81,7 @@
72 81 [
73 82 tag "div"
74 83 [ class_ "feedback-modal-content" ]
75 Removed: [
76 Removed: tag "div"
77 Removed: [ class_ "feedback-modal-heading" ]
78 Removed: [
79 Removed: tag "h2" [] [ txt "How are you feeling?" ];
80 Removed: (* Discard is a plain link back to the logbook. Nothing is stored
81 Removed: until Submit, so there is no flow state to discard on the
82 Removed: server. With script, the client closes the dialog in place. *)
83 Removed: tag "a"
84 Removed: [
85 Removed: href Routes.logbook;
86 Removed: class_ "button outline feedback-modal-close";
87 Removed: Dream_html.attr "data-hito-feedback-dismiss";
88 Removed: ]
89 Removed: [ txt "Discard" ];
90 Removed: ];
91 Removed: feedback_form request;
92 Removed: ];
84 Added: [ tag "h2" [] [ txt "How are you feeling?" ]; feedback_form request ];
93 85 ]
94 86
95 87 let feedback_list (reports : View_model.Feedback.report list) =
@@ -110,28 +102,14 @@
110 102 tag "dialog"
111 103 [ class_ "confirm-dialog"; Dream_html.attr "open" ]
112 104 [
113 Removed: tag "div"
114 Removed: [ class_ "confirm-dialog-heading" ]
115 Removed: [
116 Removed: tag "h2" [] [ txt "Delete this workout log?" ];
117 Removed: (* Discard: an oxblood-outline control top-right. The client closes
118 Removed: the modal in place; without script it is a link back to the
119 Removed: logbook. *)
120 Removed: tag "a"
121 Removed: [
122 Removed: href Routes.logbook;
123 Removed: class_ "button outline confirm-dialog-close";
124 Removed: Dream_html.attr "data-hito-confirm-dismiss";
125 Removed: ]
126 Removed: [ txt "Discard" ];
127 Removed: ];
105 Added: tag "h2" [] [ txt "Delete this workout log?" ];
128 106 tag "p" []
129 107 [
130 108 txt "Delete %s from %s. This cannot be undone." record.workout_name
131 109 record.date;
132 110 ];
133 111 tag "div"
134 Removed: [ class_ "confirm-dialog-actions" ]
112 Added: [ class_ "button-group" ]
135 113 [
136 114 tag "form"
137 115 [
@@ -145,6 +123,13 @@
145 123 [ type_ "submit"; class_ "danger" ]
146 124 [ txt "Delete workout" ];
147 125 ];
126 Added: tag "a"
127 Added: [
128 Added: href Routes.logbook;
129 Added: class_ "button secondary";
130 Added: Dream_html.attr "data-hito-confirm-dismiss";
131 Added: ]
132 Added: [ txt "Cancel" ];
148 133 ];
149 134 ]
150 135
lib/presenter/pages_profile.ml
index 0d32fa3c..d87781d9 100644..100644
@@ -89,8 +89,10 @@
89 89 ];
90 90 ];
91 91 tag "div"
92 Removed: [ class_ "profile-dialog-actions" ]
92 Added: [ class_ "button-group" ]
93 93 [
94 Added: void "input"
95 Added: [ type_ "submit"; value "Change username" ];
94 96 tag "button"
95 97 [
96 98 type_ "button";
@@ -98,8 +100,6 @@
98 100 Dream_html.attr "data-hito-dialog-close";
99 101 ]
100 102 [ txt "Cancel" ];
101 Removed: void "input"
102 Removed: [ type_ "submit"; value "Change username" ];
103 103 ];
104 104 ];
105 105 ]
@@ -176,8 +176,10 @@
176 176 ];
177 177 ];
178 178 tag "div"
179 Removed: [ class_ "profile-dialog-actions" ]
179 Added: [ class_ "button-group" ]
180 180 [
181 Added: void "input"
182 Added: [ type_ "submit"; value "Change password" ];
181 183 tag "button"
182 184 [
183 185 type_ "button";
@@ -185,8 +187,6 @@
185 187 Dream_html.attr "data-hito-dialog-close";
186 188 ]
187 189 [ txt "Cancel" ];
188 Removed: void "input"
189 Removed: [ type_ "submit"; value "Change password" ];
190 190 ];
191 191 ];
192 192 ]
lib/presenter/pages_routine.ml
index 50d00445..ba75fbba 100644..100644
@@ -99,15 +99,15 @@
99 99 [ class_ "button-group" ]
100 100 [
101 101 tag "button"
102 Added: [ type_ "button"; Dream_html.attr "data-hito-confirm-accept" ]
103 Added: [ txt "Begin under override" ];
104 Added: tag "button"
102 105 [
103 106 type_ "button";
104 107 class_ "secondary";
105 108 Dream_html.attr "data-hito-confirm-dismiss";
106 109 ]
107 110 [ txt "Wait to recover" ];
108 Removed: tag "button"
109 Removed: [ type_ "button"; Dream_html.attr "data-hito-confirm-accept" ]
110 Removed: [ txt "Begin under override" ];
111 111 ];
112 112 ];
113 113 ]
lib/presenter/pages_workout.ml
index b6fc3514..b2bc38b1 100644..100644
@@ -374,16 +374,16 @@
374 374 [
375 375 tag "button"
376 376 [
377 Added: type_ "button"; Dream_html.attr "data-hito-confirm-accept";
378 Added: ]
379 Added: [ txt "Cancel workout" ];
380 Added: tag "button"
381 Added: [
377 382 type_ "button";
378 383 class_ "secondary";
379 384 Dream_html.attr "data-hito-confirm-dismiss";
380 385 ]
381 386 [ txt "Keep logging" ];
382 Removed: tag "button"
383 Removed: [
384 Removed: type_ "button"; Dream_html.attr "data-hito-confirm-accept";
385 Removed: ]
386 Removed: [ txt "Cancel workout" ];
387 387 ];
388 388 ];
389 389 ]
lib/web/assets/hito.css
index 8a7c9196..b8e1f0c3 100644..100644
@@ -536,6 +536,41 @@
536 536 background: var(--oxblood);
537 537 }
538 538
539 Added: /* Every control in a button group — an anchor, a form button, or a submit
540 Added: input — shares the primary look, so modals and action rows read alike. The
541 Added: group is a two-column grid: primary action left, secondary right. */
542 Added: .button-group button,
543 Added: .button-group input[type="submit"] {
544 Added: display: inline-flex;
545 Added: align-items: center;
546 Added: justify-content: center;
547 Added: width: 100%;
548 Added: min-height: 3rem;
549 Added: border: 2px solid var(--oxblood-dark);
550 Added: border-radius: var(--radius);
551 Added: color: var(--on-primary);
552 Added: background: var(--oxblood);
553 Added: cursor: pointer;
554 Added: font: 700 var(--font-size-small)/1 var(--sans);
555 Added: letter-spacing: 0.055em;
556 Added: text-transform: uppercase;
557 Added: transition: background-color 150ms ease, transform 150ms ease;
558 Added: }
559 Added: .button-group button:hover,
560 Added: .button-group input[type="submit"]:hover {
561 Added: background: var(--oxblood-hover);
562 Added: transform: translateY(-2px);
563 Added: }
564 Added: .button-group button.secondary {
565 Added: color: var(--brass);
566 Added: background: transparent;
567 Added: border-color: var(--brass);
568 Added: }
569 Added: .button-group button.secondary:hover {
570 Added: color: var(--on-brass);
571 Added: background: var(--brass);
572 Added: }
573 Added:
539 574 /* The logbook is one real table at every width. The columns — date, workout
540 575 name, duration, and the row menu — are few and narrow enough to share a
541 576 single table layout, so there is no mobile card reflow: the name column takes
@@ -686,25 +721,7 @@
686 721 }
687 722 .confirm-dialog button.danger:hover { background: var(--oxblood); }
688 723 .confirm-dialog .delete-record-form { margin: 0; }
689 Removed: /* Heading row: title left, an oxblood-outline Discard control top-right. */
690 Removed: .confirm-dialog-heading {
691 Removed: display: flex;
692 Removed: align-items: flex-start;
693 Removed: justify-content: space-between;
694 Removed: gap: 1rem;
695 Removed: }
696 Removed: .confirm-dialog-heading h2 { margin: 0; }
697 Removed: .confirm-dialog-close { flex: 0 0 auto; }
698 Removed: /* The delete action sits centred at the bottom of the modal. */
699 Removed: .confirm-dialog-actions {
700 Removed: display: flex;
701 Removed: justify-content: center;
702 Removed: margin-top: 1.25rem;
703 Removed: }
704 Removed: .confirm-dialog-actions .delete-record-form button {
705 Removed: width: auto;
706 Removed: min-width: 12rem;
707 Removed: }
724 Added: .confirm-dialog h2 { margin-top: 0; text-align: center; }
708 725
709 726 .logbook-detail-row {
710 727 display: block;
@@ -1141,39 +1158,7 @@
1141 1158 .feedback-modal::backdrop { background: rgba(26, 26, 22, 0.45); }
1142 1159 .feedback-modal[open] { animation: modal-enter 180ms ease both; }
1143 1160 .feedback-modal-content { padding: 1.25rem; }
1144 Removed: .feedback-modal-heading {
1145 Removed: display: flex;
1146 Removed: align-items: start;
1147 Removed: justify-content: space-between;
1148 Removed: gap: 1rem;
1149 Removed: }
1150 Removed: .feedback-modal-heading h2 { margin-top: 0; }
1151 Removed: /* Discard sits in the heading row, so it is shorter than a full-height button. */
1152 Removed: .feedback-modal-close {
1153 Removed: min-height: 2.5rem;
1154 Removed: padding: 0 0.85rem;
1155 Removed: }
1156 Removed: .feedback-actions {
1157 Removed: display: flex;
1158 Removed: flex-wrap: wrap;
1159 Removed: gap: 0.6rem;
1160 Removed: margin-top: 1rem;
1161 Removed: }
1162 Removed: .feedback-actions button {
1163 Removed: flex: 1 1 10rem;
1164 Removed: min-height: 3rem;
1165 Removed: border: 2px solid var(--oxblood-dark);
1166 Removed: border-radius: var(--radius);
1167 Removed: color: var(--on-primary);
1168 Removed: background: var(--oxblood);
1169 Removed: cursor: pointer;
1170 Removed: font: 700 var(--font-size-small)/1 var(--sans);
1171 Removed: letter-spacing: 0.055em;
1172 Removed: text-transform: uppercase;
1173 Removed: }
1174 Removed: .feedback-actions button:hover {
1175 Removed: background: var(--oxblood-hover);
1176 Removed: }
1161 Added: .feedback-modal-content h2 { margin-top: 0; text-align: center; }
1177 1162
1178 1163 .feedback-list {
1179 1164 margin-top: 1rem;
@@ -1263,12 +1248,7 @@
1263 1248 }
1264 1249 .profile-dialog[open] { animation: modal-enter 180ms ease both; }
1265 1250 .profile-dialog::backdrop { background: rgba(26, 26, 22, 0.45); }
1266 Removed: .profile-dialog h3 { margin-top: 0; }
1267 Removed: .profile-dialog-actions {
1268 Removed: display: flex;
1269 Removed: gap: 0.75rem;
1270 Removed: margin-top: 1rem;
1271 Removed: }
1251 Added: .profile-dialog h3 { margin-top: 0; text-align: center; }
1272 1252
1273 1253 .done {
1274 1254 color: var(--evergreen);
test/web/logbook.ml
index a438c474..b91e3632 100644..100644
@@ -5,22 +5,24 @@
5 5 [
6 6 ( "web.logbook",
7 7 [
8 Removed: ( "the logbook shows an empty table state without top action buttons",
8 Added: ( "the empty logbook shows the actions and the empty state",
9 9 `Quick,
10 10 fun () ->
11 11 let c = client () in
12 12 let _ = sign_in_new c in
13 13 let page = body (get c "/logbook") in
14 Added: (* The action bar is always present. *)
14 15 Alcotest.(check bool)
15 Removed: "no Log feedback top action" false
16 Removed: (Option.is_some (index_from ~needle:"Log feedback" page 0));
16 Added: "View progress action shown" true
17 Added: (Option.is_some (index_from ~needle:"View progress" page 0));
17 18 Alcotest.(check bool)
18 Removed: "no Edit logbook top action" false
19 Removed: (Option.is_some (index_from ~needle:"Edit logbook" page 0));
19 Added: "Record feedback action shown" true
20 Added: (Option.is_some (index_from ~needle:"Record feedback" page 0));
21 Added: (* No workout table, just the empty-state line. *)
20 22 Alcotest.(check bool)
21 Removed: "the top action group is absent" false
23 Added: "no workout table" false
22 24 (Option.is_some
23 Removed: (index_from ~needle:"class=\"button-group\"" page 0));
25 Added: (index_from ~needle:"class=\"logbook-table\"" page 0));
24 26 Alcotest.(check bool)
25 27 "empty state when nothing is logged" true
26 28 (Option.is_some (index_from ~needle:"Nothing logged yet." page 0))