Theme
NAME
App::Moneymoor::Theme - the app's semantic colour palette: nineteen
required slots, the colours derived from them, and the translation into
a Selkie::Theme.
SYNOPSIS
use App::Moneymoor::Theme;
use App::Moneymoor::Themes;
my $theme = App::Moneymoor::Themes::load('tokyo-night');
# Semantic slots, named for what they mean rather than what they are:
$theme.fg-red; # an overspent envelope
$theme.fg-green; # money still available
$theme.accent; # the palette's signature hue
# Derived, so a palette never has to keep two values in sync:
$theme.blend(0x000000, 0xFFFFFF, 0.5); # 0x808080
my ($left, $right) = $theme.banner-gradient;
# And the whole thing as Selkie's own vocabulary, for the framework's
# widgets:
my $app = Selkie::App.new(theme => $theme.to-selkie);
DESCRIPTION
Two colour vocabularies meet here. Selkie::Theme is keyed to widget
roles โ border, input, scrollbar, dropdown โ because that is what a
framework can know about. This class is keyed to what a colour means
in a budget: green is money you still have, red is money you spent that
you did not have, amber is the overspend a credit card will write off
at the period boundary (see docs/ui-v0.1-spec.md ยง2). to-selkie is
the one-way translation between them.
Every slot is is required. A palette either supplies all nineteen or
fails at construction, which is the only point where a missing colour
is cheap to notice โ the alternative is a theme that looks right until
the user opens the one dialog whose slot was never filled.
Derived rather than declared
Anything computable from the nineteen lives here as a method:
blend, banner-gradient, flash-colour, and the handful of
blends to-selkie makes inline. Every extra required slot would be
eleven more hand-picked hex values to keep in step across the eleven
palettes, and eleven more chances to ship one that disagrees with
itself.
Filling every Selkie slot
to-selkie passes a value for every Selkie::Theme slot,
including the ones Selkie would default. Selkie's defaults derive from
its own palette constants, not from the slots handed in, so a defaulted
slot is how a Gruvbox app ends up with blue-grey dropdowns, toasts and
overlay titles. t/50-theme-completeness.rakutest pins it: for every
slot Selkie would have defaulted, this class's value must differ from
Selkie::Theme.default's โ and the palettes' contrast ratios have to
clear the WCAG floors on top of that.
SEE ALSO
App::Moneymoor::Themes โ the registry and the
loadby name.App::Moneymoor::View::ModalChrome โ the style bundle every dialog is built from, derived from one of these.