Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions e2e/reflow.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,12 @@ test.describe('on a touch screen', () => {
});
}

test('the icon colour swatches stay packed at 744px', async ({ page, request }) => {
test('the icon colour swatches stay packed on a wide phone', async ({ page, request }) => {
await seedConfig(request, { items: [app('one', 'One')] });
await dismissSetupPrompt(request);
await page.setViewportSize({ width: 744, height: 900 });
await page.setViewportSize({ width: 430, height: 932 });
await page.goto('/admin/#dashboard');
await expect(page.locator('html')).toHaveClass(/\bis-mobile\b/);
await page.locator('#al [data-act="edit"]').first().click();
await page.locator('#icon-col-style-btn').click();
await page.locator('#icon-col-style-list [data-val="color"]').click();
Expand Down
6 changes: 3 additions & 3 deletions ui/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
<title>Stackyard Settings</title>
<script src="/js/mobile-class.js?v=7560429d"></script>
<script src="/js/admin-theme.js?v=66565254"></script>
<script src="/js/page-zoom.js?v=f28b11d2"></script>
<link rel="stylesheet" href="/css/tokens.css?v=1cb818dc">
<link rel="stylesheet" href="/css/admin.css?v=48ec8aeb">
<script src="/js/page-zoom.js?v=01ccc9cf"></script>
<link rel="stylesheet" href="/css/tokens.css?v=7ea2122b">
<link rel="stylesheet" href="/css/admin.css?v=31e56730">
<link rel="stylesheet" href="/css/widget-config-form.css?v=53b91d5f">
<link rel="stylesheet" href="/css/config-recovery.css?v=b6b3f6bb">
</head>
Expand Down
1 change: 1 addition & 0 deletions ui/css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ html.is-mobile .m-back{display:inline-flex;align-items:center;justify-content:ce
html.is-mobile .m-back:active{opacity:.7}
/* Under 16px, iOS zooms the page on focus and does not zoom back. */
html.is-mobile .kv-k,html.is-mobile .kv-v{font-size:var(--fs-body);line-height:var(--lh-body);letter-spacing:var(--tr-body)}
@media (orientation:landscape) and (max-height:500px) and (pointer:coarse){.kv-k,.kv-v{font-size:var(--fs-body);line-height:var(--lh-body);letter-spacing:var(--tr-body)}}
html.is-mobile body.authed .mtabbar{
display:flex;position:fixed;left:21px;right:21px;bottom:21px;
z-index:8000;justify-content:space-around;align-items:center;gap:2px;
Expand Down
26 changes: 14 additions & 12 deletions ui/css/tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -231,18 +231,20 @@ html[data-theme="light"] {

Tracking is republished per step, not inherited. There is one absolute
tracking per style, so the em that reproduces it at 17 does not at 15. */
html.type-small:not(.is-mobile) {
--fs-large-title:32px; --lh-large-title:1.2188; --tr-large-title:0.0125em; /* 32/39 */
--fs-title-1:26px; --lh-title-1:1.2308; --tr-title-1:0.0146em; /* 26/32 */
--fs-title-2:20px; --lh-title-2:1.2; --tr-title-2:-0.013em; /* 20/24 */
--fs-title-3:18px; --lh-title-3:1.2778; --tr-title-3:-0.025em; /* 18/23 */
--fs-headline:15px; --lh-headline:1.3333; --tr-headline:-0.0287em; /* 15/20 */
--fs-body:15px; --lh-body:1.3333; --tr-body:-0.0287em; /* 15/20 */
--fs-callout:14px; --lh-callout:1.3571; --tr-callout:-0.0221em; /* 14/19 */
--fs-subheadline:13px; --lh-subheadline:1.3846; --tr-subheadline:-0.0177em; /* 13/18 */
--fs-footnote:12px; --lh-footnote:1.3333; --tr-footnote:-0.0067em; /* 12/16 */
--fs-caption-1:11px; --lh-caption-1:1.1818; --tr-caption-1:0; /* 11/13 */
--fs-caption-2:11px; --lh-caption-2:1.1818; --tr-caption-2:0.0055em; /* 11/13 */
@media not all and (orientation:landscape) and (max-height:500px) and (pointer:coarse) {
html.type-small:not(.is-mobile) {
--fs-large-title:32px; --lh-large-title:1.2188; --tr-large-title:0.0125em; /* 32/39 */
--fs-title-1:26px; --lh-title-1:1.2308; --tr-title-1:0.0146em; /* 26/32 */
--fs-title-2:20px; --lh-title-2:1.2; --tr-title-2:-0.013em; /* 20/24 */
--fs-title-3:18px; --lh-title-3:1.2778; --tr-title-3:-0.025em; /* 18/23 */
--fs-headline:15px; --lh-headline:1.3333; --tr-headline:-0.0287em; /* 15/20 */
--fs-body:15px; --lh-body:1.3333; --tr-body:-0.0287em; /* 15/20 */
--fs-callout:14px; --lh-callout:1.3571; --tr-callout:-0.0221em; /* 14/19 */
--fs-subheadline:13px; --lh-subheadline:1.3846; --tr-subheadline:-0.0177em; /* 13/18 */
--fs-footnote:12px; --lh-footnote:1.3333; --tr-footnote:-0.0067em; /* 12/16 */
--fs-caption-1:11px; --lh-caption-1:1.1818; --tr-caption-1:0; /* 11/13 */
--fs-caption-2:11px; --lh-caption-2:1.1818; --tr-caption-2:0.0055em; /* 11/13 */
}
}

/* ── Accessibility preferences ───────────────────────────────────────────────
Expand Down
4 changes: 2 additions & 2 deletions ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
<link rel="icon" type="image/png" href="/icons/favicon.png">

<script src="/js/admin-theme.js?v=66565254"></script>
<script src="/js/page-zoom.js?v=f28b11d2"></script>
<script src="/js/page-zoom.js?v=01ccc9cf"></script>
<title>Stackyard</title>
<link rel="stylesheet" href="/css/tokens.css?v=1cb818dc">
<link rel="stylesheet" href="/css/tokens.css?v=7ea2122b">
<link rel="stylesheet" href="/css/dashboard.css?v=dbc66d89">
<link rel="stylesheet" href="/css/config-recovery.css?v=b6b3f6bb">
</head>
Expand Down
8 changes: 5 additions & 3 deletions ui/js/page-zoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
if (!vv) return;
const root = document.documentElement;
let width = -1;
/* Read the scale only when the layout width changes. A pinch changes the
scale but not the width, and must not move the insets. */
/* The scale is the page zoom only while the visual viewport spans the
layout width. A pinch narrows it, so a width change during a pinch is
read when the pinch ends. */
const read = () => {
if (root.clientWidth === width) return;
if (root.clientWidth === width || vv.width < root.clientWidth - 2) return;
width = root.clientWidth;
root.style.setProperty('--inset-scale', String(1 / vv.scale));
};
read();
window.addEventListener('resize', read);
vv.addEventListener('resize', read);
} catch {}
})();
16 changes: 16 additions & 0 deletions ui/test/admin-touch.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,22 @@ test('the tap highlight is cleared once, on the document', () => {

test('the header fields are body size on a phone, so focusing one does not zoom', () => {
assert.match(css, /html\.is-mobile \.kv-k,html\.is-mobile \.kv-v\{font-size:var\(--fs-body\)/);
assert.match(
css,
/@media \(orientation:landscape\) and \(max-height:500px\) and \(pointer:coarse\)\{\.kv-k,\.kv-v\{font-size:var\(--fs-body\)/,
'a sideways phone keeps the desktop layout, so the phone rule alone misses it',
);
});

test('a sideways phone keeps the default type step, so no field is under 16px', () => {
const tokens = read('css/tokens.css').replace(/\/\*[\s\S]*?\*\//g, '');
const at = tokens.indexOf('html.type-small:not(.is-mobile)');
const media = tokens.lastIndexOf('@media', at);
assert.ok(media >= 0 && tokens.indexOf('}', media) > at, 'the small step must sit inside a media query');
assert.equal(
tokens.slice(media, tokens.indexOf('{', media)).trim(),
'@media not all and (orientation:landscape) and (max-height:500px) and (pointer:coarse)',
);
});

test('a long title wraps and the header buttons keep their size', () => {
Expand Down
30 changes: 26 additions & 4 deletions ui/test/page-zoom.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,23 @@ const read = rel => readFileSync(fileURLToPath(new URL(rel, import.meta.url)), '
const script = read('../js/page-zoom.js');

/** Runs page-zoom.js against a stand-in page. */
/** Runs page-zoom.js against a stand-in page. `pinch` is the zoom on top of the
page zoom: it raises the scale and narrows the visual viewport. */
function page({ width, scale }) {
const props = new Map();
const listeners = [];
const root = { clientWidth: width, style: { setProperty: (k, v) => props.set(k, v) } };
const vv = { scale };
const vv = { scale, width, addEventListener: (type, fn) => type === 'resize' && listeners.push(fn) };
const window = { visualViewport: vv, addEventListener: (type, fn) => type === 'resize' && listeners.push(fn) };
vm.runInNewContext(script, { window, document: { documentElement: root } });
let zoom = scale;
return {
props,
resize({ width: w = root.clientWidth, scale: s = vv.scale }) {
resize({ width: w = root.clientWidth, scale: s = zoom, pinch = 1 }) {
zoom = s;
root.clientWidth = w;
vv.scale = s;
vv.scale = s * pinch;
vv.width = w / pinch;
for (const fn of listeners) fn();
},
};
Expand All @@ -40,10 +45,27 @@ test('at 150% page zoom each inset shrinks to two thirds', () => {

test('a pinch leaves the insets alone', () => {
const p = page({ width: 393, scale: 1 });
p.resize({ scale: 2.5 });
p.resize({ pinch: 2.5 });
assert.equal(p.props.get('--inset-scale'), '1');
});

test('turning the phone while pinched waits for the pinch to end', () => {
const p = page({ width: 393, scale: 1 });
p.resize({ pinch: 2 });
p.resize({ width: 852, pinch: 2 });
assert.equal(p.props.get('--inset-scale'), '1');
p.resize({ width: 852, pinch: 1 });
assert.equal(p.props.get('--inset-scale'), '1');
});

test('turning the phone while pinched at 50% page zoom keeps the page zoom', () => {
const p = page({ width: 786, scale: 0.5 });
p.resize({ width: 1704, pinch: 3 });
assert.equal(p.props.get('--inset-scale'), '2');
p.resize({ width: 1704, pinch: 1 });
assert.equal(p.props.get('--inset-scale'), '2');
});

test('a page zoom change after load is picked up', () => {
const p = page({ width: 393, scale: 1 });
p.resize({ width: 786, scale: 0.5 });
Expand Down
Loading