mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-06 13:38:28 -04:00
@@ -313,6 +313,7 @@ func EnsureContrastDPSLstar(hexColor, hexBg string, minLc float64, isLightMode b
|
|||||||
fg := HexToRGB(hexColor)
|
fg := HexToRGB(hexColor)
|
||||||
cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B}
|
cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B}
|
||||||
Lf, af, bf := cf.Lab()
|
Lf, af, bf := cf.Lab()
|
||||||
|
Lf *= 100.0
|
||||||
|
|
||||||
dir := 1.0
|
dir := 1.0
|
||||||
if isLightMode {
|
if isLightMode {
|
||||||
@@ -341,6 +342,7 @@ func EnsureContrastDPSBidirectional(hexColor, hexBg string, minLc float64, isLig
|
|||||||
fg := HexToRGB(hexColor)
|
fg := HexToRGB(hexColor)
|
||||||
cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B}
|
cf := colorful.Color{R: fg.R, G: fg.G, B: fg.B}
|
||||||
origL, af, bf := cf.Lab()
|
origL, af, bf := cf.Lab()
|
||||||
|
origL *= 100.0
|
||||||
|
|
||||||
var darkerResult, lighterResult string
|
var darkerResult, lighterResult string
|
||||||
darkerL, lighterL := origL, origL
|
darkerL, lighterL := origL, origL
|
||||||
@@ -420,6 +422,24 @@ func blendHue(base, target, factor float64) float64 {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// color8 sits a fixed L* offset from the background so it keeps its dim role
|
||||||
|
// regardless of primary brightness (conventional palettes put ANSI bright
|
||||||
|
// black ~2-2.5:1 from the background, e.g. catppuccin-mocha #585b70 on #1e1e2e)
|
||||||
|
func DeriveDim(bgHex string, hue, sat float64, isLight bool) string {
|
||||||
|
offset := 22.0
|
||||||
|
if isLight {
|
||||||
|
offset = -offset
|
||||||
|
}
|
||||||
|
|
||||||
|
bgL := getLstar(bgHex)
|
||||||
|
targetL := math.Max(0, math.Min(100, bgL+offset))
|
||||||
|
|
||||||
|
tint := HSVToRGB(HSV{H: hue, S: sat, V: 0.5})
|
||||||
|
c := colorful.Color{R: tint.R, G: tint.G, B: tint.B}
|
||||||
|
_, af, bf := c.Lab()
|
||||||
|
return labToHex(targetL, af, bf)
|
||||||
|
}
|
||||||
|
|
||||||
func DeriveContainer(primary string, isLight bool) string {
|
func DeriveContainer(primary string, isLight bool) string {
|
||||||
rgb := HexToRGB(primary)
|
rgb := HexToRGB(primary)
|
||||||
hsv := RGBToHSV(rgb)
|
hsv := RGBToHSV(rgb)
|
||||||
@@ -500,10 +520,7 @@ func GeneratePalette(primaryColor string, opts PaletteOptions) Palette {
|
|||||||
gray7V := baseVal * 0.28
|
gray7V := baseVal * 0.28
|
||||||
palette.Color7 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray7S, V: gray7V})), bgColor, normalTextTarget, opts))
|
palette.Color7 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray7S, V: gray7V})), bgColor, normalTextTarget, opts))
|
||||||
|
|
||||||
gray8S := baseSat * 0.05
|
palette.Color8 = NewColorInfo(DeriveDim(bgColor, hsv.H, baseSat*0.05, true))
|
||||||
gray8V := baseVal * 0.85
|
|
||||||
dimTarget := secondaryTarget * 0.5
|
|
||||||
palette.Color8 = NewColorInfo(ensureContrastBidirectional(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray8S, V: gray8V})), bgColor, dimTarget, opts))
|
|
||||||
|
|
||||||
brightRedS := math.Min(baseSat*1.0, 1.0)
|
brightRedS := math.Min(baseSat*1.0, 1.0)
|
||||||
brightRedV := math.Min(baseVal*1.2, 1.0)
|
brightRedV := math.Min(baseVal*1.2, 1.0)
|
||||||
@@ -559,9 +576,7 @@ func GeneratePalette(primaryColor string, opts PaletteOptions) Palette {
|
|||||||
gray7V := math.Min(baseVal*1.05, 1.0)
|
gray7V := math.Min(baseVal*1.05, 1.0)
|
||||||
palette.Color7 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray7S, V: gray7V})), bgColor, normalTextTarget, opts))
|
palette.Color7 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray7S, V: gray7V})), bgColor, normalTextTarget, opts))
|
||||||
|
|
||||||
gray8S := baseSat * 0.15
|
palette.Color8 = NewColorInfo(DeriveDim(bgColor, hsv.H, baseSat*0.15, false))
|
||||||
gray8V := baseVal * 0.65
|
|
||||||
palette.Color8 = NewColorInfo(ensureContrastAuto(RGBToHex(HSVToRGB(HSV{H: hsv.H, S: gray8S, V: gray8V})), bgColor, secondaryTarget, opts))
|
|
||||||
|
|
||||||
brightRedS := math.Min(baseSat*0.75, 1.0)
|
brightRedS := math.Min(baseSat*0.75, 1.0)
|
||||||
brightRedV := math.Min(baseVal*1.35, 1.0)
|
brightRedV := math.Min(baseVal*1.35, 1.0)
|
||||||
|
|||||||
@@ -679,3 +679,73 @@ func TestContrastAlgorithmComparison(t *testing.T) {
|
|||||||
|
|
||||||
t.Logf("WCAG and DPS palettes differ in %d/16 colors", differentCount)
|
t.Logf("WCAG and DPS palettes differ in %d/16 colors", differentCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEnsureContrastDPSLightModeStaysLight(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
result string
|
||||||
|
bg string
|
||||||
|
minLstar float64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "bidirectional adjustment",
|
||||||
|
result: EnsureContrastDPSBidirectional("#d0ccc6", "#f8f8f8", 17.5, true),
|
||||||
|
bg: "#f8f8f8",
|
||||||
|
minLstar: 20.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lstar adjustment",
|
||||||
|
result: EnsureContrastDPSLstar("#c0c0c0", "#f8f8f8", 30.0, true),
|
||||||
|
bg: "#f8f8f8",
|
||||||
|
minLstar: 20.0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
lstar := getLstar(tt.result)
|
||||||
|
if lstar < tt.minLstar {
|
||||||
|
t.Errorf("result %s has L* %.2f on light bg %s, expected >= %.2f (collapsed to near-black)",
|
||||||
|
tt.result, lstar, tt.bg, tt.minLstar)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeneratePaletteColor8Dim(t *testing.T) {
|
||||||
|
hues := []string{"#e91e63", "#f59e0b", "#22c55e", "#06b6d4", "#8b5cf6", "#ef4444"}
|
||||||
|
|
||||||
|
for _, base := range hues {
|
||||||
|
t.Run(base, func(t *testing.T) {
|
||||||
|
palette := GeneratePalette(base, PaletteOptions{IsLight: false, UseDPS: true})
|
||||||
|
|
||||||
|
bgRatio := ContrastRatio(palette.Color8.Hex, palette.Color0.Hex)
|
||||||
|
if bgRatio < 1.5 || bgRatio > 3.0 {
|
||||||
|
t.Errorf("Color8 %s vs bg %s ratio %.2f, expected 1.5-3.0 (bright black stays near bg)",
|
||||||
|
palette.Color8.Hex, palette.Color0.Hex, bgRatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
sepRatio := ContrastRatio(palette.Color4.Hex, palette.Color8.Hex)
|
||||||
|
if sepRatio < 2.0 {
|
||||||
|
t.Errorf("Color4 %s vs Color8 %s ratio %.2f, expected >= 2.0 (blue must not collide with bright black)",
|
||||||
|
palette.Color4.Hex, palette.Color8.Hex, sepRatio)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeneratePaletteLightColor8StaysLight(t *testing.T) {
|
||||||
|
palette := GeneratePalette("#f59e0b", PaletteOptions{IsLight: true, UseDPS: true})
|
||||||
|
|
||||||
|
lstar := getLstar(palette.Color8.Hex)
|
||||||
|
if lstar < 60.0 {
|
||||||
|
t.Errorf("light mode Color8 %s has L* %.2f, expected >= 60 (dim grey, not near-black)",
|
||||||
|
palette.Color8.Hex, lstar)
|
||||||
|
}
|
||||||
|
|
||||||
|
bgRatio := ContrastRatio(palette.Color8.Hex, palette.Color0.Hex)
|
||||||
|
if bgRatio > 3.0 {
|
||||||
|
t.Errorf("light mode Color8 %s vs bg %s ratio %.2f, expected <= 3.0",
|
||||||
|
palette.Color8.Hex, palette.Color0.Hex, bgRatio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user