myheats / change low width table design to description tables #2

accepted · opened on 2024-10-08 by sam
Help
# add changes to patch request
git format-patch master --stdout | ssh pr.in0rdr.ch pr add 2
# add review to patch request
git format-patch master --stdout | ssh pr.in0rdr.ch pr add --review 2
# remove patchset
ssh pr.in0rdr.ch ps rm ps-x
# checkout all patches
ssh pr.in0rdr.ch pr print 2 | git am -3
# print a diff between the last two patches in a patch request
ssh pr.in0rdr.ch pr diff 2
# accept PR
ssh pr.in0rdr.ch pr accept 2
# close PR
ssh pr.in0rdr.ch pr close 2

Logs

sam created pr with ps-2 on 2024-10-08
in0rdr changed status on 2024-10-08 {"status":"accepted"}

Patchsets

Diff ↕

change low width table design to description tables

Berchtold Samuel <samuel.berchtold@gmail.com>
 index.html                   |   4 +-
 src/frontend/App.jsx         |   4 +-
 src/frontend/Leaderboard.jsx | 250 +++++++++++++++++++++--------------
 src/frontend/css/App.css     |  73 +++++-----
 src/frontend/css/index.css   |   8 +-
 5 files changed, 198 insertions(+), 141 deletions(-)
