`projects.color` has been in the schema since the first migration, has defaulted to 'graphite' on every row ever written, and nothing has ever read it. There was no palette, no name-to-colour mapping, and the dot in the sidebar was hardcoded to a border grey. So this is not "add a picker"; it is implementing colour for the first time. Names, not hex values. A hex in ten thousand rows is a decision nobody can revisit; a name is resolved when the interface paints and can be retuned for a new theme or a new display. It is also the difference between a database that stores a fact and one that stores a rendering. The set is closed and refused at the edge — an unknown name would be stored happily and painted as the default, which looks like a colour that did not save. One palette across all five themes, and this is the one place in app.css that is not redefined per theme. Everything else there is text, a ground or a border and has to be measured against what sits behind it. These are small solid marks beside a label that already carries the meaning: what they owe is to be told apart, and to be visible on both a near-black and a white ground, which is why all ten sit in the mid-tone band. It also keeps them out of the trap the themes are in — a colour added to four blocks and forgotten in the fifth looks finished and falls back silently. There is no fifth block to forget. The list is written down three times: Go for validation, colors.js for the picker, app.css for the tokens that paint. So a test reads the other two — the same small-scanner approach openapi_test.go takes to the spec — and fails if the server accepts a colour nothing can draw, or the picker offers one the server refuses. Groups get a colour of their own (migration 0006) and projects inside a group are indented. The indent is to the group's *name*, not to its dot: the eye follows the text, and lining the projects up under the dot leaves them looking a step short of where the heading starts. The drop line is indented with them, so a drop inside a group reads as landing in the group. The group's dot is the control as well as the preview — you press the thing you are about to change, as on the project page. That was not the first design: a third text action, "Farve", sat beside Omdøb and Slet, and those carry `opacity: 0` rather than `display: none`, so they take their width whether or not you can see them. A group called "Arbejde" rendered as "ARBEJD". Moving the control onto the dot gave the name its room back. The smoke test measures the indent at the content edge rather than the element's, because the indent is padding and the box does not move — which is how the first version of that assertion passed 12 against 12. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
40 lines
1.2 KiB
Go
40 lines
1.2 KiB
Go
package store
|
|
|
|
// The colours a project, group, label or filter may be marked with.
|
|
//
|
|
// Names rather than hex values, and the reason is what happens later: a hex in
|
|
// ten thousand rows is a decision nobody can revisit, while a name is resolved by
|
|
// the interface at the moment it paints and can be retuned for a new theme, a new
|
|
// palette, or a display nobody has yet. It is also the difference between a
|
|
// database that stores a fact and one that stores a rendering.
|
|
//
|
|
// The list is closed. An unknown name would be stored happily and then paint as
|
|
// the default, which looks like a colour that did not save — so it is refused at
|
|
// the edge instead. web/src/lib/colors.js carries the same ids for the picker, and
|
|
// a test reads that file to check the two have not drifted.
|
|
var Colors = []string{
|
|
"graphite",
|
|
"tomato",
|
|
"amber",
|
|
"lime",
|
|
"green",
|
|
"teal",
|
|
"blue",
|
|
"indigo",
|
|
"violet",
|
|
"magenta",
|
|
}
|
|
|
|
// DefaultColor is what an unmarked thing is, and what the schema defaults to.
|
|
const DefaultColor = "graphite"
|
|
|
|
// ValidColor reports whether a name is one this app can paint.
|
|
func ValidColor(name string) bool {
|
|
for _, c := range Colors {
|
|
if c == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|