Skip to content

Add a Stevenson green and gold theme - #19

Open
ShankarK2009 wants to merge 3 commits into
mainfrom
feature/stevenson-theme
Open

ShankarK2009 wants to merge 3 commits into
mainfrom
feature/stevenson-theme

Conversation

@ShankarK2009

@ShankarK2009 ShankarK2009 commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Adds a Theme picker to Settings → Appearance, next to the existing color scheme and time format controls. Stevenson is the default; Classic restores the stock iOS palette the app shipped with.

Modelled on how stevenson.space handles the same two colors: a green hero band, and an accent that changes with the appearance.

The accent swaps rather than being picked

Contrast rules out any single accent:

pair ratio verdict
#1B5E20 on white 7.9:1 AAA
#1B5E20 on a dark background 1.6:1 unusable
#B38825 on white 3.3:1 fails AA for text
#C9992B on a dark background 6.5:1 AAA

Green reads on white and dies on black; gold does the reverse. So the app accents in green in light mode and gold in dark mode, and neither appearance is asked to carry a color that cannot hold text there.

The hero band

The day header and the countdown now sit in a field of school green, curved off where the day's cards begin — extended to the weekend, break and summer screens too, so a non-school day gets a real header instead of an icon floating on grey.

Dark mode keeps a green band rather than a neutral one: deep enough to read as a dark surface (1.5:1 against black) while never letting the screen go grey.

The countdown gets a disc punched out of the band, matching the system appearance so .primary/.secondary stay correct on it. The ring is pulled well inside the disc — the arc is school green, and school green on the green band is the same color twice over. The track takes whichever school color the arc isn't wearing, so the ring reads as green and gold in both appearances.

The light gold

Brand gold clears only 2.4:1 against green, which rules it out on the band entirely. Lightening it to #E8C560 reaches 4.7:1 — that one value is what lets gold appear on green at all, and it carries the status-screen glyphs and the header badges.

Semantic colors stay semantic

Late Arrival purple, Early Dismissal red, passing-period orange, the override badge and destructive red are information, not branding, and are untouched in both themes. Where the band would swallow one, the fix reinforces its shape rather than repainting it:

  • An Early Dismissal capsule clears only 2.2:1 against green, so it keeps its red fill and gains a white border.
  • The passing-period banner keeps its orange on an opaque card, since a 12% wash would vanish into the green.

Both resolve to the old rendering under Classic.

Notes for review

Two judgment calls worth a look:

  1. Stevenson is the default, including for existing installs — a config blob written before themes existed decodes as Stevenson rather than pinning upgraders to Classic. This is the Stevenson app, so the school identity seemed like the right default, but it's a one-line change if you'd rather ship Classic as the default.
  2. The ID and Lunch tabs now follow the theme. Their accents were previously hardcoded green regardless of any preference, which meant two tabs were already half-themed. Routing them through the theme makes the picker actually mean something — the side effect is that those tabs go system blue under Classic.

The ID card deliberately keeps its own crest-sampled palette. It emulates printed stock, doesn't follow dark mode, and its lighter print gold carries ink at 7.0:1 versus the brand gold's 5.6:1 — moving it onto the brand values would have cost real legibility. The two greens are close enough to read as the same school green.

Verification

  • swift test on both packages: 185 + 103 tests pass, including a new assertion that a pre-theme config blob decodes as Stevenson and that the preference round-trips.
  • Checked in the simulator in light and dark, both themes, across mid-class, passing period, weekend, and Early Dismissal / Finals states. Classic renders identically to main in each.

Add a Theme picker to Settings alongside the existing color scheme and
time format controls. Stevenson is the default; Classic restores the
stock iOS palette the app shipped with.

Green and white carry the design. Measured against WCAG, #1B5E20 and
white read at 7.9:1 in both directions, but gold reaches only 3.3:1 on
white and 2.4:1 on green — so gold can never be text or an icon here.
That constraint sets the hierarchy: green does the structural work
(tint, controls, the selected tab, the hero dial's progress arc), and
gold is used strictly as a fill under near-black ink (5.6:1), where it
gets exactly one job — marking the block happening now.

Dark mode substitutes lightened values. #1B5E20 falls to roughly 1.6:1
on a dark background, so the green becomes #35A83F (5.5:1) and the gold
#C9992B. The dial's gold track also takes less alpha there: the wash
that reads as cream over white muddies into brown over black and starts
competing with the arc it sits behind.

A theme repaints chrome only. The colors that flag an abnormal schedule
are information rather than branding, so the Late Arrival, Early
Dismissal, passing period, override, and destructive colors are left
untouched in both themes.

Route the ID and Lunch tabs through the theme as well; their accents
were previously hardcoded green regardless of any preference. The ID
card keeps its own crest-sampled palette — it emulates printed stock,
does not follow dark mode, and its lighter print gold carries ink at
7.0:1 against the brand gold's 5.6:1.

Persist the choice in UserConfig. Blobs written before themes existed
decode as Stevenson, matching a fresh install rather than pinning
upgraders to Classic.
…ance

Follows the stevenson.space treatment of the same two colours. The theme
was carrying its identity in small accents on grey and white surfaces;
this puts the colour where the eye lands first and stops each appearance
from falling back to a colour it cannot actually use.

Swap the accent instead of picking one. Green reads at 7.9:1 on white
and dies at 1.6:1 on a dark background; gold does the reverse, failing
at 3.3:1 on white and clearing 6.5:1 on dark. So light mode accents in
green and dark mode accents in gold, and neither appearance is asked to
carry a colour that cannot hold text there.

Add the hero band: the day header and the countdown now sit in a field
of school green, curved off where the day's cards begin. Dark mode keeps
a green band rather than a neutral one — deep enough to read as a dark
surface at 1.5:1 against black, but the screen never goes grey. The
countdown gets a disc punched out of the band, matching the system
appearance so its text stays correct, with the ring pulled well inside:
the arc is school green, and school green on the green band is the same
colour twice over. The track takes whichever school colour the arc is
not wearing, so the ring reads as green and gold together in both.

Introduce a light gold for use on the band. Brand gold clears only 2.4:1
against green, which rules it out entirely, but lightening it to #E8C560
reaches 4.7:1 — that single value is what lets gold appear on green at
all, and it carries the status-screen glyphs and the header badges.

Semantic colours stay semantic. Where the band would swallow one, the
fix reinforces its shape rather than repainting it: an Early Dismissal
capsule clears only 2.2:1 against green, so it keeps its red fill and
gains a white border. The passing-period banner keeps its orange on an
opaque card, since a 12% wash would vanish into the green. Both resolve
to the old rendering under Classic, which is unchanged throughout.

Extend the band to the weekend, break and summer screens, so a non-school
day gets the same header rather than an icon floating on grey.
The band read as the screen's header but stopped at the safe-area
inset, leaving a grey lid above it. It now runs to the top of the
screen.

The colour under the clock is not the band's own green. iOS draws the
status bar in black in light mode and offers no way to change that
outside a navigation bar — a real bar with `toolbarColorScheme(.dark)`
no longer reaches the status bar on iOS 26, and `UIHostingController`
does not forward `childForStatusBarStyle`, so neither route works. Full
band green would carry the clock at 2.3:1. The strip is instead one step
up the same green ramp, at 4.1:1 against a 3:1 bar for text that bold,
and a short gradient eases it down to the band across the empty space
beneath, so the two read as one field. Dark mode's clock is white, so
there the strip *is* the band and the join is invisible.

Classic resolves the strip to `clear` and the gradient to clear-on-clear,
leaving the screen exactly as it shipped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant