role color stuff
This commit is contained in:
parent
41d48bf60a
commit
f72c7a357b
9 changed files with 165 additions and 14 deletions
|
@ -1,8 +1,6 @@
|
||||||
package presentation
|
package presentation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.sapphic.engineer/roleypoly/v4/types"
|
"git.sapphic.engineer/roleypoly/v4/types"
|
||||||
"git.sapphic.engineer/roleypoly/v4/utils"
|
"git.sapphic.engineer/roleypoly/v4/utils"
|
||||||
)
|
)
|
||||||
|
@ -43,6 +41,7 @@ func Role(category *types.Category, role *types.Role, selected bool) Presentable
|
||||||
|
|
||||||
type PresentableRoleColors struct {
|
type PresentableRoleColors struct {
|
||||||
Main string
|
Main string
|
||||||
|
Alt string
|
||||||
IsDark bool
|
IsDark bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,9 +49,11 @@ func GetColors(roleColor uint32) PresentableRoleColors {
|
||||||
// TODO: no color
|
// TODO: no color
|
||||||
|
|
||||||
r, g, b := utils.IntToRgb(roleColor)
|
r, g, b := utils.IntToRgb(roleColor)
|
||||||
|
altR, altG, altB := utils.AltColor(r, g, b)
|
||||||
|
|
||||||
return PresentableRoleColors{
|
return PresentableRoleColors{
|
||||||
Main: fmt.Sprintf("#%x", roleColor),
|
Main: utils.RgbToString(r, g, b),
|
||||||
|
Alt: utils.RgbToString(altR, altG, altB),
|
||||||
IsDark: utils.IsDarkColor(r, g, b),
|
IsDark: utils.IsDarkColor(r, g, b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,11 @@ body {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
padding: 0.217rem; /* this is silly number pls ignore :3 (^noe) */
|
padding: 0.217rem; /* this is silly number pls ignore :3 (^noe) */
|
||||||
gap: 0.215rem;
|
gap: 0.215rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.35s ease-in-out;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
|
cursor: pointer;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 1.216rem;
|
width: 1.216rem;
|
||||||
|
@ -52,9 +55,17 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 2px solid var(--role-color);
|
border: 2px solid var(--role-color);
|
||||||
transition: all 0.35s ease-in-out;
|
transition: all 0.25s ease-in-out;
|
||||||
border-radius: 1.216rem;
|
border-radius: 1.216rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--role-color);
|
||||||
|
&::before {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: var(--contrast-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
transition: all 0.35s ease-in-out;
|
transition: all 0.35s ease-in-out;
|
||||||
content: "";
|
content: "";
|
||||||
|
@ -72,6 +83,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:has(input:checked) {
|
&:has(input:checked) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{ $for := printf "category-%s_role-%s" .CategoryID .ID }}
|
{{ $for := printf "category-%s_role-%s" .CategoryID .ID }}
|
||||||
<div
|
<div
|
||||||
class="role"
|
class="role"
|
||||||
style="--role-color: {{.Colors.Main}}; --contrast-color: var({{if .Colors.IsDark}}--grey600{{else}}--grey100{{end}});"
|
style="--role-color: {{.Colors.Main}}; --contrast-color: {{.Colors.Alt}};"
|
||||||
data-testid="role-{{.ID}}"
|
data-testid="role-{{.ID}}"
|
||||||
>
|
>
|
||||||
<input type="{{.InputType}}" id="{{$for}}" {{if eq .InputType
|
<input type="{{.InputType}}" id="{{$for}}" {{if eq .InputType
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"git.sapphic.engineer/roleypoly/v4/templates"
|
"git.sapphic.engineer/roleypoly/v4/templates"
|
||||||
"git.sapphic.engineer/roleypoly/v4/types"
|
"git.sapphic.engineer/roleypoly/v4/types"
|
||||||
"git.sapphic.engineer/roleypoly/v4/types/fixtures"
|
"git.sapphic.engineer/roleypoly/v4/types/fixtures"
|
||||||
|
"git.sapphic.engineer/roleypoly/v4/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,18 +33,18 @@ func TestRole(t *testing.T) {
|
||||||
r := &fixtures.RoleWithDarkColor
|
r := &fixtures.RoleWithDarkColor
|
||||||
html := renderRole(t, c, r, false)
|
html := renderRole(t, c, r, false)
|
||||||
assert.Contains(t, html, "--role-color: #a20000;", "role color is set")
|
assert.Contains(t, html, "--role-color: #a20000;", "role color is set")
|
||||||
assert.Contains(t, html, "--contrast-color: var(--grey600);", "contrast color is set")
|
assert.Contains(t, html, `type="checkbox"`, "multi has input type=checkbox")
|
||||||
|
assert.Contains(t, html, fmt.Sprintf("--contrast-color: %s;", utils.RgbToString(utils.AltColor(162, 0, 0))), "contrast color is set")
|
||||||
assert.Contains(t, html, fmt.Sprintf(`id="%s"`, roleInputID(c, r)), "input has ID attr")
|
assert.Contains(t, html, fmt.Sprintf(`id="%s"`, roleInputID(c, r)), "input has ID attr")
|
||||||
assert.Contains(t, html, fmt.Sprintf(`for="%s"`, roleInputID(c, r)), "label has for attr")
|
assert.Contains(t, html, fmt.Sprintf(`for="%s"`, roleInputID(c, r)), "label has for attr")
|
||||||
assert.Contains(t, html, `type="checkbox"`, "multi has input type=checkbox")
|
|
||||||
assert.NotContains(t, html, fmt.Sprintf(`name="%s"`, roleInputName(c)), "multi has no name attr")
|
assert.NotContains(t, html, fmt.Sprintf(`name="%s"`, roleInputName(c)), "multi has no name attr")
|
||||||
// TODO: selected?
|
// TODO: selected?
|
||||||
|
|
||||||
c = &fixtures.CategorySingle
|
c = &fixtures.CategorySingle
|
||||||
r = &fixtures.RoleWithLightColor
|
r = &fixtures.RoleWithLightColor
|
||||||
html = renderRole(t, c, r, true)
|
html = renderRole(t, c, r, true)
|
||||||
assert.Contains(t, html, "--contrast-color: var(--grey100);", "single has name attr")
|
|
||||||
assert.Contains(t, html, `type="radio"`, "single has input type=radio")
|
assert.Contains(t, html, `type="radio"`, "single has input type=radio")
|
||||||
|
assert.Contains(t, html, fmt.Sprintf("--contrast-color: %s;", utils.RgbToString(utils.AltColor(0xff, 0xaa, 0x88))), "contrast color")
|
||||||
assert.Contains(t, html, fmt.Sprintf(`name="%s"`, roleInputName(c)), "single has name attr")
|
assert.Contains(t, html, fmt.Sprintf(`name="%s"`, roleInputName(c)), "single has name attr")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Pred float64 = 0.299
|
||||||
|
Pgreen float64 = 0.587
|
||||||
|
Pblue float64 = 0.114
|
||||||
|
)
|
||||||
|
|
||||||
func IsDarkColor(r, g, b uint8) bool {
|
func IsDarkColor(r, g, b uint8) bool {
|
||||||
rC := 0.299 * math.Pow(float64(r), 2)
|
lum := Luminance(r, g, b)
|
||||||
gC := 0.587 * math.Pow(float64(g), 2)
|
|
||||||
bC := 0.114 * math.Pow(float64(b), 2)
|
|
||||||
|
|
||||||
lum := math.Sqrt(rC + gC + bC)
|
|
||||||
|
|
||||||
return lum <= 130
|
return lum <= 130
|
||||||
}
|
}
|
||||||
|
@ -19,3 +24,47 @@ func IntToRgb(color uint32) (uint8, uint8, uint8) {
|
||||||
|
|
||||||
return uint8(r), uint8(g), uint8(b)
|
return uint8(r), uint8(g), uint8(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RgbToString(r, g, b uint8) string {
|
||||||
|
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AltColor(r, g, b uint8) (uint8, uint8, uint8) {
|
||||||
|
brightnessAmount := -0.6
|
||||||
|
if IsDarkColor(r, g, b) {
|
||||||
|
brightnessAmount = 0.85
|
||||||
|
}
|
||||||
|
return Brighten(r, g, b, brightnessAmount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Brighten(r, g, b uint8, amount float64) (uint8, uint8, uint8) {
|
||||||
|
return multiply(r, amount), multiply(g, amount), multiply(b, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func multiply(i uint8, amount float64) uint8 {
|
||||||
|
iF := float64(i)
|
||||||
|
return uint8(
|
||||||
|
math.Max(
|
||||||
|
0,
|
||||||
|
math.Min(
|
||||||
|
255, iF+255*amount,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Luminance(r, g, b uint8) float64 {
|
||||||
|
rC := Pred * math.Pow(float64(r), 2)
|
||||||
|
gC := Pgreen * math.Pow(float64(g), 2)
|
||||||
|
bC := Pblue * math.Pow(float64(b), 2)
|
||||||
|
|
||||||
|
return math.Sqrt(rC + gC + bC)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WCAGRatio(l1, l2 float64) float64 {
|
||||||
|
if l1 < l2 {
|
||||||
|
return (l1 + 0.05) / (l2 + 0.05)
|
||||||
|
} else {
|
||||||
|
return (l2 + 0.05) / (l1 + 0.05)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,11 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WCAGAAA float64 = 0.14285714285714285
|
||||||
|
WCAGAA float64 = 0.25
|
||||||
|
)
|
||||||
|
|
||||||
func TestIntToRgb(t *testing.T) {
|
func TestIntToRgb(t *testing.T) {
|
||||||
r, g, b := utils.IntToRgb(0x123456)
|
r, g, b := utils.IntToRgb(0x123456)
|
||||||
assert.Equal(t, uint8(0x12), r, "red")
|
assert.Equal(t, uint8(0x12), r, "red")
|
||||||
|
@ -27,3 +32,39 @@ func TestIsDarkColor(t *testing.T) {
|
||||||
isReallyQuestionable := utils.IsDarkColor(0x00, 0x88, 0x00)
|
isReallyQuestionable := utils.IsDarkColor(0x00, 0x88, 0x00)
|
||||||
assert.True(t, isReallyQuestionable)
|
assert.True(t, isReallyQuestionable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBrighten(t *testing.T) {
|
||||||
|
r, g, b := utils.Brighten(0, 0, 0, 0.1)
|
||||||
|
assert.Equal(t, uint8(0x19), r)
|
||||||
|
assert.Equal(t, uint8(0x19), g)
|
||||||
|
assert.Equal(t, uint8(0x19), b)
|
||||||
|
// assert.LessOrEqual(t, WCAGAA, utils.WCAGRatio(
|
||||||
|
// utils.Luminance(0, 0, 0),
|
||||||
|
// utils.Luminance(r, g, b),
|
||||||
|
// ))
|
||||||
|
|
||||||
|
r, g, b = utils.Brighten(0x88, 0x88, 0x88, -0.1)
|
||||||
|
assert.Equal(t, uint8(0x88-0x19-1), r)
|
||||||
|
assert.Equal(t, uint8(0x88-0x19-1), g)
|
||||||
|
assert.Equal(t, uint8(0x88-0x19-1), b)
|
||||||
|
// assert.LessOrEqual(t, WCAGAA, utils.WCAGRatio(
|
||||||
|
// utils.Luminance(0x88, 0x88, 0x88),
|
||||||
|
// utils.Luminance(r, g, b),
|
||||||
|
// ))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRgbToString(t *testing.T) {
|
||||||
|
assert.Equal(t, "#acab69", utils.RgbToString(0xac, 0xab, 0x69))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAltColor(t *testing.T) {
|
||||||
|
r, g, b := utils.AltColor(0xa2, 0xc2, 0x42)
|
||||||
|
assert.Equal(t, uint8(0x09), r, "red")
|
||||||
|
assert.Equal(t, uint8(0x29), g, "green")
|
||||||
|
assert.Equal(t, uint8(0x00), b, "blue")
|
||||||
|
|
||||||
|
r, g, b = utils.AltColor(0xa2, 0x15, 0x18)
|
||||||
|
assert.Equal(t, uint8(0xff), r, "red")
|
||||||
|
assert.Equal(t, uint8(0xed), g, "green")
|
||||||
|
assert.Equal(t, uint8(0xf0), b, "blue")
|
||||||
|
}
|
||||||
|
|
15
utils/emojis_test.go
Normal file
15
utils/emojis_test.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package utils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sapphic.engineer/roleypoly/v4/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRandomEmoji(t *testing.T) {
|
||||||
|
for i := len(utils.AllEmojis) * 1000; i >= 0; i-- {
|
||||||
|
e := utils.GetRandomEmoji()
|
||||||
|
assert.Containsf(t, e.Name, "roleypolynext", "not valid on iteration %d", i)
|
||||||
|
}
|
||||||
|
}
|
15
utils/template_fns.go
Normal file
15
utils/template_fns.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.sapphic.engineer/roleypoly/v4/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RoleInputID(c *types.Category, r *types.Role) string {
|
||||||
|
return fmt.Sprintf("category-%s_role-%s", c.ID, r.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RoleInputName(c *types.Category) string {
|
||||||
|
return fmt.Sprintf("category_group_%s", c.ID)
|
||||||
|
}
|
17
utils/template_fns_test.go
Normal file
17
utils/template_fns_test.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package utils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sapphic.engineer/roleypoly/v4/types"
|
||||||
|
"git.sapphic.engineer/roleypoly/v4/utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRoleInputID(t *testing.T) {
|
||||||
|
assert.Equal(t, "category-123_role-456", utils.RoleInputID(&types.Category{ID: "123"}, &types.Role{ID: "456"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoleInputName(t *testing.T) {
|
||||||
|
assert.Equal(t, "category_group_123", utils.RoleInputName(&types.Category{ID: "123"}))
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue