Implement color palette using Sass maps

I hand-picked the gray tones, the other colors are from the Tailwind CSS v3 palette.

Significant changes:
- light theme is brighter
- dark theme is darker
- borders are softer

I also deleted some platform- and night-mode-specific code.
This commit is contained in:
Matthias Metelka 2022-08-16 14:54:12 +02:00
parent fb197aa32e
commit 6c48dbcc7f
10 changed files with 414 additions and 173 deletions

View file

@ -16,9 +16,9 @@ py_binary(
)
py_binary(
name = "extract_sass_colors",
name = "extract_sass_vars",
srcs = [
"tools/extract_sass_colors.py",
"tools/extract_sass_vars.py",
],
imports = ["."],
visibility = [":__subpackages__"],

View file

@ -11,14 +11,15 @@ genrule(
)
genrule(
name = "extract_sass_colors",
name = "extract_sass_vars",
srcs = [
"//sass:_colors.scss",
"//sass:_vars.scss",
],
outs = ["colors.py"],
cmd = "$(location //qt:extract_sass_colors) $< $@",
outs = ["colors.py", "props.py"],
cmd = "$(location //qt:extract_sass_vars) $(SRCS) $(OUTS)",
tools = [
"//qt:extract_sass_colors",
"//qt:extract_sass_vars",
],
)

View file

@ -162,7 +162,7 @@ class Editor:
context=self,
default_css=False,
)
self.web._fix_editor_background_color_and_show()
self.web.show()
lefttopbtns: list[str] = []
gui_hooks.editor_did_init_left_buttons(lefttopbtns, self)

View file

@ -251,6 +251,9 @@ QTabWidget {{ background-color: {}; }}
if not self.night_mode:
app.setStyle(QStyleFactory.create(self._default_style)) # type: ignore
self.default_palette.setColor(
QPalette.ColorRole.Window, self.qcolor(colors.WINDOW_BG)
)
app.setPalette(self.default_palette)
return

View file

