Appearance
Accessibility
This is the reason the project exists, not a pass made over it afterwards.
Every polar-alignment app on the stores costs money and quietly assumes a body that cooperates: that you can hold a phone steady, crouch behind an eyepiece, hit a small target in the dark, and read dim grey text on black. Plenty of astronomers can do none of those things. This app is free, and it is built for them first.
Input model
Assume the least capable input, not the most:
- Nothing that only a gesture can do. The sky view can be dragged with a finger, tapped to centre, and its arrows held to keep moving — and every one of those has a button that reaches exactly the same places: the arrow pad goes everywhere the drag goes, through the same function and the same limits. The rule, in its author's words: gestures are fine "as long as a user has a button or easy accessible option … as long as no feature is available for only non-disabled users." No pinch, no two-finger anything, nothing needs two hands. The map takes the pointer only while a hand is steering it — in Auto Mode a finger on the map still scrolls the page, and the page always scrolls from anywhere that is not the map.
- No chords. Nothing needs a modifier key held while another is pressed.
- Big targets with big gaps. Minimum 3.4 rem, full-width buttons.
- Targets never move. Values update in place and nothing reflows under the pointer, because re-acquiring a target that has shifted is expensive for someone using a mouthstick or a trackball.
- Nothing is timed. No control disappears, collapses or advances on its own.
Every button says what pressing it does
One rule, no exceptions: a label is an imperative verb phrase naming the action. Not the state you are in, not the name of the thing, not a question.
| Instead of | It says |
|---|---|
Dark Mode | Use Night Mode |
Constellations: on | Hide the constellations |
Milky Way: on | Hide the Milky Way |
Ground: on | Hide the ground |
Sun (target) | Sun — a value picker, selection carried by aria-pressed |
Constellations (target) | Constellations — walks each figure by name; aria-label says so |
Wider view / Closer in | Show more sky / Show less sky |
Where is the ISS? | Find the ISS |
The app used to do both at once. The theme button read Dark Mode, meaning you are in Dark Mode. The compass button, one card below, read Turn on compass, meaning you are not in compass mode. Same shape, opposite sense — so neither label could be trusted without first working out which kind it was.
Why the action and not the state, here. A state label only works if you can also perceive which state you are in. Read aloud by a screen reader, "button, Dark Mode" says nothing about what happens next. At 1.8× text in a dark field, the highlight that was carrying the state may be exactly the part that is hard to make out. An action label needs neither the highlight nor the context.
The one exception, named rather than left as drift. A segmented value picker — North/South, East/West — is labelled with the value, because the value is what you are choosing; the selection is carried by aria-pressed. Those four buttons are listed by id in test/labels.test.mjs, so the exception is a decision on the record rather than an inconsistency nobody got round to.
It is enforced, not remembered. test/labels.test.mjs reads index.html and app.js off disk, collects every button's label — including the ones swapped in at runtime, and following a const btn = $('id') alias — and fails the build on a state label, a question mark, or an opening word that is not on the list of verbs the app uses. A new button cannot quietly introduce a sixth convention.
Two jobs, two panes
The app started as a one-shot alignment tool: five numbered cards, top to bottom, done once at the start of a night. It is no longer only that. The live sky view, the constellation figures, the Milky Way band, the ISS and the Moon are all things people come back to all evening, and none of them is a step in a procedure.
Stacked in one scroll, the sky view was four cards down. Nobody scrolls past a polar-scope reticle to find the Big Dipper.
So there is a picker at the top with two sides:
- Tonight's sky — the live view, the constellation and Milky Way toggles, the field-of-view buttons, the ISS, and the Moon.
- Align my mount — which way true north is, every number the mount asks for, the reticle, and the star-hop chart, still numbered 1–2–3.
Where you are sits above both, because it is a prerequisite for each and a step in neither. Once answered it collapses to one line — 30.06° N, 95.21° W — with the full card one press behind Change. A first visit is now a single question and two buttons.
The picker is a tablist, not a pair of action buttons. role="tab" with aria-selected, left/right arrow keys scoped to the picker so they cannot collide with the arrow keys that pan the sky view, and the chosen pane takes focus. The labels name the two jobs rather than an action, which is the same exception the hemisphere picker takes: in a segmented picker the label is the value.
One crossing between them. Finding the pole is an alignment step, but the live view is the best tool for it, so step 3 carries Show me in the live sky view, which switches sides and aims at the pole. Nobody has to already know the view is over there.
Which side you were last on is remembered, so the app opens where you left it rather than where its author thought you should start.
The cards fold away
Each section is a card you can collapse by tapping anywhere along its heading — the whole heading row is the target, not a small triangle at one end. They are native <details> elements, so they are in the tab order, work from the keyboard, and are announced as expanded or collapsed without any script; the content is deliberately not part of the control, because a card you close by touching the thing you were reading is a trap. The sky map opens by default and everything else starts folded — the reason to collapse a wall of text is that a wall of text should not greet you — and each card remembers whether you left it open.
Planning a night that has not happened yet
Set a date and the whole app answers for that instant — where the stars, the Moon and the planets will be, and where the mount will need to point. One field, above both panes, next to where you are: the two questions every other number depends on.
It is deliberately not remembered. Everything else here persists — position, text size, theme, which pane you were on — but a stale planned date is the one setting whose wrong value is dangerous. Open the app in a field at midnight, having planned a trip a fortnight ago, and it would hand you a complete, confident set of mount numbers for the wrong night with nothing obviously wrong on screen. Every load starts at "right now".
While a date is set, the app says so in three places — the bar turns and names the date, the mount numbers say "not for right now", and the sky view's own status line leads with the date it is drawing.
Two things refuse to answer for a future night, rather than answering wrongly:
- The ISS is disabled and says why. Where the station is can only be asked for, not predicted; that needs orbit propagation from a fresh element set, which this app does not do.
- The compass bearing is flagged past 2030, where the magnetic model stops being a model and starts being an extrapolation. True north, the star positions and the dial reading are unaffected — they never use it.
Time zones are the honest limitation. The date is read in the device's current zone, and the app cannot tell that you will be somewhere on a different clock. The field says so, in those words.
Auto Mode and Manual Mode
One control decides who is steering the sky view: Auto Mode follows the phone — turn or tilt it and the view turns with you — and Manual Mode shows the arrows and the buttons instead, for anyone who cannot hold a phone up and sweep it around.
It replaced two buttons whose names were a genuine trap. Follow the phone instead meant the view follows where the phone points; Follow the phone's rotation meant full screen follows whether the phone is turned sideways. Two unrelated settings, near-identical wording — a reader could not tell them apart, and asked. Now there is one switch, labelled with the action like every other button here, and Manual Mode is the only thing that shows the arrows: they cannot be hidden in the one mode that needs them.
Auto Mode is on from the moment the view opens, so the sky is already following the phone's heading and tilt by the time anyone looks — the app does not wait to be told. Its label follows the intent, not the achieved state: between opening and the phone's first orientation reading, the button already offers to switch to Manual, because Auto is already the mode. (iOS is the one exception the app cannot help from here: it requires a tap before it will hand over orientation at all, so on iOS the Use Auto Mode button — which is that tap — is what starts it.)
Filling the screen
Turn the phone sideways and the sky view fills the screen. Turn it back and it does not. No press required — turning a phone to look at something is already the gesture.
It follows a rotation, never the state at load. The first version filled the screen the instant the page opened whenever the window happened to be landscape — so every desktop, and any phone opened already sideways, went full screen on load. That was a bug: a rotation is a change, and the orientation at load is not one. Now opening the view records the orientation without acting on it, and only a genuine flip fills or empties. A desktop is permanently landscape and never rotates, so it never auto-fills at all — the control is a touch-device gesture, and there the button is the only way in.
The ⛶ icon in the bottom-right corner does it by hand — the corner and the symbol every video player uses. A manual press pins the choice; a later physical rotation is a strong enough statement of intent to resume the automatic behaviour.
It is a CSS overlay, not the Fullscreen API — and that is the whole reason it works. The API requires a user gesture, and an orientation change is not one any browser accepts, so a rotation-driven fullscreen request fails silently. Pressing the button is a gesture, so that path additionally asks for real fullscreen; when the request is refused, the overlay is already doing the job.
Zoom, pan, the targets and the mode switch stay reachable in full screen. The pane's controls sit below the canvas, and in full screen there is no below. Two large glyph buttons — + and −, 4 rem square, stacked bottom-right — carry the zoom; a directional cluster — ▲ ◀ ◎ ▶ ▼, the pad's own five actions in the pad's own arrangement — sits bottom-left, so each thumb has its own corner; and top-left is Track, a roll-up: one plate that opens the list of things to point at and closes it again, rolled up by default (the screen was "getting really crowded") and remembered. It is a disclosure in the WAI-ARIA sense — the button carries the section's name and the open/closed state rides on aria-expanded, with a chevron that turns for the eye — and when it is rolled up the list is hidden outright, so a screen reader is not walked through six buttons that are not on the screen. The Auto/Manual switch sits beneath it, outside the roll-up, so rolling the targets away never rolls away the way back. All of them delegate to the same controls that live below the canvas, carry the same accessible names, and are translucent plates with a light rim so they read against the sky rather than vanishing into it. A filled screen that could be zoomed but not moved was one of the real-phone reports; a control the same colour as the black canvas was another.
Where the view is pointing is written on the picture. A small plate shows the azimuth with its compass point and the altitude, in whole degrees, in both modes — bottom centre under the map, and in full screen directly above the arrow pad, aligned with it, so it reads as the pad's own gauge: press Up and watch Alt climb. (Bottom centre was tried in full screen too; on a 375 px phone it touched the Down button.) When the pad is hidden in Auto Mode the plate drops into the corner it leaves. In Auto Mode that is the phone's own heading and tilt with the declination applied, which is the pair of numbers a polar-alignment app is for and the one thing the picture had not said. It updates every frame, so it is deliberately not a live region — the mode hint under the map still announces the position when a hand moves it — and it is plain text a screen reader can read on request. A drag that starts on the numbers is a drag on the sky. (It is also the instrument: every drag bug so far was diagnosed by reading these two numbers out of a hidden hint.)
The canvas is resized to the box it is drawn into, rather than a fixed 720×480 stretched by CSS. That was soft on a phone and outright distorted the moment the aspect ratio changed — a circle drawn round the pole would have come out an oval, on the one view whose entire promise is that things are where they really are.
The caption is not deleted in full screen, it is moved out of sight and left for a screen reader, exactly as the reticle's description is. A full-screen view with no text alternative would be the one part of this app that is picture-only.
Seeing it
- Text scales 0.8×–1.8× from two permanent buttons in the header, and the choice persists.
- Night Mode: pure red on black, outlines only. See below — it is a measurable property, not a vibe.
- The numbers are the interface. The reticle drawing and the sky chart are support, never the only source of anything. Each canvas carries a text alternative that states the same values in words, so the app is fully usable by someone who cannot see either graphic.
- Prefers-reduced-motion is honoured.
Night Mode, and why it is measured
Rod cells are effectively blind above about 620 nm. That is the whole basis of the century-old astronomy convention: deep red lets you read without bleaching rhodopsin, and dark adaptation takes 20–30 minutes to rebuild once it is lost. Green is the worst thing you can put on the screen — rods peak around 500–550 nm — so any green in a "red" theme is actively undoing the thing the theme exists for.
The first version of this app failed that test. Its night palette used #ff5544 and #ff4a3a, which read as red and are not: 0x55 of green and 0x44 of blue is a lot of light landing exactly where rods are most sensitive.
Night Mode is now pure red channel — every colour is #RR0000, so the only variable is intensity — and that is checkable rather than a matter of taste:
179 CSS colour properties audited -> 0 leaking green or blue
sky chart canvas -> 0 green/blue pixels, 10,838 red
reticle canvas -> 0 green/blue pixels, 12,088 redNothing is filled. A filled button is a solid block of emitted light; the same button as an outline is a thin line of it. On a phone at arm's length in a dark field that is the difference between a lantern and a marker — and it is not only your own night vision at stake, it is everyone standing near you. So buttons, step badges, cards and figures all become transparent shapes with a red border. The only solid thing left is the black background, which emits nothing.
Two deliberate exceptions:
- Stars stay filled. They are one to three pixels across; an outline at that size is invisible, and a point of light is what a star is. The intensity comes down instead.
- All text is one red. The palette carried a dimmer secondary shade (
#cc0000) for a while, on the reasoning that hierarchy is worth a second tone. In the field it read the other way: some text is darker than the rest, on a surface that is a single hue by design. So secondary text is now the same#ff0000as primary — brighter is if anything more readable, so the original readability reason still holds — and only borders stay dimmer, because a border is not text.
The green default layout is untouched and remains the default; Night Mode is a second theme, not a replacement.
And the docs open in the same darkness. The ? button in the header opens these pages, and when the app is in Night Mode it hands that over — the docs site reads it and paints itself red before the first frame, so reaching for help outdoors does not flash a white page at eyes that took twenty minutes to adapt. The theme travels both ways: leave the app in day mode and the docs open in day mode too.
The numbers, in the units the mount asks for
Hand controllers and setting circles want degrees and decimal minutes, not decimal degrees. Making someone convert 42.5078 into 42° 30.5′ by hand, in the dark, is exactly the avoidable work this app exists to remove. So the app publishes the whole set: altitude axis, azimuth (true and as a compass bearing), latitude and longitude in DM, elevation, UTC offset, and local sidereal time for an RA setting circle.
The latitude is also spelled out in words — "forty-two degrees, thirty point five minutes". That is not decoration. It is the one number that, set wrong, quietly ruins every exposure of the night, and it is read off a screen while your hands and attention are on a knob. 42 and 24 are one glance apart and transpose easily; forty-two does not. It also gives screen readers and the speech button something unambiguous to say, since 42° 30.5′ N is voiced very differently by different engines and some drop the units entirely.
The order to do it in
The app now carries the procedure itself, with your numbers already in the steps, because reconstructing a sequence from a page of figures is work.
The first step is level the tripod, which is the one most often skipped and the one everything else depends on: on an unlevel tripod the altitude and azimuth adjustments stop being independent, so moving one drags the other and you chase the pole around without converging.
Hearing it
"Read these out loud" speaks the altitude axis, the compass bearing and the reticle position through the browser's speech synthesis. The case this is for is real and specific: you are at the mount, in the dark, and the phone is not where your eyes are.
The words it says are also printed underneath it. Not a caption on audio — it is literally the same string, rendered from one source. That matters three ways: a deaf or hard-of-hearing user gets the sentence at all, where the button alone would give them nothing; a sighted user can check what will be read before committing to listening to it; and the two can never drift apart, because there is only one of them.
Writing it for the ear changed the wording. "Fourteen degrees on a compass, fourteen degrees west declination" sounds like a stutter — and for western declination those two numbers are always equal, so it is not a coincidence that can be avoided. It now says "…that is 14 degrees on a magnetic compass, because the needle here points 14 degrees west of true north", which turns an apparent repeat into the reason for itself.
Extracting it also caught a live bug. The spoken text said "Put Polaris" in both hemispheres while the screen correctly said "Put σ Oct" — so anyone relying on the audio in the southern hemisphere was sent after a star that never rises where they are standing. The briefing now names the right star, the right pole, and adds a warning the northern version does not need: that Sigma Octantis is magnitude 5.5 and the Southern Cross is the way to find the pole.
Haptic confirmation fires once when the live arrows reach Polaris, so "you're pointing at it" arrives without looking at the screen at all.
Pointing the phone at the sky
Hold the phone up like a camera and it guides both axes at once: turn left/right, and raise/lower. When both are right it says so, vibrates, and shows a star.
Before this the compass was the only live signal, so the app could say "turn left 20 degrees" while the altitude was a constant printed in the same sentence — "then look 46 degrees up" — with no idea whether you were anywhere near it. That reads like guidance and is not.
The tilt convention, which is the part that is easy to get backwards:
beta = 0 flat on a table, screen up
beta = 90 upright, screen facing you
beta = 180 flat, screen down
altitude the BACK of the phone is aimed at = beta - 90So aiming at Polaris from 46°N wants beta near 136 — the phone tilted well back, screen toward your face, looking "through" it.
Three deliberate behaviours:
- The target is the pole, not the pole star. In the north they are within half a degree so it makes no difference; in the south Sigma Octantis is magnitude 5.5 and there is nothing to see, so the pole itself is the only honest thing to aim at.
- It corrects the bigger error first. Chasing both axes at once is how people oscillate and never settle.
- A phone with no tilt sensor is told so. If the heading is right but tilt is unavailable, the app says the altitude is not being checked rather than reporting "on target" — that is how someone ends up confidently aimed at the ground.
The live tilt reading stays on screen whether or not it is on target. It doubles as an inclinometer you can hold against the mount's altitude scale, and it is the only honest way to find out what a given phone's beta actually means — like the compass reference question, that is not verified on real hardware and the number is shown so it can be.
The live sky view
Hold the phone up and the stars sit where they really are, the way Stellarium and its kin work — a rectilinear projection of the sky centred on wherever the phone is aimed, with the pole ringed and named.
Holding the phone up is the OPT-IN, not the default. That gesture — holding a phone up, steadily, and sweeping it around — is exactly the one the rest of this app exists to avoid needing, and plenty of people cannot do it at all. A sky view that only works that way is a sky view most of this app's intended users cannot open. It is also dead on a desktop, where there is no orientation sensor to follow.
So the view opens already aimed at the pole and is driven by a directional pad: Look up / down / left / right, a Find the pole button that recentres, and the arrow keys — one key at a time, no modifiers, which is the input this app is written for. Follow the phone instead is there for anyone who wants it, and pressing any pad button takes control back rather than fighting the sensor for it.
Three smaller choices follow from the same rule:
- Zoom is two buttons, not a pinch. Pinching is a two-finger gesture and this app uses none.
- When the pole is off-screen there is an arrow at the edge pointing at it. An empty sky and a sky pointed the wrong way look identical, and leaving someone to work out which is which is the failure mode.
- Everything it shows still exists elsewhere — as numbers, as the arrows, and as the circumpolar chart. None of those changed.
The pad shows itself exactly when it is the only way to drive. It is five full-width buttons — most of a phone screen — and while the phone itself is steering they do nothing. So it is closed when the view is following an orientation sensor, and open when there is nothing to follow: every desktop, and any phone that declines the permission. That is not a default anyone has to discover, it is the state that matches the device. Move the view by hand opens it anyway, and that choice outranks the guess from then on.
And it is never disabled. While the phone is steering, a press on the pad hands control to the buttons — that is what a press means. An earlier version greyed the pad out during following, and on a dark screen at arm's length greyed reads as gone: the first real-phone report was "the controls to move the screen are not visible." Someone lying down who can lift a phone but cannot sweep it had a view pointed at the ceiling and four dead buttons under it.
There is no longer a button to open the sky view, because the view is the Tonight's sky pane. A press whose only job was to reveal the thing you had just navigated to was ceremony in front of the feature people come for.
Constellations and the Milky Way
Both are on by default, with a button each. The figures are how people recognise what they are looking at, and the band is what most of them are pointing a camera at in the first place — knowing where it runs, and where its bright core in Sagittarius sits, is the difference between framing a shot and hunting for one.
Twenty-six figures, the ones people actually point at, rather than all 88: a sky full of lines is unreadable exactly when you are trying to find one thing.
They are written as Bayer letters and resolved against the shipped catalogue at build time, not as catalogue numbers. A line drawn to the wrong star still looks like a constellation — it is just a wrong one, and nothing in the app can tell. Resolving them means a mistake is a build failure with a star's name in it. That caught two on the first run.
The Milky Way is drawn from the galactic plane rotated into the sky, not traced by hand, so it cannot drift out of step with the stars drawn on top of it. The frame is built from the galactic pole and the galactic centre, and checked against five stars' published galactic latitudes — Deneb at 2.0°, Polaris at 26.5° — because a slightly wrong rotation shows up nowhere else. It is drawn as soft additive blobs rather than a filled shape: a hard-edged band reads as something drawn on the sky, and the real thing has no edge.
Why a rotation matrix rather than two angles
The arrows take heading for left/right and tilt for up/down, which is right for "which way do I turn". It falls apart for a sky view: tip the phone sideways and the sky must roll with it, and near the zenith the naive version gimbals and the view snaps around. Building the device's actual basis and projecting onto it costs a dozen lines and has neither problem.
One thing worth recording, because "gamma is roll" is the natural assumption and it is wrong: gamma rotates about the phone's top-to-bottom axis, so it always moves where the back points. A roll that leaves the aim fixed would be rotation about the view axis, which DeviceOrientation does not report. Held upright the phone's long axis is vertical, so gamma becomes a pure yaw — the ZXY convention's gimbal degeneracy, pinned by a test.
Stars behind you are dropped rather than projected. Without that test every star behind the phone is mirrored through the origin and drawn in front, which produces a plausible-looking sky that is upside down and back to front.
Not needing a compass at all
The strongest accessibility decision in the app is a factual one.
People assume they need to know where north is in order to find Polaris. It works the other way round: Polaris is within about 0.6° of the true pole, so pointing at it is pointing true north. Star-hop to it from the Big Dipper using the chart and the mount is aligned, with no compass, no declination and no magnetometer anywhere in the loop.
That matters practically as well as physically. A desktop browser has no magnetometer at all, plenty of phone compasses are badly calibrated, and holding a phone flat and steady to read one is itself a demand not everyone can meet. So the app offers three routes to north and only one of them involves a compass:
- Polaris itself — nothing required.
- A shadow at solar noon, computed to the minute — nothing required.
- A magnetic compass, with declination already applied.
Not making people type
Typing is expensive. Every field that can be filled some other way, is:
- GPS fills position in one tap on a phone — latitude, longitude and altitude, no typing whatsoever.
- Altitude can be looked up from the coordinates, so nobody has to go and find their own elevation. (It is a button rather than automatic because it is the only network request the app makes and it carries the user's coordinates off-device — see deploying.)
- Position is remembered between visits, so the common case is zero input.
- Rejected entries keep their values. A bad latitude produces a message and leaves every field exactly as typed. Wiping a form someone paid for keystroke by keystroke is the worst thing an error handler can do.
- The sign is a button, not a character. See below.
The minus sign was the worst input bug in the app
Latitude and longitude used to be typed as signed decimals — -33.8688. That is one character, easy to drop, and dropping it was silent and catastrophic: typing 33.8688 for Sydney produced a complete, confident, northern answer. Wrong pole star, wrong reticle circles, wrong chart, azimuth 180° out, and nothing on the page marking any of it as suspect.
Longitude was worse, because there is no hemisphere wording to contradict it. 95.2107 instead of -95.2107 moves you from Texas to central China, and the only thing that disagrees is the sidereal time — which is exactly the number nobody can sanity-check by eye.
So the sign is now two big buttons — North/South and East/West — and the field holds a plain magnitude. That removes a keystroke, which matters here, but the real gain is that the hemisphere becomes a visible state you can check rather than a punctuation mark you have to remember.
Three details that make it hold up:
- A typed minus still works, and moves the button to match. Letting the button silently override an explicit
-33.8688would reintroduce the very bug the buttons exist to remove. - The field normalises to a magnitude afterwards, so the number and the button can never sit on screen contradicting each other. That invariant is asserted directly in
test/coords.test.mjs. - GPS moves the buttons too, so opening the panel after locating shows the right side of the equator rather than a stale default.
Working where it is used
A dark field has no signal. The app is offline-first: everything, including the 9,096-star catalogue and the magnetic model, is on the device after the first load. No network call is required to produce any number it shows.
It installs to a home screen from the browser, so there is no app store account, no purchase, and no update anyone has to approve.
The site header is not an app control
On polaris.evomedia.net the app is preceded by the evomedia.net site header — the mark, the wordmark linking home, and the portfolio nav. It is injected by src/evomedia-chrome.js, mounts only on *.evomedia.net, and no-ops anywhere else, so nobody self-hosting this gets a header advertising someone else's business.
It is deliberately not sized with --tap. The 3.4 rem minimum exists because the app's controls are pressed outdoors, in the dark, with an unsteady hand or a mouthstick. Portfolio links are pressed once, indoors, by someone who has already decided to leave. Sizing them as if they were mount controls put three 58 px rows above the fold and spent 265 px of an 812 px phone screen before the app began. They are 2 rem instead — 34 px, comfortably past the WCAG 2.2 AA target-size floor of 24 px — and they still scale with the text-size buttons, so at 1.8× they grow like everything else. The header is now 82 px on a phone and 45 px on a desktop.
The Evomedia mark is a white rounded square, and in Night Mode it was the one thing left on screen emitting white: #ffffff behind the 0.7 brightness filter is about #b3b3b3, with green and blue in it, landing where rods are most sensitive. It now gets the same treatment as every button — unfilled, pure red, said with a line instead of a block.
Known gaps
- Screen-reader testing has not been done with a real screen reader. The semantics are there — landmarks, live regions, labelled controls, text alternatives on both canvases — but that is not the same as having used it with TalkBack, and should not be claimed as such until someone has.
- No high-contrast mode distinct from night mode. Night mode is red-on-black for dark adaptation, which is not the same requirement as maximum contrast.
- The compass reference question in accuracy is unresolved on real Android hardware.