From d28f69d93a183a945570ad7650b94d54255bd226 Mon Sep 17 00:00:00 2001 From: Timothy Allen Date: Sat, 3 Oct 2026 11:53:47 +0200 Subject: [PATCH] Show the story sort headings as chips on narrow screens --- bindery/static/style.css | 33 ++++++++++++++++++++++++++++++--- tests/js/layout.test.mjs | 24 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/bindery/static/style.css b/bindery/static/style.css index 627e51c..1a4a2e4 100644 --- a/bindery/static/style.css +++ b/bindery/static/style.css @@ -875,18 +875,45 @@ textarea { display: block; } + /* The column headings become a row of sort chips. The chip of the sorted column is tinted and + shows the direction. The other chips show no arrow. */ .stories-table thead tr { display: flex; flex-wrap: wrap; - gap: 8px 16px; - margin-bottom: 8px; + gap: 6px; + margin-bottom: 10px; + } + + .stories-table thead tr::before { + content: 'Sort by'; + flex-basis: 100%; + color: var(--muted); + font-size: var(--text-sm); } .stories-table th { - padding: 4px 0; + padding: 0; border: 0; } + .stories-table th button { + inline-size: auto; + padding: 3px 10px; + border: 1px solid var(--border); + border-radius: 999px; + background: var(--surface); + font-size: var(--text-sm); + } + + .stories-table th[aria-sort='none'] button::after { + display: none; + } + + .stories-table th:not([aria-sort='none']) button { + background: var(--info-bg); + color: var(--info); + } + .stories-table tbody tr, .chapters-table tbody tr { border: 1px solid var(--border); diff --git a/tests/js/layout.test.mjs b/tests/js/layout.test.mjs index 901e365..f71bd3b 100644 --- a/tests/js/layout.test.mjs +++ b/tests/js/layout.test.mjs @@ -114,6 +114,30 @@ test('sortable headings show direction and remain usable above story cards on na assert.doesNotMatch(css, /\.stories-table thead\s*\{[^}]*clip-path/); }); +test('on narrow screens the headings are sort chips, and only the sorted chip is tinted with an arrow', () => { + const start = css.indexOf('@media (max-width: 600px)', css.indexOf('.stories-table th button')); + const mobile = css.slice(start); + assert.equal(declarations('.stories-table thead tr', mobile)['flex-wrap'], 'wrap'); + const caption = declarations('.stories-table thead tr::before', mobile); + assert.equal(caption.content, "'Sort by'"); + assert.equal(caption['flex-basis'], '100%'); + const chip = declarations('.stories-table th button', mobile); + assert.equal(chip['border-radius'], '999px'); + assert.equal(chip['inline-size'], 'auto'); + assert.equal(chip.padding, '3px 10px'); + assert.equal(chip['font-size'], 'var(--text-sm)'); + assert.equal(chip.background, 'var(--surface)'); + assert.equal(declarations(".stories-table th[aria-sort='none'] button::after", mobile).display, 'none'); + const sorted = declarations(".stories-table th:not([aria-sort='none']) button", mobile); + assert.equal(sorted.background, 'var(--info-bg)'); + assert.equal(sorted.color, 'var(--info)'); + assert.equal(sorted['border-color'], undefined, 'The sorted chip keeps the soft border'); + // The wide layout keeps plain headings. + const wide = css.slice(0, start); + assert.doesNotMatch(wide, /\.stories-table thead tr::before/); + assert.equal(declarations('.stories-table th button', wide).border, '0'); +}); + test('sortable headings fill the column with one clickable highlight without shifting layout', () => { assert.equal(declarations('.stories-table th').padding, '0'); const button = declarations('.stories-table th button');