@ -233,9 +233,7 @@ class AnkiWebView(QWebEngineView):
self.set_title(title)
self._page = AnkiWebPage(self._onBridgeCmd)
# reduce flicker
self._page.setBackgroundColor(
self.get_window_bg_color(theme_manager.night_mode)
)
self._page.setBackgroundColor(QColor(theme_manager.color(colors.WINDOW_BG)))
# in new code, use .set_bridge_command() instead of setting this directly
self.onBridgeCmd: Callable[[str], Any] = self.defaultOnBridgeCmd
@ -404,15 +402,6 @@ class AnkiWebView(QWebEngineView):
else:
return 3
def get_window_bg_color(self, night_mode: bool) -> QColor:
if night_mode:
return QColor(colors.WINDOW_BG[1])
elif is_mac:
# standard palette does not return correct window color on macOS
return QColor("#ececec")
else:
return theme_manager.default_palette.color(QPalette.ColorRole.Window)
def standard_css(self) -> str:
palette = theme_manager.default_palette
color_hl = palette.color(QPalette.ColorRole.Highlight).name()
@ -459,15 +448,12 @@ div[contenteditable="true"]:focus {{
zoom = self.app_zoom_factor()
window_bg_day = self.get_window_bg_color(False).name()
window_bg_night = self.get_window_bg_color(True).name()
return f"""
body {{ zoom: {zoom}; background-color: var(--window-bg); }}
html {{ {font} }}
{button_style}
:root {{ --window-bg: {window_bg_day} }}
:root[class*=night-mode] {{ --window-bg: {window_bg_night} }}
:root {{ --window-bg: {colors.WINDOW_BG[0]} }}
:root[class*=night-mode] {{ --window-bg: {colors.WINDOW_BG[1]} }}
"""
def stdHtml(
@ -712,7 +698,9 @@ html {{ {font} }}
def on_theme_did_change(self) -> None:
# avoid flashes if page reloaded
self._page.setBackgroundColor(
self.get_window_bg_color(theme_manager.night_mode)
QColor(colors.WINDOW_BG[1])
if theme_manager.night_mode
else QColor(colors.WINDOW_BG[0])
)
# update night-mode class, and legacy nightMode/night-mode body classes
self.eval(
@ -732,30 +720,3 @@ html {{ {font} }}
}})();
"""
)
def _fix_editor_background_color_and_show(self) -> None:
# The editor does not use our standard CSS, which takes care of matching the background
# colour of the webview to the window we're showing it in. This causes a difference in
# shades on Windows/Linux in day mode, that we need to work around. This is a temporary
# fix before the 2.1.50 release; with more time there may be a better way to do this.
if theme_manager.night_mode:
# The styling changes are not required for night mode, and hiding+showing the
# webview causes a flash of black.
return
self.hide()
window_bg_day = self.get_window_bg_color(False).name()
css = f":root {{ --window-bg: {window_bg_day} }}"
self.evalWithCallback(
f"""
(function(){{
const style = document.createElement('style');
style.innerHTML = `{css}`;
document.head.appendChild(style);
}})();
""",
# avoids FOUC
lambda _: self.show(),
)

View file

@ -1,45 +0,0 @@
#!/usr/bin/env python3
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import json
import re
import sys
input_scss = sys.argv[1]
output_py = sys.argv[2]
colors = {}
for line in open(input_scss):
line = line.strip()
if not line:
continue
m = re.match(r"--(.+): (.+);$", line)
if not m:
if (
line != "}"
and not ":root" in line
and "Copyright" not in line
and "License" not in line
and "color-scheme" not in line
):
print("failed to match", line)
continue
var = m.group(1)
val = m.group(2)
colors.setdefault(var, []).append(val)
with open(output_py, "w") as buf:
buf.write(
"""\
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
"""
)
buf.write("# this file is auto-generated from _vars.scss\n")
for color, (day, night) in colors.items():
color = color.replace("-", "_").upper()
buf.write(f'{color} = ("{day}", "{night}")\n')

View file

@ -0,0 +1,89 @@
#!/usr/bin/env python3
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import json
import re
import sys
# bazel genrule "srcs"
colors_scss = sys.argv[1]
vars_scss = sys.argv[2]
# bazel genrule "outs"
colors_py = sys.argv[3]
props_py = sys.argv[4]
palette = {}
colors = {}
props = {}
color = ""
for line in open(colors_scss):
line = line.strip()
if not line:
continue
if m := re.match(r"^([a-z]+): \($", line):
color = m.group(1)
palette[color] = {}
elif m := re.match(r"(\d): (.+),$", line):
palette[color][m.group(1)] = m.group(2)
# TODO: recursive extraction of arbitrarily nested Sass maps?
reached_colors = False
for line in open(vars_scss):
line = line.strip()
if not line:
continue
if line == "themes: (":
reached_colors = True
continue
if reached_colors:
line = re.sub(
r"get\(\$color, (.+), (\d)\)",
lambda m: palette[m.group(1)][m.group(2)],
line,
)
if m := re.match(r"^(.+): ([^\(]+),$", line):
var = m.group(1)
val = m.group(2)
if reached_colors:
colors.setdefault(var, []).append(val)
else:
props.setdefault(var, []).append(val)
copyright_notice = """\
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
"""
with open(colors_py, "w") as buf:
buf.write(copyright_notice)
buf.write("# this file is auto-generated from _vars.scss and _colors.scss\n")
for color, (day, night) in colors.items():
color = color.replace("-", "_").upper()
buf.write(f'{color} = ("{day}", "{night}")\n')
with open(props_py, "w") as buf:
buf.write(copyright_notice)
buf.write("# this file is auto-generated from _vars.scss\n")
for prop, val in props.items():
day = val[0]
night = val[1] if len(val) > 1 else day
prop = prop.replace("-", "_").upper()
buf.write(f'{prop} = ("{day}", "{night}")\n')

View file

@ -84,6 +84,6 @@ sass_library(
)
exports_files(
["_vars.scss"],
["_colors.scss", "_vars.scss"],
visibility = ["//visibility:public"],
)

203
sass/_colors.scss Normal file
View file

@ -0,0 +1,203 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later, http://www.gnu.org/licenses/agpl.html */
/*
* Anki Color Palette
* custom gray, rest from Tailwind CSS v3 palette
*
* */
$color: (
gray: (
0: #fcfdfe,
1: #f2f5f8,
2: #dde1e7,
3: #aeb5be,
4: #7a818a,
5: #4a5058,
6: #2d3138,
7: #181b1f,
8: #101215,
9: #0b0c0e,
),
red: (
0: #fef2f2,
1: #fee2e2,
2: #fecaca,
3: #fca5a5,
4: #f87171,
5: #ef4444,
6: #dc2626,
7: #b91c1c,
8: #991b1b,
9: #7f1d1d,
),
orange: (
0: #fff7ed,
1: #ffedd5,
2: #fed7aa,
3: #fdba74,
4: #fb923c,
5: #f97316,
6: #ea580c,
7: #c2410c,
8: #9a3412,
9: #7c2d12,
),
amber: (
0: #fffbeb,
1: #fef3c7,
2: #fde68a,
3: #fcd34d,
4: #fbbf24,
5: #f59e0b,
6: #d97706,
7: #b45309,
8: #92400e,
9: #78350f,
),
yellow: (
0: #fefce8,
1: #fef9c3,
2: #fef08a,
3: #fde047,
4: #facc15,
5: #eab308,
6: #ca8a04,
7: #a16207,
8: #854d0e,
9: #713f12,
),
lime: (
0: #f7fee7,
1: #ecfccb,
2: #d9f99d,
3: #bef264,
4: #a3e635,
5: #84cc16,
6: #65a30d,
7: #4d7c0f,
8: #3f6212,
9: #365314,
),
green: (
0: #f0fdf4,
1: #dcfce7,
2: #bbf7d0,
3: #86efac,
4: #4ade80,
5: #22c55e,
6: #16a34a,
7: #15803d,
8: #166534,
9: #14532d,
),
teal: (
0: #f0fdfa,
1: #ccfbf1,
2: #99f6e4,
3: #5eead4,
4: #2dd4bf,
5: #14b8a6,
6: #0d9488,
7: #0f766e,
8: #115e59,
9: #134e4a,
),
cyan: (
0: #ecfeff,
1: #cffafe,
2: #a5f3fc,
3: #67e8f9,
4: #22d3ee,
5: #06b6d4,
6: #0891b2,
7: #0e7490,
8: #155e75,
9: #164e63,
),
sky: (
0: #f0f9ff,
1: #e0f2fe,
2: #bae6fd,
3: #7dd3fc,
4: #38bdf8,
5: #0ea5e9,
6: #0284c7,
7: #0369a1,
8: #075985,
9: #0c4a6e,
),
blue: (
0: #eff6ff,
1: #dbeafe,
2: #bfdbfe,
3: #93c5fd,
4: #60a5fa,
5: #3b82f6,
6: #2563eb,
7: #1d4ed8,
8: #1e40af,
9: #1e3a8a,
),
indigo: (
0: #eef2ff,
1: #e0e7ff,
2: #c7d2fe,
3: #a5b4fc,
4: #818cf8,
5: #6366f1,
6: #4f46e5,
7: #4338ca,
8: #3730a3,
9: #312e81,
),
violet: (
0: #f5f3ff,
1: #ede9fe,
2: #ddd6fe,
3: #c4b5fd,
4: #a78bfa,
5: #8b5cf6,
6: #7c3aed,
7: #6d28d9,
8: #5b21b6,
9: #4c1d95,
),
purple: (
0: #faf5ff,
1: #f3e8ff,
2: #e9d5ff,
3: #d8b4fe,
4: #c084fc,
5: #a855f7,
6: #9333ea,
7: #7e22ce,
8: #6b21a8,
9: #581c87,
),
fuchsia: (
0: #fdf4ff,
1: #fae8ff,
2: #f5d0fe,
3: #f0abfc,
4: #e879f9,
5: #d946ef,
6: #c026d3,
7: #a21caf,
8: #86198f,
9: #701a75,
),
pink: (
0: #fdf2f8,
1: #fce7f3,
2: #fbcfe8,
3: #f9a8d4,
4: #f472b6,
5: #ec4899,
6: #db2777,
7: #be185d,
8: #9d174d,
9: #831843,
),
);

View file

@ -1,81 +1,110 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
* License: GNU AGPL, version 3 or later, http://www.gnu.org/licenses/agpl.html */
@use "colors" as *;
@function get($map, $keys...) {
@each $key in $keys {
$map: map-get($map, $key);
}
@return $map;
}
$vars: (
props: (
border-radius-default: 5px,
),
themes: (
light: (
text-fg: get($color, gray, 9),
window-bg: get($color, gray, 1),
frame-bg: white,
border: get($color, gray, 4),
faint-border: get($color, gray, 2),
link: get($color, blue, 9),
review-count: #0a0,
new-count: get($color, blue, 9),
learn-count: get($color, orange, 7),
zero-count: get($color, gray, 2),
slightly-grey-text: get($color, gray, 8),
highlight-bg: get($color, cyan, 2),
highlight-fg: black,
disabled: get($color, gray, 5),
flag1-fg: get($color, red, 6),
flag2-fg: get($color, orange, 5),
flag3-fg: get($color, green, 5),
flag4-fg: get($color, blue, 5),
flag5-fg: get($color, fuchsia, 4),
flag6-fg: get($color, teal, 5),
flag7-fg: get($color, purple, 6),
flag1-bg: get($color, red, 5),
flag2-bg: get($color, orange, 3),
flag3-bg: get($color, green, 3),
flag4-bg: get($color, blue, 3),
flag5-bg: get($color, fuchsia, 2),
flag6-bg: get($color, teal, 4),
flag7-bg: get($color, purple, 4),
buried-fg: get($color, amber, 5),
suspended-fg: get($color, yellow, 4),
suspended-bg: get($color, yellow, 1),
marked-bg: get($color, indigo, 2),
tooltip-bg: get($color, gray, 0),
focus-border: get($color, indigo, 6),
),
dark: (
text-fg: get($color, gray, 0),
window-bg: get($color, gray, 7),
frame-bg: get($color, gray, 6),
border: get($color, gray, 9),
faint-border: get($color, gray, 8),
link: get($color, blue, 2),
review-count: get($color, green, 4),
new-count: get($color, blue, 2),
learn-count: get($color, red, 4),
zero-count: get($color, gray, 7),
slightly-grey-text: get($color, gray, 4),
highlight-bg: get($color, cyan, 2),
highlight-fg: white,
disabled: get($color, gray, 6),
flag1-fg: get($color, red, 4),
flag2-fg: get($color, orange, 4),
flag3-fg: get($color, green, 4),
flag4-fg: get($color, blue, 4),
flag5-fg: get($color, fuchsia, 3),
flag6-fg: get($color, teal, 4),
flag7-fg: get($color, purple, 5),
flag1-bg: get($color, red, 6),
flag2-bg: get($color, orange, 5),
flag3-bg: get($color, green, 5),
flag4-bg: get($color, blue, 5),
flag5-bg: get($color, fuchsia, 5),
flag6-bg: get($color, teal, 5),
flag7-bg: get($color, purple, 7),
buried-fg: get($color, amber, 8),
suspended-fg: get($color, yellow, 1),
suspended-bg: get($color, yellow, 5),
marked-bg: get($color, purple, 5),
tooltip-bg: get($color, gray, 8),
focus-border: get($color, indigo, 5),
),
),
);
:root {
--text-fg: black;
--window-bg: #ececec;
--frame-bg: white;
--border: #aaa;
--medium-border: #b6b6b6;
--faint-border: #e7e7e7;
--link: #00a;
--review-count: #0a0;
--new-count: #00a;
--learn-count: #c35617;
--zero-count: #ddd;
--slightly-grey-text: #333;
--highlight-bg: #77ccff;
--highlight-fg: black;
--disabled: #777;
--flag1-fg: #e25252;
--flag2-fg: #ffb347;
--flag3-fg: #54c414;
--flag4-fg: #578cff;
--flag5-fg: #ff82ee;
--flag6-fg: #00d1b5;
--flag7-fg: #9649dd;
--flag1-bg: #ff9b9b;
--flag2-bg: #ffb347;
--flag3-bg: #93e066;
--flag4-bg: #9dbcff;
--flag5-bg: #f5a8eb;
--flag6-bg: #7edbd7;
--flag7-bg: #cca3f1;
--buried-fg: #aaaa33;
--suspended-fg: #dd0;
--suspended-bg: #ffffb2;
--marked-bg: #cce;
--tooltip-bg: #fcfcfc;
--focus-border: #0969da;
--focus-shadow: rgba(9 105 218 / 0.3);
@each $name, $value in get($vars, themes, light) {
--#{$name}: #{$value};
}
&.night-mode {
@each $name, $value in get($vars, themes, dark) {
--#{$name}: #{$value};
}
:root[class*="night-mode"] {
--text-fg: white;
--window-bg: #2f2f31;
--frame-bg: #3a3a3a;
--border: #777;
--medium-border: #444;
--faint-border: #29292b;
--link: #77ccff;
--review-count: #5ccc00;
--new-count: #77ccff;
--learn-count: #ff935b;
--zero-count: #444;
--slightly-grey-text: #ccc;
--highlight-bg: #77ccff;
--highlight-fg: white;
--disabled: #777;
--flag1-fg: #ff7b7b;
--flag2-fg: #f5aa41;
--flag3-fg: #86ce5d;
--flag4-fg: #6f9dff;
--flag5-fg: #f097e4;
--flag6-fg: #5ccfca;
--flag7-fg: #9f63d3;
--flag1-bg: #aa5555;
--flag2-bg: #ac653a;
--flag3-bg: #559238;
--flag4-bg: #506aa3;
--flag5-bg: #975d8f;
--flag6-bg: #399185;
--flag7-bg: #624b77;
--buried-fg: #777733;
--suspended-fg: #ffffb2;
--suspended-bg: #aaaa33;
--marked-bg: #77c;
--tooltip-bg: #272727;
--focus-border: #316dca;
--focus-shadow: #194380;
color-scheme: dark;
}
}
/* separate rule for cleaner dev tools appearance */
:root {
@each $name, $value in get($vars, props) {
--#{$name}: #{$value};
}
}