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
6 changes: 5 additions & 1 deletion docs/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

This dependency-free preview contains a homepage and one detailed example report for Wordle!. It uses static example data only: there is no live app search, service integration, or report queue.

Open `index.html` directly in a browser. For HTTP review, serve the `docs/design/` directory with any static server scoped to that directory; the parent preview server uses `http://localhost:4318/` in this session.
Open `index.html` directly in a browser. For HTTP review, serve the `docs/design/` directory with any static server scoped to that directory.

The mobile refinement retains the existing palette, typography and shared responsive pages. It reduces header and hero spacing on phones, keeps example reports ahead of research statistics, and adds a plain-language report introduction with expandable analysis metadata. Sample-only entries remain visibly labelled on mobile. The live homepage now uses only its main search form; other live pages keep the header search.

Browser checks covered 320px, 390px and 1280px widths without page-level horizontal overflow, app search, combined tracker filters and the metadata disclosure. Mobile form controls retain 16px text and at least 44px height. This is browser viewport validation, not a physical iPhone Safari test.

The preview was checked with `node --check docs/design/preview.js`. Only Wordle! has a local detailed report; Merge Dragons! and Subway Surfers are clearly labelled example entries.
60 changes: 43 additions & 17 deletions docs/design/design.css
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ input::placeholder {

.back-link {
color: var(--muted);
gap: .3rem;
font-size: 0.85rem;
text-decoration: none;
}
Expand Down Expand Up @@ -832,6 +833,16 @@ input::placeholder {
margin: 0;
}

.report-takeaway { padding: 1.75rem 0; max-width: 720px; }
.report-takeaway h2 { margin-bottom: .8rem; }
.report-takeaway p:not(.eyebrow) { color: #3c514d; margin-bottom: .8rem; }
.report-takeaway a { display: inline-flex; align-items: center; min-height: 44px; gap: .5rem; }
.report-disclosure { border-bottom: 1px solid var(--border); margin-bottom: 2.5rem; }
.report-disclosure > summary { cursor: pointer; padding: 1rem 0; min-height: 56px; font-weight: 750; }
.report-disclosure > summary span { color: var(--muted); font-size: .85rem; font-weight: 400; margin-left: .5rem; }
.report-disclosure .stale-note { margin: 1rem 0; }
.report-disclosure .metadata { border-bottom: 0; padding-top: .5rem; }

@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
Expand Down Expand Up @@ -915,8 +926,9 @@ input::placeholder {

.site-header {
min-height: 0;
padding-bottom: 1.25rem;
padding-top: 1.25rem;
padding-bottom: .5rem;
padding-top: .75rem;
gap: 0;
}

.preview-strip__inner {
Expand All @@ -930,11 +942,27 @@ input::placeholder {
}

h1 {
font-size: clamp(2.55rem, 13vw, 3.5rem);
font-size: clamp(2rem, 9vw, 2.8rem);
line-height: 1.05;
margin-bottom: 1rem;
}

.hero { padding-top: 1.25rem; padding-bottom: 1.75rem; }
.hero .eyebrow { margin-bottom: .75rem; }
.preview-strip__inner { gap: 0; padding: .25rem 0; }
.preview-strip nav a, .site-nav a, .site-footer nav a, .wordmark, .back-link {
display: inline-flex;
align-items: center;
min-height: 44px;
}
.search-form { gap: .75rem; padding-top: 1rem; }
.field-hint { max-width: none; }
.section-heading { margin-bottom: 1rem; }
.report-disclosure > summary span { display: block; margin: .15rem 0 0 1rem; }

.hero__intro {
margin-bottom: 2.6rem;
margin-bottom: 1.5rem;
font-size: 1rem;
}

.search-form__controls {
Expand All @@ -947,8 +975,8 @@ input::placeholder {
}

.featured {
padding-bottom: 4.5rem;
padding-top: 2.6rem;
padding-bottom: 2.5rem;
padding-top: 1.5rem;
}

.app-row {
Expand All @@ -968,10 +996,9 @@ input::placeholder {
font-size: 0.78rem;
}

.app-row__arrow,
.app-row__tag {
display: none;
}
.app-row__arrow { display: none; }
.app-row__tag { grid-column: 2 / -1; justify-self: start; font-size: .7rem; }
.app-row__name { overflow-wrap: anywhere; }

.metrics {
gap: 1.5rem 0.9rem;
Expand Down Expand Up @@ -1003,14 +1030,14 @@ input::placeholder {
}

.report-page {
padding-top: 1.4rem;
padding-top: .25rem;
}

.report-header {
gap: 1rem;
grid-template-columns: 64px minmax(0, 1fr);
padding-bottom: 2.2rem;
padding-top: 2.4rem;
padding-bottom: 1.25rem;
padding-top: 1rem;
}

.app-tile--large {
Expand All @@ -1025,14 +1052,13 @@ input::placeholder {
}

.report-header__back {
grid-column: 2;
justify-self: start;
display: none;
}

.summary {
gap: 1.2rem 0.8rem;
padding-bottom: 1.9rem;
padding-top: 1.9rem;
padding-bottom: 1.25rem;
padding-top: .25rem;
}

.summary-item strong {
Expand Down
6 changes: 3 additions & 3 deletions docs/design/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
<a class="skip-link" href="#main-content">Skip to content</a>
<div class="preview-strip">
<div class="site-width preview-strip__inner">
<span>Design preview · Example data from 8 September 2026</span>
<span>Mobile design preview · Example data from 8 September 2026</span>
<nav aria-label="Preview pages">
<a href="index.html">Homepage</a>
<a href="index.html" aria-current="page">Homepage</a>
<a href="report.html">App report</a>
</nav>
</div>
Expand All @@ -36,7 +36,7 @@
<main id="main-content">
<section class="hero site-width" aria-labelledby="hero-title">
<p class="eyebrow">Independent app privacy research</p>
<h1 id="hero-title">A closer look at<br>your iPhone apps.</h1>
<h1 id="hero-title">A closer look at your iPhone apps.</h1>
<p class="hero__intro">Find the tracking software built into iOS apps, and the companies behind it.</p>

<form class="search-form" id="search" role="search" aria-labelledby="search-title">
Expand Down
12 changes: 11 additions & 1 deletion docs/design/report.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<a class="skip-link" href="#main-content">Skip to content</a>
<div class="preview-strip">
<div class="site-width preview-strip__inner">
<span>Design preview · Example data from 8 September 2026</span>
<span>Mobile design preview · Example data from 8 September 2026</span>
<nav aria-label="Preview pages">
<a href="index.html">Homepage</a>
<a href="report.html" aria-current="page">App report</a>
Expand Down Expand Up @@ -47,20 +47,30 @@ <h1 id="report-title">Wordle!</h1>
<a class="report-header__back" href="index.html#search">Back to search <span aria-hidden="true">↗</span></a>
</section>

<section class="report-takeaway" aria-labelledby="takeaway-title">
<p class="eyebrow">At a glance</p>
<h2 id="takeaway-title">30 trackers found in the app’s code.</h2>
<p>These are recognised software components, associated with 25 companies. Their presence does not establish that the app tracked you or sent your data.</p>
<a href="#trackers-title">Explore the detected trackers <span aria-hidden="true">↓</span></a>
</section>

<section class="summary" aria-label="Report summary">
<div class="summary-item"><strong>30</strong><span>detected trackers</span></div>
<div class="summary-item"><strong>25</strong><span>associated companies</span></div>
<div class="summary-item"><strong>5</strong><span>declared permissions</span></div>
<p class="summary-note">This preview identifies embedded tracker signatures. It does not report observed network traffic.</p>
</section>

<details class="report-disclosure">
<summary>Analysis details <span>Version, dates and limitations</span></summary>
<section class="metadata" aria-label="Report metadata">
<div><span>Analysed version</span><strong>Not recorded in this preview</strong></div>
<div><span>Analysis date</span><strong>12 May 2026</strong></div>
<div><span>Last known store version</span><strong>1.37.0</strong></div>
<div><span>Store metadata checked</span><strong>7 July 2023</strong></div>
</section>
<p class="stale-note"><strong>Metadata context.</strong> Store details are retained from the last recorded check and may be stale. They do not verify the current app version or current behaviour.</p>
</details>

<div class="report-layout">
<section class="trackers" aria-labelledby="trackers-title">
Expand Down
14 changes: 13 additions & 1 deletion test/websiteViews.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ test('production templates preserve the principal page states and data', () => {
assert.match(home, /not random/);
assert.match(home, /action="\/search"/);
assert.match(home, /name="search"/);
assert.equal((home.match(/role="search"/g) || []).length, 1);
assert.doesNotMatch(home, /id="nav-search"/);
assert.match(home, /Example App/);

const success = render('form.pug', reportBase);
Expand Down Expand Up @@ -147,8 +149,9 @@ test('production templates preserve the principal page states and data', () => {
});

test('search, directory, lookup, statistics, about, request and error states render safely', () => {
const emptySearch = render('form.pug', { data: { search: 'nothing' }, searchResults: [] });
const emptySearch = render('form.pug', { currentPath: '/search', data: { search: 'nothing' }, searchResults: [] });
assertSingleH1(emptySearch, 'empty search');
assert.match(emptySearch, /id="nav-search"/);
assert.match(emptySearch, /No apps found for/);
assert.match(emptySearch, /No matching free app/);

Expand Down Expand Up @@ -231,6 +234,15 @@ test('Danish pages expose the language toggle and translate report labels and co
languageUrl: (target) => languageUrl('/trackers', target),
localUrl: (path) => localUrl(path, 'da', '/trackers')
};
const home = render('form.pug', {
...danish,
currentPath: '/',
localUrl: (path) => localUrl(path, 'da', '/'),
languageUrl: (target) => languageUrl('/', target)
});
assert.equal((home.match(/role="search"/g) || []).length, 1);
assert.doesNotMatch(home, /id="nav-search"/);
assert.match(home, /action="\/da\/search"/);
const directory = render('directory.pug', {
...danish,
kind: 'tracker',
Expand Down
9 changes: 5 additions & 4 deletions views/layout.pug
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ html(lang=localeCode === 'da' ? 'da-DK' : 'en-GB')
a(href=localize('/companies') class=current.startsWith('/company') || current === '/companies' ? 'is-current' : undefined aria-current=current.startsWith('/company') || current === '/companies' ? 'page' : undefined)= translate('Companies')
a(href=localize('/statistics') class=current === '/statistics' ? 'is-current' : undefined aria-current=current === '/statistics' ? 'page' : undefined)= translate('Statistics')
a(href=localize('/about') class=current === '/about' ? 'is-current' : undefined aria-current=current === '/about' ? 'page' : undefined)= translate('About')
form.nav-search(action=localize('/search') method='GET' role='search')
label.sr-only(for='nav-search')= translate('Search apps')
input#nav-search(type='search' name='search' value=searchData.search || '' placeholder=translate('Search apps placeholder') autocomplete='off')
button.button.button--small(type='submit')= translate('Search')
if current !== '/'
form.nav-search(action=localize('/search') method='GET' role='search')
label.sr-only(for='nav-search')= translate('Search apps')
input#nav-search(type='search' name='search' value=searchData.search || '' placeholder=translate('Search apps placeholder') autocomplete='off')
button.button.button--small(type='submit')= translate('Search')
nav.language-toggle(aria-label=translate('Language'))
a(href=languageHref('en') class=localeCode === 'en' ? 'is-current' : undefined aria-current=localeCode === 'en' ? 'true' : undefined)= translate('English')
a(href=languageHref('da') class=localeCode === 'da' ? 'is-current' : undefined aria-current=localeCode === 'da' ? 'true' : undefined)= translate('Dansk')
Expand Down
Loading