Patch
  1From 1b0a391fd2609391299e9802b835d0e467acb3b4 Mon Sep 17 00:00:00 2001
  2From: Berchtold Samuel <samuel.berchtold@gmail.com>
  3Date: Wed, 9 Oct 2024 00:06:13 +0200
  4Subject: [PATCH] change low width table design to description tables
  5
  6---
  7 index.html                   |   4 +-
  8 src/frontend/App.jsx         |   4 +-
  9 src/frontend/Leaderboard.jsx | 250 +++++++++++++++++++++--------------
 10 src/frontend/css/App.css     |  73 +++++-----
 11 src/frontend/css/index.css   |   8 +-
 12 5 files changed, 198 insertions(+), 141 deletions(-)
 13
 14diff --git a/index.html b/index.html
 15index 05d71f0..3d94e61 100644
 16--- a/index.html
 17+++ b/index.html
 18@@ -3,7 +3,7 @@
 19   <head>
 20     <meta charset="UTF-8" />
 21     <link rel="icon" type="image/x-icon" href="/favicon.ico" />
 22-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 23+    <meta name="viewport" content="width=device-width" />
 24     <link rel="apple-touch-icon" href="/logo192.png" />
 25     <!--
 26       manifest.json provides metadata used when your web app is installed on a
 27@@ -13,7 +13,7 @@
 28     <title>My Heats</title>
 29   </head>
 30   <body>
 31-    <noscript>You need to enable JavaScript to run this app.</noscript>
 32+    <noscript>Why You scared of JavaScript so much, failure.</noscript>
 33     <div id="root"></div>
 34     <script type="module" src="/src/frontend/main.jsx"></script>
 35   </body>
 36diff --git a/src/frontend/App.jsx b/src/frontend/App.jsx
 37index b8f6357..1ef94c4 100644
 38--- a/src/frontend/App.jsx
 39+++ b/src/frontend/App.jsx
 40@@ -65,7 +65,7 @@ function Layout() {
 41   }, [])
 42 
 43   return (
 44-    <Fragment>
 45+    <>
 46       <nav className={theme}>
 47         <ul>
 48           <li>
 49@@ -123,7 +123,7 @@ function Layout() {
 50           }
 51         </span>
 52       </footer>
 53-    </Fragment>
 54+    </>
 55   )
 56 }
 57 
 58diff --git a/src/frontend/Leaderboard.jsx b/src/frontend/Leaderboard.jsx
 59index 1e44ddc..b345b12 100644
 60--- a/src/frontend/Leaderboard.jsx
 61+++ b/src/frontend/Leaderboard.jsx
 62@@ -204,46 +204,51 @@ function NewHeatForm(leaderboard, rankingComp, selectHeatRef, selectRankRef, ses
 63       <p>
 64         Create new heat with top N athletes from the sorted leaderboard (<i>* required</i>).
 65       </p>
 66-      <form method='post' onSubmit={e => newHeatFromLeaderboard(
 67-          e,
 68-          leaderboard,
 69-          rankingComp,
 70-          selectHeatRef,
 71-          selectRankRef,
 72-          session,
 73-        )}>
 74-        <table>
 75-          <thead>
 76-            <tr>
 77-              <th>New heat name *</th>
 78-              <th>Location</th>
 79-              <th>Planned start</th>
 80-              <th>Include top N</th>
 81-              <td></td>
 82-            </tr>
 83-          </thead>
 84-          <tbody>
 85-            <tr>
 86-              <td data-title='New heat name *'>
 87-                <input type='text' name='name' />
 88-              </td>
 89-              <td data-title='Location'>
 90-                <input type='text' name='location' />
 91-              </td>
 92-              <td data-title='Planned start'>
 93-                <input
 94-                  type='time'
 95-                  name='planned_start' />
 96-              </td>
 97-              <td data-title='Include top N'>
 98-                <input type='number' name='size' />
 99-              </td>
100-              <td>
101-                <button type='submit'>&#43; new</button>
102-              </td>
103-            </tr>
104-          </tbody>
105-        </table>
106+      <form
107+        method="post"
108+        onSubmit={(e) =>
109+          newHeatFromLeaderboard(
110+            e,
111+            leaderboard,
112+            rankingComp,
113+            selectHeatRef,
114+            selectRankRef,
115+            session
116+          )
117+        }
118+      >
119+        <div className="table-container">
120+          <table>
121+            <thead>
122+              <tr>
123+                <th>New heat name *</th>
124+                <th>Location</th>
125+                <th>Planned start</th>
126+                <th>Include top N</th>
127+                <td></td>
128+              </tr>
129+            </thead>
130+            <tbody>
131+              <tr>
132+                <td data-title="New heat name *">
133+                  <input type="text" name="name" />
134+                </td>
135+                <td data-title="Location">
136+                  <input type="text" name="location" />
137+                </td>
138+                <td data-title="Planned start">
139+                  <input type="time" name="planned_start" />
140+                </td>
141+                <td data-title="Include top N">
142+                  <input type="number" name="size" />
143+                </td>
144+                <td>
145+                  <button type="submit">&#43; new</button>
146+                </td>
147+              </tr>
148+            </tbody>
149+          </table>
150+        </div>
151       </form>
152     </div>
153   )
154@@ -351,85 +356,130 @@ function Leaderboard({session}) {
155   }, [heatSelection]);
156 
157   return (
158-    <div>
159-      <div className='Leaderboard'>
160+    <>
161+      <div className="Leaderboard">
162         <header>
163-          <button disabled={!loading} className='loading' >↺ loading</button>
164-          <button className={`show-details ${details ? 'toggled' : ''}`} onClick={() => showDetails(!details)}>
165-            <div className='thumb'></div>
166-            <span>{details ? 'less' : 'more'}</span>
167+          <button disabled={!loading} className="loading">
168+            ↺ loading
169           </button>
170-          <label htmlFor='heat'>Heats to display</label>
171+          <button
172+            className={`show-details ${details ? "toggled" : ""}`}
173+            onClick={() => showDetails(!details)}
174+          >
175+            <div className="thumb"></div>
176+            <span>{details ? "less" : "more"}</span>
177+          </button>
178+          <label htmlFor="heat">Heats to display</label>
179           <Select
180             closeMenuOnSelect={false}
181             isMulti
182             options={heatOpts}
183-            onChange={h => setHeatSelection(h)}
184+            onChange={(h) => setHeatSelection(h)}
185             ref={selectHeatRef}
186-            id='heat' />
187-           <label htmlFor='rank' className={details ? '' : 'hidden'}>Rank by</label>
188-           <Select
189+            id="heat"
190+          />
191+          <label htmlFor="rank" className={details ? "" : "hidden"}>
192+            Rank by
193+          </label>
194+          <Select
195             closeMenuOnSelect={false}
196             isMulti
197             options={rankOpts}
198             defaultValue={rankOpts[0].options[3]}
199-            onChange={h => setRankingComp(h)}
200+            onChange={(h) => setRankingComp(h)}
201             ref={selectRankRef}
202-            className={details ? '' : 'hidden'}
203-            id='rank' />
204+            className={details ? "" : "hidden"}
205+            id="rank"
206+          />
207         </header>
208-        <table className={details ? 'leaderboard' : 'hide-rank'}>
209-          <thead>
210-            <tr>
211-              <th className={details ? 'right' : 'hidden'}>Rank</th>
212-              <th className='right'>Start Nr.</th>
213-              <th>Name</th>
214-              <th className={details ? '' : 'hidden'}>Birthday</th>
215-              <th className={details ? '' : 'hidden'}>School</th>
216-              {heatSelection.map(h => (
217-                <th className={details ? 'right' : 'hidden'} key={h.value}>{h.label}</th>
218+        <div className="table-container">
219+          <table className={details ? "leaderboard" : "hide-rank"}>
220+            <thead>
221+              <tr>
222+                <th className={details ? "right" : "hidden"}>Rank</th>
223+                <th className="right">Start Nr.</th>
224+                <th>Name</th>
225+                <th className={details ? "" : "hidden"}>Birthday</th>
226+                <th className={details ? "" : "hidden"}>School</th>
227+                {heatSelection.map((h) => (
228+                  <th className={details ? "right" : "hidden"} key={h.value}>
229+                    {h.label}
230+                  </th>
231+                ))}
232+                <th className={details ? "right" : "hidden"}>Best</th>
233+                <th className={details ? "right" : "hidden"}>Worst</th>
234+                <th className="right">Total</th>
235+              </tr>
236+            </thead>
237+            <tbody>
238+              {leaderboard.sort(rankByHeat(rankingComp)).map((i) => (
239+                <tr key={i.id}>
240+                  <td className={details ? "right" : "hidden"}></td>
241+                  <td data-title="Start Nr." className="right">
242+                    {i.nr}
243+                  </td>
244+                  <td data-title="Name">
245+                    {i.firstname} {i.lastname}
246+                  </td>
247+                  <td data-title="Birthday" className={details ? "" : "hidden"}>
248+                    {i.birthday
249+                      ? new Date(i.birthday).toLocaleDateString(
250+                          locale,
251+                          dateOptions
252+                        )
253+                      : ""}
254+                  </td>
255+                  <td data-title="School" className={details ? "" : "hidden"}>
256+                    {i.school}
257+                  </td>
258+                  {heatSelection.map((h) => (
259+                    // list all scores from the judges seperated with '+' signs, show sum on right side
260+                    <td
261+                      key={h.value}
262+                      className={details ? "right" : "hidden"}
263+                      data-title={h.label}
264+                    >
265+                      {formatScores(i, h)}
266+                    </td>
267+                  ))}
268+                  <td
269+                    className={details ? "right" : "hidden"}
270+                    data-title="Best"
271+                  >
272+                    {i.bestHeat}
273+                  </td>
274+                  <td
275+                    className={details ? "right" : "hidden"}
276+                    data-title="Worst"
277+                  >
278+                    {i.worstHeat}
279+                  </td>
280+                  <td className="right total" data-title="Total">
281+                    {i.sum}
282+                  </td>
283+                </tr>
284               ))}
285-              <th className={details ? 'right' : 'hidden'}>Best</th>
286-              <th className={details ? 'right' : 'hidden'}>Worst</th>
287-              <th className='right'>Total</th>
288-            </tr>
289-          </thead>
290-          <tbody>
291-          {leaderboard.sort(rankByHeat(rankingComp)).map(i => (
292-            <tr key={i.id}>
293-              <td className={details ? 'right' : 'hidden'}></td>
294-              <td data-title='Start Nr.' className='right'>{i.nr}</td>
295-              <td data-title='Name'>{i.firstname} {i.lastname}</td>
296-              <td data-title='Birthday' className={details ? '' : 'hidden'}>
297-                {i.birthday ? new Date(i.birthday).toLocaleDateString(locale, dateOptions) : ''}
298-              </td>
299-              <td data-title='School' className={details ? '' : 'hidden'}>{i.school}</td>
300-              {heatSelection.map(h => (
301-                <Fragment key={h.value}>
302-                  {/* list all scores from the judges seperated with '+' signs, show sum on right side */}
303-                  <td className={details ? 'right' : 'hidden'} data-title={h.label}>{formatScores(i, h)}</td>
304-                </Fragment>
305-              ))}
306-              <td className={details ? 'right' : 'hidden'} data-title='Best'>{i.bestHeat}</td>
307-              <td className={details ? 'right' : 'hidden'} data-title='Worst'>{i.worstHeat}</td>
308-              <td className='right total' data-title='Total'>{i.sum}</td>
309-            </tr>
310-          ))}
311-          </tbody>
312-        </table>
313+            </tbody>
314+          </table>
315+        </div>
316       </div>
317       <ExportForm
318         leaderboard={leaderboard}
319         heatSelection={heatSelection}
320-        rankingComp={rankingComp} />
321-      {session.auth ? <NewHeatForm
322-        leaderboard={leaderboard}
323         rankingComp={rankingComp}
324-        selectHeatRef={selectHeatRef}
325-        selectRankRef={selectRankRef}
326-        session={session}
327-      /> : ''}
328-    </div>
329+      />
330+      {session.auth ? (
331+        <NewHeatForm
332+          leaderboard={leaderboard}
333+          rankingComp={rankingComp}
334+          selectHeatRef={selectHeatRef}
335+          selectRankRef={selectRankRef}
336+          session={session}
337+        />
338+      ) : (
339+        ""
340+      )}
341+    </>
342   )
343 }
344 
345diff --git a/src/frontend/css/App.css b/src/frontend/css/App.css
346index ba26e36..ad1fc99 100644
347--- a/src/frontend/css/App.css
348+++ b/src/frontend/css/App.css
349@@ -99,10 +99,11 @@ footer span button {
350 }
351 
352 .loginForm, .exportForm {
353-  margin: 30px;
354+  margin: auto;
355+  padding: 3rem;
356 }
357 .loginForm button, .loginForm input, .exportForm button {
358-  width: 250px;
359+  width: 100%;
360   display: block;
361 }
362 
363@@ -125,6 +126,11 @@ footer span button {
364   padding: 0 20px;
365 }
366 
367+.table-container {
368+  padding-top: 1rem;
369+  overflow-x: auto;
370+}
371+
372 .Scoring ul {
373   display: flex;
374   flex-direction: row;
375@@ -214,55 +220,50 @@ td.total {
376 }
377 
378 /* https://css-tricks.com/making-tables-responsive-with-minimal-css */
379-@media(max-width: 1100px) {
380+/* Mobile / Small screen styles */
381+@media (max-width: 768px) {
382+  table.leaderboard {
383+    display: block;
384+  }
385+
386   table.leaderboard thead {
387-    left: -9999px;
388-    position: absolute;
389-    visibility: hidden;
390+    display: none;
391   }
392 
393-  table.leaderboard tr {
394-    display: flex;
395-    flex-direction: row;
396-    flex-wrap: wrap;
397-    padding: 20px 0;
398+  table.leaderboard tbody,
399+  table.leaderboard tr,
400+  table.leaderboard td {
401+    display: block;
402   }
403 
404-  table.leaderboard tr:not(:last-child) {
405-    border-bottom: 1px solid #e1e1e7;
406+  table.leaderboard tr {
407+    padding: 2rem;
408   }
409 
410   table.leaderboard td {
411-    margin: 0 -1px -1px 0;
412-    padding-top: 35px;
413-    margin-bottom: 25px;
414+    display: flex;
415+    padding: 10px;
416+    border-bottom: 1px solid #ddd;
417     position: relative;
418-    width: 35%;
419-    text-align: left !important;
420   }
421 
422-  table.leaderboard td:before {
423-    content: attr(data-title);
424-    position: absolute;
425-    top: 3px;
426-    left: 20px;
427+  table.leaderboard:not(.hide-rank) tr td:first-child::before {
428+    content: counter(rowNumber) ". Rank";
429+  }
430+
431+  table.leaderboard td[data-title]:before {
432+    content: attr(data-title) ": ";
433+    display: inline-block;
434+    width: 50%;
435+    padding-right: 1rem;
436+    text-align: right;
437+    align-content: center;
438     font-size: 0.8em;
439     text-transform: uppercase;
440     color: #b0b0b6;
441   }
442 
443-  table.leaderboard td:nth-child(-n+6) {
444-    /* background: rgb(236, 236, 236); */
445-  }
446-
447-  table td button {
448-    position: absolute;
449-    bottom: 0;
450-    height: 50px;
451-    width: 100%;
452-  }
453-
454-  table td:empty {
455-    display: none;
456+  table.leaderboard tr {
457+    border-bottom: 1px solid #ddd;
458   }
459 }
460diff --git a/src/frontend/css/index.css b/src/frontend/css/index.css
461index ae40ae5..59eaa31 100644
462--- a/src/frontend/css/index.css
463+++ b/src/frontend/css/index.css
464@@ -1,8 +1,13 @@
465 * {
466   padding: 0;
467   margin: 0;
468+  box-sizing: border-box;
469   font-family: monospace;
470-  font-size: 18px;
471+  font-size: 1.125rem;
472+}
473+
474+html, body {
475+  height: 100%;
476 }
477 
478 a {
479@@ -21,6 +26,7 @@ button, input {
480   background: white;
481   border: 1px solid #f3f2f7;
482   color: black;
483+  white-space: nowrap;
484 }
485 
486 button {
487-- 
4882.46.0
489
ps-2 by sam on 2024-10-08T22:18:28Z

change low width table design to description tables

Berchtold Samuel <samuel.berchtold@gmail.com> 2024-10-08
 index.html                   |   4 +-
 src/frontend/App.jsx         |   4 +-
 src/frontend/Leaderboard.jsx | 250 +++++++++++++++++++++--------------
 src/frontend/css/App.css     |  73 +++++-----
 src/frontend/css/index.css   |   8 +-
 5 files changed, 198 insertions(+), 141 deletions(-)
Patch
  1From 1b0a391fd2609391299e9802b835d0e467acb3b4 Mon Sep 17 00:00:00 2001
  2From: Berchtold Samuel <samuel.berchtold@gmail.com>
  3Date: Wed, 9 Oct 2024 00:06:13 +0200
  4Subject: [PATCH] change low width table design to description tables
  5
  6---
  7 index.html                   |   4 +-
  8 src/frontend/App.jsx         |   4 +-
  9 src/frontend/Leaderboard.jsx | 250 +++++++++++++++++++++--------------
 10 src/frontend/css/App.css     |  73 +++++-----
 11 src/frontend/css/index.css   |   8 +-
 12 5 files changed, 198 insertions(+), 141 deletions(-)
 13
 14diff --git a/index.html b/index.html
 15index 05d71f0..3d94e61 100644
 16--- a/index.html
 17+++ b/index.html
 18@@ -3,7 +3,7 @@
 19   <head>
 20     <meta charset="UTF-8" />
 21     <link rel="icon" type="image/x-icon" href="/favicon.ico" />
 22-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 23+    <meta name="viewport" content="width=device-width" />
 24     <link rel="apple-touch-icon" href="/logo192.png" />
 25     <!--
 26       manifest.json provides metadata used when your web app is installed on a
 27@@ -13,7 +13,7 @@
 28     <title>My Heats</title>
 29   </head>
 30   <body>
 31-    <noscript>You need to enable JavaScript to run this app.</noscript>
 32+    <noscript>Why You scared of JavaScript so much, failure.</noscript>
 33     <div id="root"></div>
 34     <script type="module" src="/src/frontend/main.jsx"></script>
 35   </body>
 36diff --git a/src/frontend/App.jsx b/src/frontend/App.jsx
 37index b8f6357..1ef94c4 100644
 38--- a/src/frontend/App.jsx
 39+++ b/src/frontend/App.jsx
 40@@ -65,7 +65,7 @@ function Layout() {
 41   }, [])
 42 
 43   return (
 44-    <Fragment>
 45+    <>
 46       <nav className={theme}>
 47         <ul>
 48           <li>
 49@@ -123,7 +123,7 @@ function Layout() {
 50           }
 51         </span>
 52       </footer>
 53-    </Fragment>
 54+    </>
 55   )
 56 }
 57 
 58diff --git a/src/frontend/Leaderboard.jsx b/src/frontend/Leaderboard.jsx
 59index 1e44ddc..b345b12 100644
 60--- a/src/frontend/Leaderboard.jsx
 61+++ b/src/frontend/Leaderboard.jsx
 62@@ -204,46 +204,51 @@ function NewHeatForm(leaderboard, rankingComp, selectHeatRef, selectRankRef, ses
 63       <p>
 64         Create new heat with top N athletes from the sorted leaderboard (<i>* required</i>).
 65       </p>
 66-      <form method='post' onSubmit={e => newHeatFromLeaderboard(
 67-          e,
 68-          leaderboard,
 69-          rankingComp,
 70-          selectHeatRef,
 71-          selectRankRef,
 72-          session,
 73-        )}>
 74-        <table>
 75-          <thead>
 76-            <tr>
 77-              <th>New heat name *</th>
 78-              <th>Location</th>
 79-              <th>Planned start</th>
 80-              <th>Include top N</th>
 81-              <td></td>
 82-            </tr>
 83-          </thead>
 84-          <tbody>
 85-            <tr>
 86-              <td data-title='New heat name *'>
 87-                <input type='text' name='name' />
 88-              </td>
 89-              <td data-title='Location'>
 90-                <input type='text' name='location' />
 91-              </td>
 92-              <td data-title='Planned start'>
 93-                <input
 94-                  type='time'
 95-                  name='planned_start' />
 96-              </td>
 97-              <td data-title='Include top N'>
 98-                <input type='number' name='size' />
 99-              </td>
100-              <td>
101-                <button type='submit'>&#43; new</button>
102-              </td>
103-            </tr>
104-          </tbody>
105-        </table>
106+      <form
107+        method="post"
108+        onSubmit={(e) =>
109+          newHeatFromLeaderboard(
110+            e,
111+            leaderboard,
112+            rankingComp,
113+            selectHeatRef,
114+            selectRankRef,
115+            session
116+          )
117+        }
118+      >
119+        <div className="table-container">
120+          <table>
121+            <thead>
122+              <tr>
123+                <th>New heat name *</th>
124+                <th>Location</th>
125+                <th>Planned start</th>
126+                <th>Include top N</th>
127+                <td></td>
128+              </tr>
129+            </thead>
130+            <tbody>
131+              <tr>
132+                <td data-title="New heat name *">
133+                  <input type="text" name="name" />
134+                </td>
135+                <td data-title="Location">
136+                  <input type="text" name="location" />
137+                </td>
138+                <td data-title="Planned start">
139+                  <input type="time" name="planned_start" />
140+                </td>
141+                <td data-title="Include top N">
142+                  <input type="number" name="size" />
143+                </td>
144+                <td>
145+                  <button type="submit">&#43; new</button>
146+                </td>
147+              </tr>
148+            </tbody>
149+          </table>
150+        </div>
151       </form>
152     </div>
153   )
154@@ -351,85 +356,130 @@ function Leaderboard({session}) {
155   }, [heatSelection]);
156 
157   return (
158-    <div>
159-      <div className='Leaderboard'>
160+    <>
161+      <div className="Leaderboard">
162         <header>
163-          <button disabled={!loading} className='loading' >↺ loading</button>
164-          <button className={`show-details ${details ? 'toggled' : ''}`} onClick={() => showDetails(!details)}>
165-            <div className='thumb'></div>
166-            <span>{details ? 'less' : 'more'}</span>
167+          <button disabled={!loading} className="loading">
168+            ↺ loading
169           </button>
170-          <label htmlFor='heat'>Heats to display</label>
171+          <button
172+            className={`show-details ${details ? "toggled" : ""}`}
173+            onClick={() => showDetails(!details)}
174+          >
175+            <div className="thumb"></div>
176+            <span>{details ? "less" : "more"}</span>
177+          </button>
178+          <label htmlFor="heat">Heats to display</label>
179           <Select
180             closeMenuOnSelect={false}
181             isMulti
182             options={heatOpts}
183-            onChange={h => setHeatSelection(h)}
184+            onChange={(h) => setHeatSelection(h)}
185             ref={selectHeatRef}
186-            id='heat' />
187-           <label htmlFor='rank' className={details ? '' : 'hidden'}>Rank by</label>
188-           <Select
189+            id="heat"
190+          />
191+          <label htmlFor="rank" className={details ? "" : "hidden"}>
192+            Rank by
193+          </label>
194+          <Select
195             closeMenuOnSelect={false}
196             isMulti
197             options={rankOpts}
198             defaultValue={rankOpts[0].options[3]}
199-            onChange={h => setRankingComp(h)}
200+            onChange={(h) => setRankingComp(h)}
201             ref={selectRankRef}
202-            className={details ? '' : 'hidden'}
203-            id='rank' />
204+            className={details ? "" : "hidden"}
205+            id="rank"
206+          />
207         </header>
208-        <table className={details ? 'leaderboard' : 'hide-rank'}>
209-          <thead>
210-            <tr>
211-              <th className={details ? 'right' : 'hidden'}>Rank</th>
212-              <th className='right'>Start Nr.</th>
213-              <th>Name</th>
214-              <th className={details ? '' : 'hidden'}>Birthday</th>
215-              <th className={details ? '' : 'hidden'}>School</th>
216-              {heatSelection.map(h => (
217-                <th className={details ? 'right' : 'hidden'} key={h.value}>{h.label}</th>
218+        <div className="table-container">
219+          <table className={details ? "leaderboard" : "hide-rank"}>
220+            <thead>
221+              <tr>
222+                <th className={details ? "right" : "hidden"}>Rank</th>
223+                <th className="right">Start Nr.</th>
224+                <th>Name</th>
225+                <th className={details ? "" : "hidden"}>Birthday</th>
226+                <th className={details ? "" : "hidden"}>School</th>
227+                {heatSelection.map((h) => (
228+                  <th className={details ? "right" : "hidden"} key={h.value}>
229+                    {h.label}
230+                  </th>
231+                ))}
232+                <th className={details ? "right" : "hidden"}>Best</th>
233+                <th className={details ? "right" : "hidden"}>Worst</th>
234+                <th className="right">Total</th>
235+              </tr>
236+            </thead>
237+            <tbody>
238+              {leaderboard.sort(rankByHeat(rankingComp)).map((i) => (
239+                <tr key={i.id}>
240+                  <td className={details ? "right" : "hidden"}></td>
241+                  <td data-title="Start Nr." className="right">
242+                    {i.nr}
243+                  </td>
244+                  <td data-title="Name">
245+                    {i.firstname} {i.lastname}
246+                  </td>
247+                  <td data-title="Birthday" className={details ? "" : "hidden"}>
248+                    {i.birthday
249+                      ? new Date(i.birthday).toLocaleDateString(
250+                          locale,
251+                          dateOptions
252+                        )
253+                      : ""}
254+                  </td>
255+                  <td data-title="School" className={details ? "" : "hidden"}>
256+                    {i.school}
257+                  </td>
258+                  {heatSelection.map((h) => (
259+                    // list all scores from the judges seperated with '+' signs, show sum on right side
260+                    <td
261+                      key={h.value}
262+                      className={details ? "right" : "hidden"}
263+                      data-title={h.label}
264+                    >
265+                      {formatScores(i, h)}
266+                    </td>
267+                  ))}
268+                  <td
269+                    className={details ? "right" : "hidden"}
270+                    data-title="Best"
271+                  >
272+                    {i.bestHeat}
273+                  </td>
274+                  <td
275+                    className={details ? "right" : "hidden"}
276+                    data-title="Worst"
277+                  >
278+                    {i.worstHeat}
279+                  </td>
280+                  <td className="right total" data-title="Total">
281+                    {i.sum}
282+                  </td>
283+                </tr>
284               ))}
285-              <th className={details ? 'right' : 'hidden'}>Best</th>
286-              <th className={details ? 'right' : 'hidden'}>Worst</th>
287-              <th className='right'>Total</th>
288-            </tr>
289-          </thead>
290-          <tbody>
291-          {leaderboard.sort(rankByHeat(rankingComp)).map(i => (
292-            <tr key={i.id}>
293-              <td className={details ? 'right' : 'hidden'}></td>
294-              <td data-title='Start Nr.' className='right'>{i.nr}</td>
295-              <td data-title='Name'>{i.firstname} {i.lastname}</td>
296-              <td data-title='Birthday' className={details ? '' : 'hidden'}>
297-                {i.birthday ? new Date(i.birthday).toLocaleDateString(locale, dateOptions) : ''}
298-              </td>
299-              <td data-title='School' className={details ? '' : 'hidden'}>{i.school}</td>
300-              {heatSelection.map(h => (
301-                <Fragment key={h.value}>
302-                  {/* list all scores from the judges seperated with '+' signs, show sum on right side */}
303-                  <td className={details ? 'right' : 'hidden'} data-title={h.label}>{formatScores(i, h)}</td>
304-                </Fragment>
305-              ))}
306-              <td className={details ? 'right' : 'hidden'} data-title='Best'>{i.bestHeat}</td>
307-              <td className={details ? 'right' : 'hidden'} data-title='Worst'>{i.worstHeat}</td>
308-              <td className='right total' data-title='Total'>{i.sum}</td>
309-            </tr>
310-          ))}
311-          </tbody>
312-        </table>
313+            </tbody>
314+          </table>
315+        </div>
316       </div>
317       <ExportForm
318         leaderboard={leaderboard}
319         heatSelection={heatSelection}
320-        rankingComp={rankingComp} />
321-      {session.auth ? <NewHeatForm
322-        leaderboard={leaderboard}
323         rankingComp={rankingComp}
324-        selectHeatRef={selectHeatRef}
325-        selectRankRef={selectRankRef}
326-        session={session}
327-      /> : ''}
328-    </div>
329+      />
330+      {session.auth ? (
331+        <NewHeatForm
332+          leaderboard={leaderboard}
333+          rankingComp={rankingComp}
334+          selectHeatRef={selectHeatRef}
335+          selectRankRef={selectRankRef}
336+          session={session}
337+        />
338+      ) : (
339+        ""
340+      )}
341+    </>
342   )
343 }
344 
345diff --git a/src/frontend/css/App.css b/src/frontend/css/App.css
346index ba26e36..ad1fc99 100644
347--- a/src/frontend/css/App.css
348+++ b/src/frontend/css/App.css
349@@ -99,10 +99,11 @@ footer span button {
350 }
351 
352 .loginForm, .exportForm {
353-  margin: 30px;
354+  margin: auto;
355+  padding: 3rem;
356 }
357 .loginForm button, .loginForm input, .exportForm button {
358-  width: 250px;
359+  width: 100%;
360   display: block;
361 }
362 
363@@ -125,6 +126,11 @@ footer span button {
364   padding: 0 20px;
365 }
366 
367+.table-container {
368+  padding-top: 1rem;
369+  overflow-x: auto;
370+}
371+
372 .Scoring ul {
373   display: flex;
374   flex-direction: row;
375@@ -214,55 +220,50 @@ td.total {
376 }
377 
378 /* https://css-tricks.com/making-tables-responsive-with-minimal-css */
379-@media(max-width: 1100px) {
380+/* Mobile / Small screen styles */
381+@media (max-width: 768px) {
382+  table.leaderboard {
383+    display: block;
384+  }
385+
386   table.leaderboard thead {
387-    left: -9999px;
388-    position: absolute;
389-    visibility: hidden;
390+    display: none;
391   }
392 
393-  table.leaderboard tr {
394-    display: flex;
395-    flex-direction: row;
396-    flex-wrap: wrap;
397-    padding: 20px 0;
398+  table.leaderboard tbody,
399+  table.leaderboard tr,
400+  table.leaderboard td {
401+    display: block;
402   }
403 
404-  table.leaderboard tr:not(:last-child) {
405-    border-bottom: 1px solid #e1e1e7;
406+  table.leaderboard tr {
407+    padding: 2rem;
408   }
409 
410   table.leaderboard td {
411-    margin: 0 -1px -1px 0;
412-    padding-top: 35px;
413-    margin-bottom: 25px;
414+    display: flex;
415+    padding: 10px;
416+    border-bottom: 1px solid #ddd;
417     position: relative;
418-    width: 35%;
419-    text-align: left !important;
420   }
421 
422-  table.leaderboard td:before {
423-    content: attr(data-title);
424-    position: absolute;
425-    top: 3px;
426-    left: 20px;
427+  table.leaderboard:not(.hide-rank) tr td:first-child::before {
428+    content: counter(rowNumber) ". Rank";
429+  }
430+
431+  table.leaderboard td[data-title]:before {
432+    content: attr(data-title) ": ";
433+    display: inline-block;
434+    width: 50%;
435+    padding-right: 1rem;
436+    text-align: right;
437+    align-content: center;
438     font-size: 0.8em;
439     text-transform: uppercase;
440     color: #b0b0b6;
441   }
442 
443-  table.leaderboard td:nth-child(-n+6) {
444-    /* background: rgb(236, 236, 236); */
445-  }
446-
447-  table td button {
448-    position: absolute;
449-    bottom: 0;
450-    height: 50px;
451-    width: 100%;
452-  }
453-
454-  table td:empty {
455-    display: none;
456+  table.leaderboard tr {
457+    border-bottom: 1px solid #ddd;
458   }
459 }
460diff --git a/src/frontend/css/index.css b/src/frontend/css/index.css
461index ae40ae5..59eaa31 100644
462--- a/src/frontend/css/index.css
463+++ b/src/frontend/css/index.css
464@@ -1,8 +1,13 @@
465 * {
466   padding: 0;
467   margin: 0;
468+  box-sizing: border-box;
469   font-family: monospace;
470-  font-size: 18px;
471+  font-size: 1.125rem;
472+}
473+
474+html, body {
475+  height: 100%;
476 }
477 
478 a {
479@@ -21,6 +26,7 @@ button, input {
480   background: white;
481   border: 1px solid #f3f2f7;
482   color: black;
483+  white-space: nowrap;
484 }
485 
486 button {
487-- 
4882.46.0
489