primary
deriveAccentStates() turns it into the ramp.ThemeTheme around.
Colours only. Spacing, radii and typography are not per-palette and cannot
be made so from here; they come from the process-wide SizeTokens table, and
Theme.tokens(limn.scene.ControlSize) explains at length why. A palette changes what the toolkit looks
like, never how big it is.
The three derive methods are the same expressions the built-in palettes
were built from, and each reads the tones set before it: set the accent
first, then deriveAccentStates(). Call them in any order relative to each
other; call them again after changing an input, or the derived tone stays where the
previous input put it.
Every tone is snapped to eight bits per channel on the way in, so what
get(limn.components.Theme.Token) answers is what build() produces. That is the precision a hex
value, a colour field and a monitor all share, and it is what makes a palette
exactly representable: a tone that could not be written down would come back
different from a file it had just been saved to, and two palettes that render
identically would compare unequal. A colour handed in at higher precision (anything
interpolated, which is most derived tones) is rounded, by at most 1/255.
background(Color value) build()floatcornerScale(float value) Theme.cornerScale.dark(boolean value) disabledFill(Color value) disabledText(Color value) fontFamily(String value) Theme.fontFamily.get(Theme.Token token) set(limn.components.Theme.Token, limn.graphics.Color).booleanisDark()name()Theme.name, which is also what
Theme.displayName() falls back to.deriveAccentStates() turns it into the ramp.primaryHover(Color value) primaryPressed(Color value) Theme.scrim.set(Theme.Token token,
Color value) surfaceRaised(Color value) Theme.name, which is also what
Theme.displayName() falls back to.
Avoid a built-in's name. Display names are looked up by this string,
so a palette called "Nordic" is shown under the built-in Nordic's
translation in every language that has one. Anything else stands as written,
since nothing could translate it.
deriveAccentStates() and deriveSemanticStates() produce, and
applications group and default on it. Changing it does not re-derive
anything already derived.Theme.cornerScale. Clamped to
[0, MAX_CORNER_SCALE]; 1 is the shipped ramp.IllegalArgumentException - if value is not a finite number, which is
what a text field hands over when it is emptyTheme.fontFamily.
null or blank means no preference and stores Font.DEFAULT_FAMILY,
which is what the built-ins carry.
The name is not validated against the installed faces, and deliberately: a
palette is a value that outlives the machine it was authored on, so a family this
machine happens not to have is a legitimate thing to write down. It falls back to the
embedded face when it is applied (see Theme.applyFontFamily()) rather than
being rejected here, which would make a palette unloadable on the wrong laptop.
deriveAccentStates() turns it into the ramp.Theme.scrim. The only tone whose alpha is
read rather than assumed opaque, and passing an opaque colour here is the way to
make a modal hide what it blocks instead of dimming it.set(limn.components.Theme.Token, limn.graphics.Color).The darkening is the one to check by eye rather than trust. A light accent carrying dark label ink has almost no room to darken: 20% can drop the label below 4.5:1, so pressing the button makes its own text harder to read. That is why the toolkit's own palette spells its ramp out instead of calling this.
surfaceRaised, text and background.dark.
They are deliberately not derived from the accent: red means error in every palette, and a palette whose danger colour follows its brand is a palette where an error looks like a link. Override individually where a tone has to clear 4.5:1 on a surface these generic ones do not.