Releases: tenphi/glaze
v0.10.1
Patch Changes
- #52
1988ff8Thanks @tenphi! - FixsrgbToOkhsl(and downstreamglaze.color()) returning a bogus saturated hue/saturation for pure white (#FFFFFF) and other colors at the OKHSL lightness extremes. Floating-point residue fromlinearSrgbToOklabslipped past the existing chroma epsilon, sending the chromatic path through a degenerate gamut where saturation divides by ~zero. White now correctly resolves tookhsl(0 0% 100%)(light) /okhsl(0 0% 15%)(dark) instead ofokhsl(89.88 55.83% 100%).
v0.10.0
Minor Changes
-
#50
6e2d42dThanks @tenphi! - Revampglaze.color()with a value-shorthand overload, seed-anchored
contrast solving, a per-call lightness-scaling argument, and a.css()
export.glaze.shadow()now accepts the same value forms asglaze.color().New defaults for
glaze.color()— split by input form so end-user
string values (color picker / theme settings) get a natural light/dark
inversion, while programmatic object / tuple / structured inputs keep
predictable linear behavior:- String value-shorthand (hex,
rgb(),hsl(),okhsl(),
oklch()):mode: 'auto'with snapshotted scaling
{ lightLightness: false, darkLightness: [globalConfig.darkLightness[0], 100] }.
Light preserves the input exactly; dark Möbius-inverts up to100,
soglaze.color('#000')renders as#fffin dark mode and
glaze.color('#fff')falls to the darklofloor (default0.15).
The darklois snapshotted fromglobalConfigat color-creation
time, matching how an explicitscaling.darkLightness: [lo, hi]
behaves. - Object / tuple value-shorthand (
{ h, s, l },[r, g, b]) and
structured form:mode: 'fixed'with light preserved and dark
linearly mapped intoglobalConfig.darkLightness(default[15, 95]),
also snapshotted at create time so laterglaze.configure()calls
don't retroactively change already-created tokens. - Override per call via the new third positional argument
GlazeColorScaling:{ lightLightness?: false | [lo, hi]; darkLightness?: false | [lo, hi] }.
falsedisables the remap, a tuple sets a custom window. To opt
string inputs back into the previous fixed-linear default, pass
{ mode: 'fixed' }as the second arg or supply an explicit
scaling.
Behavior change (minor bump):
- String value-shorthand callers will see a Möbius-inverted dark
variant by default —glaze.color('#000').resolve().dark.lis now
≈ 1.0, not0.15. To preserve the old fixed-linear behavior pass
{ mode: 'fixed' }as the second argument. - Structured callers without an explicit
modewill see
glaze.color({...}).resolve().light.lmatch the input lightness
exactly instead of being remapped toglobalConfig.lightLightness.
To preserve the old behavior pass
{ lightLightness: globalConfig.lightLightness }as the second
argument. - The default lightness windows for object / tuple / structured
inputs are now snapshotted fromglobalConfig.darkLightnessat
color-creation time, matching the existing behavior for string
inputs. Tokens created before aglaze.configure()call no longer
pick up the new dark window on their next.resolve(). To get the
old "live config" behavior, recreate the token afterconfigure().
Value shorthand additions:
- Accepts hex (
#rgb/#rrggbb/#rrggbbaa), the four CSS color
functions Glaze itself emits (rgb(),hsl(),okhsl(),oklch()),
OkhslColorobjects ({ h, s, l }), and[r, g, b](0–255) tuples
as the first argument. Every string emitted bytheme.tasty() / .json() / .css()
round-trips back throughglaze.color(). - 8-digit hex and
rgba()/hsla()/ slash-alpha alpha components are
parsed and dropped with aconsole.warn(standalone colors have no
opacity field). oklch()chroma now correctly interprets percent values per CSS Color 4
(100% → 0.4).OkhslColorand[r, g, b]inputs are validated up front with helpful
error messages — passing 0–100-scales/lthrows with a hint to use
the structured form, and out-of-range RGB tuples throw with the offending
value in the message.
Anchor model: by default, relative
lightness: '+N'and
contrast: <ratio>are anchored to the literal seed (the value passed
toglaze.color()), so the contrast solver compares against the
unmapped user-provided color across every variant. Pass
overrides.base(aGlazeColorToken) to anchor against another
color's resolved variant per scheme instead.Color pairing via
base:GlazeColorOverrides.baselets one
standalone color depend on another. Accepts either aGlazeColorToken
or anyGlazeColorValue(hex /rgb()/OkhslColor/[r, g, b]);
raw values are auto-wrapped viaglaze.color(value)and inherit the
same string-vs-object defaults. When set:contrastis solved per scheme against the base's resolved variant
(light / dark / lightContrast / darkContrast).- Relative
lightness: '+N'/'-N'is anchored to the base's
lightness per scheme (matches theme behavior for dependent colors). - Relative
hue: '+N'still anchors to the seed (the value passed to
glaze.color()), not the base. modeis the per-pair knob — passmode: 'fixed'to disable Möbius
inversion for the dependent color,mode: 'auto'to keep it.
The base token's
.resolve()is called lazily on first resolve and
the result is captured by reference, matching existing snapshot
semantics. Internally,resolveAllColorsaccepts pre-resolved
external bases and seeds them into the resolution context;
validateColorDefsandtopoSorttreat external base names as leaves.opacityandnameonglaze.color():GlazeColorOverrides.opacity(and the same field on
GlazeColorInput) sets a fixed alpha 0–1 that surfaces in every
scheme variant. Combining withcontrastis not recommended (perceived
lightness becomes unpredictable) —glazeemits aconsole.warnin
that case.GlazeColorOverrides.name(and the same field onGlazeColorInput)
is a human-readable label that surfaces in error and warning messages
in place of the internal"value"sentinel. Empty / whitespace-only
names and reserved internal names ("value","seed",
"externalBase") are rejected with a clear error.
Structured form parity: the
glaze.color({...})overload now
acceptsopacity,contrast,base, andnamein addition to the
existinghue,saturation,lightness,saturationFactor, and
mode.contrastwithoutbasesynthesizes a hidden static seed
from the input's normal-mode lightness so the contrast solver always
has an anchor (mirrors value-form behavior).hue(finite),
saturation/lightness(0–100),saturationFactor(0–1), and
opacity(0–1) are range-checked up front with helpful error
messages — non-finite or out-of-range values fail at creation rather
than producing a NaN-laden token.Contrast warning: when the contrast solver cannot meet the
requested target (e.g. AAA against a mid-grey base — physically
unreachable),glazeemits a singleconsole.warnper
(name, scheme, target)triple naming the affected color, scheme, and
the actual achieved ratio. The token still resolves to the closest
passing variant. Use thenameoverride to make the warning easier to
trace.Persisting standalone colors:
token.export()returns a JSON-safe
snapshot containing the originalvalue(or structured input), the
overrides, and the capturedscaling. Token-typedbaseis
recursively serialized; value-typedbaseis preserved as the raw
value. Pass the result toglaze.colorFrom(data)to rehydrate a token
that resolves byte-for-byte identically to the original — across
glaze.configure()calls and across processes. The capturedscaling
snapshots bothlightLightnessanddarkLightnessfromglobalConfig
at create time, so laterglaze.configure()calls don't retroactively
change exported tokens regardless of input form..css({ name })export: new method on the standalone color token
reaches export parity withtheme.css(). Existing
.token() / .tasty() / .json()continue to work unchanged.glaze.shadow()upgrade:bgandfgnow accept any
GlazeColorValueform — hex,rgb()/hsl()/okhsl()/oklch()
strings,OkhslColorobjects, or[r, g, b]tuples — sharing the same
parser asglaze.color().Internal: standalone color tokens now memoize the underlying resolve
across.resolve() / .token() / .tasty() / .json() / .css()calls.Public type additions:
GlazeColorValue,GlazeColorOverrides,
GlazeColorOverridesExport,GlazeColorCssOptions,
GlazeColorScaling,GlazeColorTokenExport,GlazeColorInputExport.
Newglaze.colorFrom(data)factory andtoken.export()method on
GlazeColorToken. NewhslToSrgb,oklabToOkhsl, andparseHexAlpha
math helpers re-exported from the package root. - String value-shorthand (hex,
v0.9.3
v0.9.2
v0.9.1
v0.9.0
v0.8.0
Minor Changes
- #37
8b0b62bThanks @tenphi! - Bypass lightLightness and darkLightness window constraints in high-contrast mode, allowing colors to use the full 0–100 lightness spectrum for increased perceivable contrast.
Patch Changes
-
#39
f9f6defThanks @tenphi! - AdddarkCurveconfig option for perceptual dark-theme lightness inversion using a power curve. Expands subtle near-white distinctions in dark mode. Default0.5; set to1for legacy linear behavior. Widen contrast solver search range to[0, 1]so contrast targets are met regardless of dark lightness window. -
#39
f9f6defThanks @tenphi! - Replace power-curve dark lightness mapping with Möbius transformation for proportional expansion of lightness deltas across all sizes.
v0.7.0
v0.6.3
Patch Changes
-
d148498Thanks @tenphi! - ApplylightLightnessmapping to dependent colors with absolute lightness, matchingdarkLightnessbehavior. -
#32
90bd23cThanks @tenphi! - Propagate scheme lightness range to contrast solver for dependent colors, preventing pure black/white output when contrast-solving against extreme lightness values.