diff --git a/qt/BUILD.bazel b/qt/BUILD.bazel index be49bc6a6..3c1db25f5 100644 --- a/qt/BUILD.bazel +++ b/qt/BUILD.bazel @@ -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__"], diff --git a/qt/aqt/BUILD.bazel b/qt/aqt/BUILD.bazel index 9248b51af..65fba5a32 100644 --- a/qt/aqt/BUILD.bazel +++ b/qt/aqt/BUILD.bazel @@ -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", ], ) diff --git a/qt/aqt/browser/sidebar/tree.py b/qt/aqt/browser/sidebar/tree.py index cbdaca784..305858c49 100644 --- a/qt/aqt/browser/sidebar/tree.py +++ b/qt/aqt/browser/sidebar/tree.py @@ -104,7 +104,7 @@ class SidebarTreeView(QTreeView): def _setup_style(self) -> None: # match window background color and tweak style bgcolor = QPalette().window().color().name() - border = theme_manager.color(colors.MEDIUM_BORDER) + border = theme_manager.color(colors.BORDER_DEFAULT) styles = [ "padding: 3px", "padding-right: 0px", @@ -303,7 +303,7 @@ class SidebarTreeView(QTreeView): ) -> None: if self.current_search and (item := self.model().item_for_index(idx)): if item.is_highlighted(): - brush = QBrush(theme_manager.qcolor(colors.SUSPENDED_BG)) + brush = QBrush(theme_manager.qcolor(colors.STATE_SUSPENDED)) painter.save() painter.fillRect(options.rect, brush) painter.restore() @@ -653,36 +653,36 @@ class SidebarTreeView(QTreeView): type=SidebarItemType.CARD_STATE_ROOT, ) type = SidebarItemType.CARD_STATE - colored_icon = ColoredIcon(path=icon, color=colors.DISABLED) + colored_icon = ColoredIcon(path=icon, color=colors.FG_DISABLED) root.add_simple( tr.actions_new(), - icon=colored_icon.with_color(colors.NEW_COUNT), + icon=colored_icon.with_color(colors.STATE_NEW), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_NEW), ) root.add_simple( name=tr.scheduling_learning(), - icon=colored_icon.with_color(colors.LEARN_COUNT), + icon=colored_icon.with_color(colors.STATE_LEARN), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_LEARN), ) root.add_simple( name=tr.scheduling_review(), - icon=colored_icon.with_color(colors.REVIEW_COUNT), + icon=colored_icon.with_color(colors.STATE_REVIEW), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_REVIEW), ) root.add_simple( name=tr.browsing_suspended(), - icon=colored_icon.with_color(colors.SUSPENDED_FG), + icon=colored_icon.with_color(colors.STATE_SUSPENDED), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_SUSPENDED), ) root.add_simple( name=tr.browsing_buried(), - icon=colored_icon.with_color(colors.BURIED_FG), + icon=colored_icon.with_color(colors.STATE_BURIED), type=type, search_node=SearchNode(card_state=SearchNode.CARD_STATE_BURIED), ) diff --git a/qt/aqt/browser/table/__init__.py b/qt/aqt/browser/table/__init__.py index 2cfd96b46..314ac742c 100644 --- a/qt/aqt/browser/table/__init__.py +++ b/qt/aqt/browser/table/__init__.py @@ -13,6 +13,7 @@ from anki.collection import BrowserColumns as Columns from anki.collection import BrowserRow from anki.notes import NoteId from aqt import colors +from aqt.qt import QColor from aqt.utils import tr Column = Columns.Column @@ -76,25 +77,38 @@ class CellRow: def backend_color_to_aqt_color(color: BrowserRow.Color.V) -> tuple[str, str] | None: + temp_color = None + if color == BrowserRow.COLOR_MARKED: - return colors.MARKED_BG + temp_color = colors.STATE_MARKED if color == BrowserRow.COLOR_SUSPENDED: - return colors.SUSPENDED_BG + temp_color = colors.STATE_SUSPENDED if color == BrowserRow.COLOR_FLAG_RED: - return colors.FLAG1_BG + temp_color = colors.FLAG_1 if color == BrowserRow.COLOR_FLAG_ORANGE: - return colors.FLAG2_BG + temp_color = colors.FLAG_2 if color == BrowserRow.COLOR_FLAG_GREEN: - return colors.FLAG3_BG + temp_color = colors.FLAG_3 if color == BrowserRow.COLOR_FLAG_BLUE: - return colors.FLAG4_BG + temp_color = colors.FLAG_4 if color == BrowserRow.COLOR_FLAG_PINK: - return colors.FLAG5_BG + temp_color = colors.FLAG_5 if color == BrowserRow.COLOR_FLAG_TURQUOISE: - return colors.FLAG6_BG + temp_color = colors.FLAG_6 if color == BrowserRow.COLOR_FLAG_PURPLE: - return colors.FLAG7_BG - return None + temp_color = colors.FLAG_7 + + return adjusted_bg_color(temp_color) + + +def adjusted_bg_color(color: tuple[str, str]) -> tuple[str, str]: + if color: + return ( + QColor(color[0]).lighter(150).name(), + QColor(color[1]).darker(150).name(), + ) + else: + return None from .model import DataModel diff --git a/qt/aqt/browser/table/table.py b/qt/aqt/browser/table/table.py index 29cdb67db..56de065a2 100644 --- a/qt/aqt/browser/table/table.py +++ b/qt/aqt/browser/table/table.py @@ -375,7 +375,7 @@ class Table: ) elif theme_manager.macos_dark_mode(): self._view.setStyleSheet( - f"QTableView {{ gridline-color: {colors.FRAME_BG} }}" + f"QTableView {{ gridline-color: {colors.CANVAS_INSET} }}" ) else: self._view.setStyleSheet("") diff --git a/qt/aqt/data/web/css/deckbrowser.scss b/qt/aqt/data/web/css/deckbrowser.scss index 4e32bcc46..f955b5e3b 100644 --- a/qt/aqt/data/web/css/deckbrowser.scss +++ b/qt/aqt/data/web/css/deckbrowser.scss @@ -4,7 +4,7 @@ @use "sass/card-counts"; a.deck { - color: var(--text-fg); + color: var(--fg-default); text-decoration: none; min-width: 5em; display: inline-block; @@ -15,7 +15,7 @@ a.deck:hover { } tr.deck td { - border-bottom: 1px solid var(--faint-border); + border-bottom: 1px solid var(--border-subtle); } tr.top-level-drag-row td { @@ -27,7 +27,7 @@ td { } tr.drag-hover td { - border-bottom: 1px solid var(--border); + border-bottom: 1px solid var(--border-default); } body { @@ -36,7 +36,7 @@ body { } .current { - background-color: var(--faint-border); + background-color: var(--canvas-outset); } .decktd { @@ -55,14 +55,14 @@ body { } .collapse { - color: var(--text-fg); + color: var(--fg-default); text-decoration: none; display: inline-block; width: 1em; } .filtered { - color: var(--link) !important; + color: var(--accent-link) !important; } .gears { @@ -79,7 +79,7 @@ body { } .callout { - background: var(--medium-border); + background: var(--border-default); padding: 1em; margin: 1em; diff --git a/qt/aqt/data/web/css/overview.scss b/qt/aqt/data/web/css/overview.scss index 23565f573..719581eef 100644 --- a/qt/aqt/data/web/css/overview.scss +++ b/qt/aqt/data/web/css/overview.scss @@ -13,7 +13,7 @@ h3 { .descfont { padding: 1em; - color: var(--slightly-grey-text); + color: var(--fg-subtle); } .description { diff --git a/qt/aqt/data/web/css/reviewer-bottom.scss b/qt/aqt/data/web/css/reviewer-bottom.scss index 61e421a14..72d408981 100644 --- a/qt/aqt/data/web/css/reviewer-bottom.scss +++ b/qt/aqt/data/web/css/reviewer-bottom.scss @@ -1,10 +1,11 @@ /* Copyright: Ankitects Pty Ltd and contributors * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ -@use "sass/card-counts"; + @use "vars" as *; + @use "sass/card-counts"; :root { - --focus-color: #0078d7; + --focus-color: #{palette-of(button-focus)}; .isMac { --focus-color: rgba(0 103 244 / 0.247); @@ -73,13 +74,13 @@ button { } #outer { - border-top: 1px solid var(--border); + border-top: 1px solid var(--border-default); /* Better compatibility with graphics pad/touchscreen */ -webkit-user-select: none; } .nightMode { #outer { - border-top-color: var(--faint-border); + border-top-color: var(--border-subtle); } } diff --git a/qt/aqt/data/web/css/toolbar.scss b/qt/aqt/data/web/css/toolbar.scss index 235387461..b07dcd81d 100644 --- a/qt/aqt/data/web/css/toolbar.scss +++ b/qt/aqt/data/web/css/toolbar.scss @@ -4,7 +4,7 @@ #header { padding: 3px; font-weight: bold; - border-bottom: 1px solid var(--border); + border-bottom: 1px solid var(--border-default); } .tdcenter { @@ -26,7 +26,7 @@ body { padding-right: 12px; padding-left: 12px; text-decoration: none; - color: var(--text-fg); + color: var(--fg-default); } .hitem:hover { @@ -38,11 +38,11 @@ body { } .nightMode #header { - border-bottom-color: var(--faint-border); + border-bottom-color: var(--border-subtle); } .isMac.nightMode #header { - border-bottom-color: var(--frame-bg); + border-bottom-color: var(--canvas-outset); } @keyframes spin { @@ -71,9 +71,9 @@ body { } .normal-sync { - color: var(--new-count); + color: var(--state-new); } .full-sync { - color: var(--learn-count); + color: var(--state-learn); } diff --git a/qt/aqt/data/web/css/webview.scss b/qt/aqt/data/web/css/webview.scss index f96d5f65b..1c13b80a6 100644 --- a/qt/aqt/data/web/css/webview.scss +++ b/qt/aqt/data/web/css/webview.scss @@ -12,8 +12,8 @@ } body { - color: var(--text-fg); - background: var(--window-bg); + color: var(--fg-default); + background: var(--canvas-default); transition: opacity 0.5s ease-out; margin: 2em; overscroll-behavior: none; @@ -21,7 +21,7 @@ body { } a { - color: var(--link); + color: var(--accent-link); text-decoration: none; } diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index f55eef4e3..f297717e4 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -164,7 +164,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) @@ -670,7 +670,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too self.tags = aqt.tagedit.TagEdit(self.widget) qconnect(self.tags.lostFocus, self.on_tag_focus_lost) self.tags.setToolTip(shortcut(tr.editing_jump_to_tags_with_ctrlandshiftandt())) - border = theme_manager.color(colors.BORDER) + border = theme_manager.color(colors.BORDER_DEFAULT) self.tags.setStyleSheet(f"border: 1px solid {border}") tb.addWidget(self.tags, 1, 1) g.setLayout(tb) diff --git a/qt/aqt/filtered_deck.py b/qt/aqt/filtered_deck.py index 2fa6c3924..1c5a38728 100644 --- a/qt/aqt/filtered_deck.py +++ b/qt/aqt/filtered_deck.py @@ -84,8 +84,8 @@ class FilteredDeckConfigDialog(QDialog): qconnect(self.form.search_button.clicked, self.on_search_button) qconnect(self.form.search_button_2.clicked, self.on_search_button_2) qconnect(self.form.hint_button.clicked, self.on_hint_button) - blue = theme_manager.color(colors.LINK) - grey = theme_manager.color(colors.DISABLED) + blue = theme_manager.color(colors.ACCENT_LINK) + grey = theme_manager.color(colors.FG_DISABLED) self.setStyleSheet( f"""QPushButton[label] {{ padding: 0; border: 0 }} QPushButton[label]:hover {{ text-decoration: underline }} diff --git a/qt/aqt/flags.py b/qt/aqt/flags.py index 7a3ea6862..5373206ab 100644 --- a/qt/aqt/flags.py +++ b/qt/aqt/flags.py @@ -61,55 +61,55 @@ class FlagManager: def _load_flags(self) -> None: labels = cast(dict[str, str], self.mw.col.get_config("flagLabels", {})) - icon = ColoredIcon(path="icons:flag-variant.svg", color=colors.DISABLED) + icon = ColoredIcon(path="icons:flag-variant.svg", color=colors.FG_DISABLED) self._flags = [ Flag( 1, labels["1"] if "1" in labels else tr.actions_flag_red(), - icon.with_color(colors.FLAG1_FG), + icon.with_color(colors.FLAG_1), SearchNode(flag=SearchNode.FLAG_RED), "actionRed_Flag", ), Flag( 2, labels["2"] if "2" in labels else tr.actions_flag_orange(), - icon.with_color(colors.FLAG2_FG), + icon.with_color(colors.FLAG_2), SearchNode(flag=SearchNode.FLAG_ORANGE), "actionOrange_Flag", ), Flag( 3, labels["3"] if "3" in labels else tr.actions_flag_green(), - icon.with_color(colors.FLAG3_FG), + icon.with_color(colors.FLAG_3), SearchNode(flag=SearchNode.FLAG_GREEN), "actionGreen_Flag", ), Flag( 4, labels["4"] if "4" in labels else tr.actions_flag_blue(), - icon.with_color(colors.FLAG4_FG), + icon.with_color(colors.FLAG_4), SearchNode(flag=SearchNode.FLAG_BLUE), "actionBlue_Flag", ), Flag( 5, labels["5"] if "5" in labels else tr.actions_flag_pink(), - icon.with_color(colors.FLAG5_FG), + icon.with_color(colors.FLAG_5), SearchNode(flag=SearchNode.FLAG_PINK), "actionPink_Flag", ), Flag( 6, labels["6"] if "6" in labels else tr.actions_flag_turquoise(), - icon.with_color(colors.FLAG6_FG), + icon.with_color(colors.FLAG_6), SearchNode(flag=SearchNode.FLAG_TURQUOISE), "actionTurquoise_Flag", ), Flag( 7, labels["7"] if "7" in labels else tr.actions_flag_purple(), - icon.with_color(colors.FLAG7_FG), + icon.with_color(colors.FLAG_7), SearchNode(flag=SearchNode.FLAG_PURPLE), "actionPurple_Flag", ), diff --git a/qt/aqt/switch.py b/qt/aqt/switch.py index ca92fafbc..239830258 100644 --- a/qt/aqt/switch.py +++ b/qt/aqt/switch.py @@ -19,8 +19,8 @@ class Switch(QAbstractButton): radius: int = 10, left_label: str = "", right_label: str = "", - left_color: tuple[str, str] = colors.FLAG4_BG, - right_color: tuple[str, str] = colors.FLAG3_BG, + left_color: tuple[str, str] = colors.ACCENT_CARD, + right_color: tuple[str, str] = colors.ACCENT_NOTE, parent: QWidget = None, ) -> None: super().__init__(parent=parent) @@ -103,12 +103,12 @@ class Switch(QAbstractButton): if theme_manager.night_mode: color = QColor(theme_manager.DARK_MODE_BUTTON_BG_MIDPOINT) else: - color = theme_manager.qcolor(colors.FRAME_BG) + color = theme_manager.qcolor(colors.CANVAS_OUTSET) painter.setBrush(QBrush(color)) painter.drawEllipse(self._current_knob_rectangle()) def _paint_label(self, painter: QPainter) -> None: - painter.setPen(theme_manager.qcolor(colors.SLIGHTLY_GREY_TEXT)) + painter.setPen(theme_manager.qcolor(colors.FG_DEFAULT)) font = painter.font() font.setPixelSize(int(1.2 * self._knob_radius)) painter.setFont(font) diff --git a/qt/aqt/theme.py b/qt/aqt/theme.py index 39f4b3916..eec623154 100644 --- a/qt/aqt/theme.py +++ b/qt/aqt/theme.py @@ -228,6 +228,9 @@ class ThemeManager: if not self.night_mode: app.setStyle(QStyleFactory.create(self._default_style)) # type: ignore + self.default_palette.setColor( + QPalette.ColorRole.Window, self.qcolor(colors.CANVAS_DEFAULT) + ) app.setPalette(self.default_palette) return @@ -236,11 +239,11 @@ class ThemeManager: palette = QPalette() - text_fg = self.qcolor(colors.TEXT_FG) - palette.setColor(QPalette.ColorRole.WindowText, text_fg) - palette.setColor(QPalette.ColorRole.ToolTipText, text_fg) - palette.setColor(QPalette.ColorRole.Text, text_fg) - palette.setColor(QPalette.ColorRole.ButtonText, text_fg) + TEXT = self.qcolor(colors.FG_DEFAULT) + palette.setColor(QPalette.ColorRole.WindowText, TEXT) + palette.setColor(QPalette.ColorRole.ToolTipText, TEXT) + palette.setColor(QPalette.ColorRole.Text, TEXT) + palette.setColor(QPalette.ColorRole.ButtonText, TEXT) hlbg = self.qcolor(colors.HIGHLIGHT_BG) hlbg.setAlpha(64) @@ -249,18 +252,21 @@ class ThemeManager: ) palette.setColor(QPalette.ColorRole.Highlight, hlbg) - window_bg = self.qcolor(colors.WINDOW_BG) - palette.setColor(QPalette.ColorRole.Window, window_bg) - palette.setColor(QPalette.ColorRole.AlternateBase, window_bg) + CANVAS_DEFAULT = self.qcolor(colors.CANVAS_DEFAULT) + palette.setColor(QPalette.ColorRole.Window, CANVAS_DEFAULT) + palette.setColor(QPalette.ColorRole.AlternateBase, CANVAS_DEFAULT) palette.setColor(QPalette.ColorRole.Button, QColor("#454545")) - frame_bg = self.qcolor(colors.FRAME_BG) - palette.setColor(QPalette.ColorRole.Base, frame_bg) - palette.setColor(QPalette.ColorRole.ToolTipBase, frame_bg) + CANVAS_INSET = self.qcolor(colors.CANVAS_INSET) + palette.setColor(QPalette.ColorRole.Base, CANVAS_INSET) + palette.setColor(QPalette.ColorRole.ToolTipBase, CANVAS_INSET) - disabled_color = self.qcolor(colors.DISABLED) - palette.setColor(QPalette.ColorRole.PlaceholderText, disabled_color) + palette.setColor( + QPalette.ColorRole.PlaceholderText, self.qcolor(colors.FG_SUBTLE) + ) + + disabled_color = self.qcolor(colors.FG_DISABLED) palette.setColor( QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_color ) @@ -273,7 +279,7 @@ class ThemeManager: disabled_color, ) - palette.setColor(QPalette.ColorRole.Link, self.qcolor(colors.LINK)) + palette.setColor(QPalette.ColorRole.Link, self.qcolor(colors.ACCENT_LINK)) palette.setColor(QPalette.ColorRole.BrightText, Qt.GlobalColor.red) @@ -282,11 +288,11 @@ class ThemeManager: def _update_stat_colors(self) -> None: import anki.stats as s - s.colLearn = self.color(colors.NEW_COUNT) - s.colRelearn = self.color(colors.LEARN_COUNT) - s.colCram = self.color(colors.SUSPENDED_BG) - s.colSusp = self.color(colors.SUSPENDED_BG) - s.colMature = self.color(colors.REVIEW_COUNT) + s.colLearn = self.color(colors.STATE_NEW) + s.colRelearn = self.color(colors.STATE_LEARN) + s.colCram = self.color(colors.STATE_SUSPENDED) + s.colSusp = self.color(colors.STATE_SUSPENDED) + s.colMature = self.color(colors.STATE_REVIEW) s._legacy_nightmode = self._night_mode_preference diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index f68b58926..75e93e0e2 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -234,7 +234,7 @@ class AnkiWebView(QWebEngineView): self._page = AnkiWebPage(self._onBridgeCmd) # reduce flicker self._page.setBackgroundColor( - self.get_window_bg_color(theme_manager.night_mode) + QColor(theme_manager.color(colors.CANVAS_DEFAULT)) ) # in new code, use .set_bridge_command() instead of setting this directly @@ -404,15 +404,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 +450,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); }} +body {{ zoom: {zoom}; background-color: var(--canvas-default); }} html {{ {font} }} {button_style} -:root {{ --window-bg: {window_bg_day} }} -:root[class*=night-mode] {{ --window-bg: {window_bg_night} }} +:root {{ --canvas-default: {colors.CANVAS_DEFAULT[0]} }} +:root[class*=night-mode] {{ --canvas-default: {colors.CANVAS_DEFAULT[1]} }} """ def stdHtml( @@ -712,7 +700,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.CANVAS_DEFAULT[1]) + if theme_manager.night_mode + else QColor(colors.CANVAS_DEFAULT[0]) ) # update night-mode class, and legacy nightMode/night-mode body classes self.eval( @@ -732,30 +722,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(), - ) diff --git a/qt/tools/extract_sass_colors.py b/qt/tools/extract_sass_colors.py deleted file mode 100644 index 219fd6a26..000000000 --- a/qt/tools/extract_sass_colors.py +++ /dev/null @@ -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') diff --git a/qt/tools/extract_sass_vars.py b/qt/tools/extract_sass_vars.py new file mode 100644 index 000000000..9898dc6f7 --- /dev/null +++ b/qt/tools/extract_sass_vars.py @@ -0,0 +1,111 @@ +#!/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 +current_key = "" + +for line in open(vars_scss): + line = line.strip() + + if not line or line == "props: (": + continue + if line == ":root {": + break + if line == "colors: (": + reached_colors = True + continue + if line == "),": + if "_" in current_key: + current_key = re.sub(r"_.+?$", "", current_key) + else: + current_key = "" + + if m := re.match(r"^([^$]+): \(", line): + if current_key == "": + current_key = m.group(1) + else: + current_key = "_".join([current_key, m.group(1)]) + continue + + if reached_colors: + line = re.sub( + r"palette\((.+), (\d)\)", + lambda m: palette[m.group(1)][m.group(2)], + line, + ) + + if m := re.match(r"^(.+): (.+),$", line): + theme = m.group(1) + val = m.group(2) + + if reached_colors: + if not current_key in colors: + colors[current_key] = {} + colors[current_key][theme] = val + else: + if not current_key in props: + props[current_key] = {} + props[current_key][theme] = 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, val in colors.items(): + day = val["light"] if "light" in val else val["default"] + night = val["dark"] if len(val) > 1 else day + + 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["light"] if "light" in val else val["default"] + night = val["dark"] if "dark" in val else day + + prop = prop.replace("-", "_").upper() + buf.write(f'{prop} = ("{day}", "{night}")\n') diff --git a/sass/BUILD.bazel b/sass/BUILD.bazel index 2e5ac043c..ff51448d1 100644 --- a/sass/BUILD.bazel +++ b/sass/BUILD.bazel @@ -3,17 +3,26 @@ load("@io_bazel_rules_sass//:defs.bzl", "sass_library") sass_library( name = "base_lib", srcs = [ - "_fusion-vars.scss", - "_vars.scss", "base.scss", "bootstrap-dark.scss", ], visibility = ["//visibility:public"], deps = [ + "vars_lib", "//sass/bootstrap", ], ) +sass_library( + name = "vars_lib", + srcs = [ + "_colors.scss", + "_functions.scss", + "_fusion-vars.scss", + "_vars.scss", + ], +) + sass_library( name = "buttons_lib", srcs = [ @@ -64,6 +73,9 @@ sass_library( "_button-mixins.scss", "_fusion-vars.scss", ], + deps = [ + "vars_lib", + ], visibility = ["//visibility:public"], ) @@ -84,6 +96,6 @@ sass_library( ) exports_files( - ["_vars.scss"], + ["_colors.scss", "_vars.scss"], visibility = ["//visibility:public"], ) diff --git a/sass/_button-mixins.scss b/sass/_button-mixins.scss index 6dc2e186f..df274b75a 100644 --- a/sass/_button-mixins.scss +++ b/sass/_button-mixins.scss @@ -1,5 +1,6 @@ /* Copyright: Ankitects Pty Ltd and contributors * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +@use "vars" as *; @use "fusion-vars"; @use "sass:color"; @@ -22,9 +23,9 @@ $btn-base-color-day: white; @mixin btn-day-base { - color: var(--text-fg); + color: var(--fg-default); background-color: $btn-base-color-day; - border-color: var(--medium-border) !important; + border-color: var(--border-default) !important; } @mixin btn-day($with-hover: true, $with-active: true, $with-disabled: true) { @@ -62,7 +63,7 @@ $btn-base-color-day: white; $btn-base-color-night: fusion-vars.$button-border; @mixin btn-night-base { - color: var(--text-fg); + color: var(--fg-default); background: linear-gradient( 180deg, fusion-vars.$button-gradient-start 0%, @@ -113,8 +114,7 @@ $btn-base-color-night: fusion-vars.$button-border; } } -// should be similar to -webkit-focus-ring-color -$focus-color: rgba(21 97 174); +$focus-color: color(shadow-focus); @function down-arrow($color) { @return url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='transparent' stroke='#{$color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"); diff --git a/sass/_colors.scss b/sass/_colors.scss new file mode 100644 index 000000000..aac18460c --- /dev/null +++ b/sass/_colors.scss @@ -0,0 +1,201 @@ +/* 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 + * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ + +$colors: ( + gray: ( + 0: #f4f4f4, + 1: #efefef, + 2: #e3e3e3, + 3: #cacaca, + 4: #919191, + 5: #3a3a3a, + 6: #2d2d2d, + 7: #2b2b2b, + 8: #232323, + 9: #181818, + ), + 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, + ), +); diff --git a/sass/_functions.scss b/sass/_functions.scss new file mode 100644 index 000000000..df98ad002 --- /dev/null +++ b/sass/_functions.scss @@ -0,0 +1,48 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ + +@use "sass:map"; +@use "sass:list"; + +@function create-vars-from-map($map, $theme, $name: "-", $output: ()) { + @each $key, $value in $map { + @if $key == $theme { + @return map.set($output, $name, map.get($map, $key)); + } + @if type-of($value) == "map" { + $output: map-merge( + $output, + create-vars-from-map($value, $theme, #{$name}-#{$key}, $output) + ); + } + } + @return $output; +} + +@function map-deep-get($map, $keys) { + @each $key in $keys { + $map: map-get($map, $key); + } + @return $map; +} + +@function get-value-from-map($map, $keyword, $theme, $keys: ()) { + $i: str-index($keyword, "-"); + + @if $i { + @while $i { + $sub: str-slice($keyword, 0, $i - 1); + + @if list.length($keys) == 0 { + $keys: ($sub); + } @else { + $keys: list.append($keys, $sub); + } + $keyword: str-slice($keyword, $i + 1, -1); + $i: str-index($keyword, "-"); + } + } + $keys: list.join($keys, ($keyword, $theme)); + + @return map-deep-get($map, $keys); +} diff --git a/sass/_vars.scss b/sass/_vars.scss index d0b1ab343..2b0473e98 100644 --- a/sass/_vars.scss +++ b/sass/_vars.scss @@ -1,113 +1,217 @@ /* 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 "sass:map"; +@use "functions" as *; +@use "colors" as *; + +@function palette($key, $shade) { + $color: map.get($colors, $key); + @return map.get($color, $shade); +} + +$vars: ( + props: ( + border-radius: ( + default: 5px, + ), + ), + colors: ( + canvas: ( + default: ( + light: palette(gray, 0), + dark: palette(gray, 7), + ), + outset: ( + light: white, + dark: palette(gray, 8), + ), + inset: ( + light: palette(gray, 2), + dark: palette(gray, 8), + ), + overlay: ( + light: white, + dark: palette(gray, 9), + ), + ), + border: ( + default: ( + light: palette(gray, 3), + dark: palette(gray, 9), + ), + subtle: ( + light: palette(gray, 2), + dark: palette(gray, 8), + ), + focus: ( + light: palette(indigo, 5), + dark: palette(indigo, 5), + ), + ), + button: ( + focus: ( + default: palette(cyan, 3), + light: palette(cyan, 2), + dark: palette(cyan, 4), + ), + ), + fg: ( + default: ( + light: palette(gray, 9), + dark: palette(gray, 1), + ), + subtle: ( + light: palette(gray, 7), + dark: palette(gray, 3), + ), + disabled: ( + light: palette(gray, 5), + dark: palette(gray, 5), + ), + faint: ( + light: palette(gray, 2), + dark: palette(gray, 8), + ), + ), + shadow: ( + default: ( + light: palette(gray, 5), + dark: palette(gray, 9), + ), + inset: ( + light: palette(gray, 5), + dark: palette(gray, 9), + ), + focus: ( + default: palette(indigo, 5), + ), + ), + accent: ( + card: ( + light: palette(blue, 3), + dark: palette(blue, 4), + ), + note: ( + light: palette(green, 5), + dark: palette(green, 4), + ), + link: ( + light: palette(blue, 7), + dark: palette(blue, 2), + ), + danger: ( + light: palette(red, 5), + dark: palette(red, 4), + ), + ), + flag: ( + 1: ( + light: palette(red, 5), + dark: palette(red, 4), + ), + 2: ( + light: palette(orange, 4), + dark: palette(orange, 3), + ), + 3: ( + light: palette(green, 4), + dark: palette(green, 3), + ), + 4: ( + light: palette(blue, 5), + dark: palette(blue, 4), + ), + 5: ( + light: palette(fuchsia, 4), + dark: palette(fuchsia, 3), + ), + 6: ( + light: palette(teal, 4), + dark: palette(teal, 3), + ), + 7: ( + light: palette(purple, 5), + dark: palette(purple, 4), + ), + ), + state: ( + new: ( + light: palette(blue, 5), + dark: palette(blue, 3), + ), + learn: ( + light: palette(red, 6), + dark: palette(red, 4), + ), + review: ( + light: palette(green, 6), + dark: palette(green, 5), + ), + buried: ( + light: palette(amber, 5), + dark: palette(amber, 8), + ), + suspended: ( + light: palette(yellow, 4), + dark: palette(yellow, 1), + ), + marked: ( + light: palette(indigo, 2), + dark: palette(purple, 5), + ), + ), + highlight: ( + bg: ( + light: palette(cyan, 4), + dark: palette(cyan, 2), + ), + fg: ( + light: black, + dark: white, + ), + ), + ), +); + +@function color($keyword) { + @return var(--#{$keyword}); +} + +@function palette-of($keyword, $theme: default) { + $colors: map.get($vars, colors); + @return get-value-from-map($colors, $keyword, $theme); +} + +@function prop($keyword) { + @return var(--#{$keyword}); +} :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); - --code-bg: white; - --button-primary-gradient-start: #69b3fa; - --button-primary-gradient-end: #087fff; - --button-primary-hover-gradient-start: #69b3fa; - --button-primary-hover-gradient-end: #087fff; - --button-primary-disabled: #8fc5fc; - --button-gradient-start: white; - --button-gradient-end: #f6f6f6; - --button-hover-gradient-start: #fefefe; - --button-hover-gradient-end: #f1f1f1; - --button-pressed-shadow: #555; - --button-border: #666; - --button-pressed-border: #8f8f8f; - --scrollbar-bg: #d8d8d8; - --scrollbar-hover-bg: #d0d0d0; - --scrollbar-active-bg: #c8c8c8; + $colors: map.get($vars, colors); + @each $name, $val in create-vars-from-map($colors, light) { + #{$name}: #{$val}; + } + color-scheme: light; + &.night-mode { + @each $name, $val in create-vars-from-map($colors, dark) { + #{$name}: #{$val}; + } + color-scheme: dark; + } } -:root[class*="night-mode"] { - --text-fg: white; - --window-bg: #2f2f31; - --frame-bg: #3a3a3a; - --border: #1f1f1f; - --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: #1e95df; - --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; - --code-bg: #272822; - --button-primary-gradient-start: #1769e2; - --button-primary-gradient-end: #014996; - --button-primary-hover-gradient-start: #2877c2; - --button-primary-hover-gradient-end: #0d5db3; - --button-primary-disabled: #4977a1; - --button-gradient-start: #3f3f3f; - --button-gradient-end: #363636; - --button-hover-gradient-start: #434343; - --button-hover-gradient-end: #404040; - --button-pressed-shadow: #232323; - --button-border: #2f2f31; - --button-pressed-border: #0a0a0a; - --scrollbar-bg: #434343; - --scrollbar-hover-bg: #4e4e4e; - --scrollbar-active-bg: #464646; - color-scheme: dark; +:root { + $props: map.get($vars, props); + @each $name, $val in create-vars-from-map($props, default) { + #{$name}: #{$val}; + } + @each $name, $val in create-vars-from-map($props, light) { + #{$name}: #{$val}; + } + &.night-mode { + @each $name, $val in create-vars-from-map($props, dark) { + #{$name}: #{$val}; + } + } } diff --git a/sass/base.scss b/sass/base.scss index 9dcd6552e..46fb6cce1 100644 --- a/sass/base.scss +++ b/sass/base.scss @@ -1,10 +1,10 @@ @use "vars"; @use "scrollbar"; -$body-color: var(--text-fg); -$body-bg: var(--window-bg); +$body-color: var(--fg-default); +$body-bg: var(--canvas-default); -$link-hover-color: var(--link); +$link-hover-color: var(--accent-link); $link-hover-decoration: none; $utilities: ( @@ -69,7 +69,7 @@ samp { } .night-mode .form-select:disabled { - background-color: var(--disabled); + background-color: var(--fg-disabled); } .reduced-motion * { diff --git a/sass/bootstrap-dark.scss b/sass/bootstrap-dark.scss index 17de09341..252987f96 100644 --- a/sass/bootstrap-dark.scss +++ b/sass/bootstrap-dark.scss @@ -7,11 +7,11 @@ @mixin night-mode { input, select { - background-color: var(--frame-bg); - border-color: var(--border); + background-color: var(--canvas-outset); + border-color: var(--border-default); &:focus { - background-color: var(--window-bg); + background-color: var(--canvas-default); } } } diff --git a/sass/bootstrap-tooltip.scss b/sass/bootstrap-tooltip.scss index a4d57e81d..5860e7e16 100644 --- a/sass/bootstrap-tooltip.scss +++ b/sass/bootstrap-tooltip.scss @@ -1,6 +1,8 @@ /* Copyright: Ankitects Pty Ltd and contributors * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +@use "vars" as *; + $tooltip-padding-y: 0.45rem; $tooltip-padding-x: 0.65rem; $tooltip-max-width: 300px; @@ -19,7 +21,7 @@ $tooltip-max-width: 300px; // the default code color in tooltips is difficult to read; we'll probably // want to add more of our own styling in the future code { - color: #ffaaaa; + color: palette(red, 0); direction: inherit; } } diff --git a/sass/buttons.scss b/sass/buttons.scss index e79b02310..fe2626108 100644 --- a/sass/buttons.scss +++ b/sass/buttons.scss @@ -1,7 +1,8 @@ +@use "vars" as *; @use "fusion-vars"; :root { - --focus-color: #0078d7; + --focus-color: #{palette-of(button-focus)}; .isMac { --focus-color: rgba(0 103 244 / 0.247); @@ -27,14 +28,14 @@ -webkit-appearance: none; border-radius: 5px; padding: 5px; - border: 1px solid var(--border); + border: 1px solid var(--border-default); } } .nightMode { button { -webkit-appearance: none; - color: var(--text-fg); + color: var(--fg-default); /* match the fusion button gradient */ background: linear-gradient( diff --git a/sass/card-counts.scss b/sass/card-counts.scss index 2ddc5c947..8539313f9 100644 --- a/sass/card-counts.scss +++ b/sass/card-counts.scss @@ -1,21 +1,21 @@ .review-count { - color: var(--review-count); + color: var(--state-review); } .new-count { - color: var(--new-count); + color: var(--state-new); } .learn-count { - color: var(--learn-count); + color: var(--state-learn); } .zero-count { - color: var(--zero-count); + color: var(--fg-faint); } .bury-count { - color: var(--disabled); + color: var(--fg-disabled); font-weight: bold; margin-inline-start: 2px; diff --git a/sass/core.scss b/sass/core.scss index 0fa86894c..c2cec3f0d 100644 --- a/sass/core.scss +++ b/sass/core.scss @@ -8,14 +8,14 @@ } body { - color: var(--text-fg); - background: var(--window-bg); + color: var(--fg-default); + background: var(--canvas-default); margin: 1em; transition: opacity 0.5s ease-out; overscroll-behavior: none; } a { - color: var(--link); + color: var(--accent-link); text-decoration: none; } diff --git a/sass/night-mode.scss b/sass/night-mode.scss index 3a7eff2dd..7d1ab53f8 100644 --- a/sass/night-mode.scss +++ b/sass/night-mode.scss @@ -2,10 +2,10 @@ * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ @mixin input { - background-color: var(--frame-bg); - border-color: var(--border); + background-color: var(--canvas-outset); + border-color: var(--border-default); &:focus { - background-color: var(--window-bg); + background-color: var(--canvas-default); } } diff --git a/sass/scrollbar.scss b/sass/scrollbar.scss index 4b47b91d8..2ca268c3a 100644 --- a/sass/scrollbar.scss +++ b/sass/scrollbar.scss @@ -6,7 +6,7 @@ @mixin custom { ::-webkit-scrollbar { - background-color: var(--window-bg); + background-color: var(--canvas-default); &:horizontal { height: 12px; @@ -39,7 +39,7 @@ } ::-webkit-scrollbar-corner { - background-color: var(--window-bg); + background-color: var(--canvas-default); } ::-webkit-scrollbar-track { diff --git a/ts/card-info/Revlog.svelte b/ts/card-info/Revlog.svelte index 88f2842cf..c71298de2 100644 --- a/ts/card-info/Revlog.svelte +++ b/ts/card-info/Revlog.svelte @@ -177,16 +177,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } .revlog-learn { - color: var(--new-count); + color: var(--state-new); } .revlog-review { - color: var(--review-count); + color: var(--state-review); } .revlog-relearn, .revlog-ease1 { - color: var(--learn-count); + color: var(--state-learn); } @media only screen and (max-device-width: 480px) and (orientation: portrait) { diff --git a/ts/change-notetype/StickyHeader.svelte b/ts/change-notetype/StickyHeader.svelte index 480fc76c5..c84a3a72f 100644 --- a/ts/change-notetype/StickyHeader.svelte +++ b/ts/change-notetype/StickyHeader.svelte @@ -28,7 +28,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html

diff --git a/ts/components/ButtonDropdown.svelte b/ts/components/ButtonDropdown.svelte index 0a3ebe0c0..3288bf862 100644 --- a/ts/components/ButtonDropdown.svelte +++ b/ts/components/ButtonDropdown.svelte @@ -31,8 +31,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html min-width: 0; padding: calc(var(--buttons-size) / 10) 0; - background-color: var(--window-bg); - border-color: var(--medium-border); + background-color: var(--canvas-default); + border-color: var(--border-default); :global(.btn-group) { position: static; diff --git a/ts/components/Collapsible.svelte b/ts/components/Collapsible.svelte index 22396e11d..695c406b6 100644 --- a/ts/components/Collapsible.svelte +++ b/ts/components/Collapsible.svelte @@ -58,15 +58,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html isCollapsed = false; } - inner.addEventListener( - "transitionend", - () => { - inner.toggleAttribute("hidden", collapse); - outer.style.removeProperty("overflow"); - hidden = collapse; - }, - { once: true }, - ); + const update = () => { + inner.toggleAttribute("hidden", collapse); + outer.style.removeProperty("overflow"); + hidden = collapse; + }; + + /* fallback required because transition only triggers if the height changes */ + if (height) { + inner.addEventListener("transitionend", update, { once: true }); + } else { + update(); + } } /* prevent transition on mount for performance reasons */ diff --git a/ts/components/DropdownMenu.svelte b/ts/components/DropdownMenu.svelte index 4720a4fe4..e0764bfd4 100644 --- a/ts/components/DropdownMenu.svelte +++ b/ts/components/DropdownMenu.svelte @@ -32,8 +32,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html diff --git a/ts/components/Popover.svelte b/ts/components/Popover.svelte index aa015eb52..84683b7ae 100644 --- a/ts/components/Popover.svelte +++ b/ts/components/Popover.svelte @@ -15,13 +15,13 @@ Alternative to DropdownMenu that avoids Bootstrap diff --git a/ts/components/StickyContainer.svelte b/ts/components/StickyContainer.svelte index f64507d41..08c8fb95d 100644 --- a/ts/components/StickyContainer.svelte +++ b/ts/components/StickyContainer.svelte @@ -29,9 +29,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html right: 0; z-index: 10; - background: var(--sticky-bg, var(--window-bg)); + background: var(--sticky-bg, var(--canvas-default)); border-style: solid; - border-color: var(--sticky-border, var(--medium-border)); + border-color: var(--sticky-border, var(--border-default)); border-width: var(--sticky-borders, 0); } diff --git a/ts/components/Switch.svelte b/ts/components/Switch.svelte index 7db1e6a63..4c8a76ff3 100644 --- a/ts/components/Switch.svelte +++ b/ts/components/Switch.svelte @@ -41,7 +41,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } .nightMode:not(:checked) { - background-color: var(--frame-bg); - border-color: var(--border); + background-color: var(--canvas-outset); + border-color: var(--border-default); } diff --git a/ts/components/WithFloating.svelte b/ts/components/WithFloating.svelte index 63a88688d..919cc3543 100644 --- a/ts/components/WithFloating.svelte +++ b/ts/components/WithFloating.svelte @@ -95,7 +95,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html .arrow { position: absolute; - background-color: var(--frame-bg); + background-color: var(--canvas-outset); width: 10px; height: 10px; z-index: 60; diff --git a/ts/congrats/CongratsPage.svelte b/ts/congrats/CongratsPage.svelte index 55db748c3..159308ab0 100644 --- a/ts/congrats/CongratsPage.svelte +++ b/ts/congrats/CongratsPage.svelte @@ -70,7 +70,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html font-size: var(--base-font-size); :global(a) { - color: var(--link); + color: var(--accent-link); text-decoration: none; } @@ -80,7 +80,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } .description { - border: 1px solid var(--border); + border: 1px solid var(--border-default); padding: 1em; } diff --git a/ts/deck-options/CardStateCustomizer.svelte b/ts/deck-options/CardStateCustomizer.svelte index 08505b481..920ba33cf 100644 --- a/ts/deck-options/CardStateCustomizer.svelte +++ b/ts/deck-options/CardStateCustomizer.svelte @@ -36,8 +36,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } .card-state-customizer { - color: var(--text-fg); - background-color: var(--frame-bg); + color: var(--fg-default); + background-color: var(--canvas-outset); width: 100%; height: 10em; diff --git a/ts/deck-options/Switch.svelte b/ts/deck-options/Switch.svelte index 7db1e6a63..4c8a76ff3 100644 --- a/ts/deck-options/Switch.svelte +++ b/ts/deck-options/Switch.svelte @@ -41,7 +41,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } .nightMode:not(:checked) { - background-color: var(--frame-bg); - border-color: var(--border); + background-color: var(--canvas-outset); + border-color: var(--border-default); } diff --git a/ts/deck-options/TabbedValue.svelte b/ts/deck-options/TabbedValue.svelte index 3490d3dde..6704f4219 100644 --- a/ts/deck-options/TabbedValue.svelte +++ b/ts/deck-options/TabbedValue.svelte @@ -58,7 +58,7 @@ margin-top: 1rem; margin-bottom: 0.5rem; list-style: none; - border-bottom: 1px solid var(--border); + border-bottom: 1px solid var(--border-default); } span { @@ -69,15 +69,15 @@ padding: 0.25rem 1rem; cursor: pointer; margin: 0 8px -1px 0; - color: var(--disabled); + color: var(--fg-subtle); } li.active > span { - border-color: var(--border) var(--border) var(--window-bg); - color: var(--text-fg); + border-color: var(--border-default) var(--border-default) var(--canvas-default); + color: var(--fg-default); } span:hover { - color: var(--text-fg); + color: var(--fg-default); } diff --git a/ts/deck-options/TextInputModal.svelte b/ts/deck-options/TextInputModal.svelte index 0937f4f86..0a267e264 100644 --- a/ts/deck-options/TextInputModal.svelte +++ b/ts/deck-options/TextInputModal.svelte @@ -100,8 +100,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } .default-colors { - background-color: var(--window-bg); - color: var(--text-fg); + background-color: var(--canvas-default); + color: var(--fg-default); } .invert { diff --git a/ts/deck-options/TitledContainer.svelte b/ts/deck-options/TitledContainer.svelte index 7fb1e4ed0..0bfd2fae0 100644 --- a/ts/deck-options/TitledContainer.svelte +++ b/ts/deck-options/TitledContainer.svelte @@ -16,6 +16,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html diff --git a/ts/editable/editable-base.scss b/ts/editable/editable-base.scss index ea21d81f4..ad3d87eba 100644 --- a/ts/editable/editable-base.scss +++ b/ts/editable/editable-base.scss @@ -20,6 +20,16 @@ p { :host(body) { @include scrollbar.custom; +/* Nudge users towards using brighter colors, + so they are more readable in dark themes */ +span[style*="color"] { + filter: brightness(0.8); +} + +:host(.night-mode) { + span[style*="color"] { + filter: brightness(1.2); + } } pre { diff --git a/ts/editor/EditingArea.svelte b/ts/editor/EditingArea.svelte index da5dfd0f9..eea4b45b1 100644 --- a/ts/editor/EditingArea.svelte +++ b/ts/editor/EditingArea.svelte @@ -188,28 +188,29 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html /* grid-template-columns: repeat(2, 1fr); */ position: relative; - background: var(--frame-bg); + background: var(--canvas-outset); border-radius: 5px; - border: 1px solid var(--border); - box-shadow: 0px 0px 2px 0px var(--border); - transition: box-shadow 80ms cubic-bezier(0.33, 1, 0.68, 1); + /* Pseudo-element required to display + inset focus box-shadow above field contents */ + &::after { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + pointer-events: none; + border-radius: 5px; + border: 1px solid var(--border-default); + transition: box-shadow 80ms cubic-bezier(0.33, 1, 0.68, 1); + box-shadow: inset 0 0 1px 0 var(--shadow-inset); + } &:focus-within { outline: none; - - /* This pseudo-element is required to display - the inset box-shadow above field contents */ &::after { - content: ""; - position: absolute; - top: -1px; - right: -1px; - bottom: -1px; - left: -1px; - pointer-events: none; - border-radius: 5px; - box-shadow: inset 0 0 0 2px var(--focus-border); + box-shadow: inset 0 0 0 2px var(--border-focus); } } } diff --git a/ts/editor/EditorField.svelte b/ts/editor/EditorField.svelte index 9f903c824..805e48dd9 100644 --- a/ts/editor/EditorField.svelte +++ b/ts/editor/EditorField.svelte @@ -117,6 +117,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html diff --git a/ts/editor/NoteEditor.svelte b/ts/editor/NoteEditor.svelte index 7aaa1d01f..5bbc1942e 100644 --- a/ts/editor/NoteEditor.svelte +++ b/ts/editor/NoteEditor.svelte @@ -321,7 +321,7 @@ the AddCards dialog) should be implemented in the user of this component. {#if hint} - {@html alertIcon} {@html hint} @@ -357,8 +357,8 @@ the AddCards dialog) should be implemented in the user of this component. }} collapsed={fieldsCollapsed[index]} --label-color={cols[index] === "dupe" - ? "var(--flag1-bg)" - : "var(--window-bg)"} + ? "var(--flag-1)" + : "var(--canvas-default)"} > .notification { - background-color: var(--notification-bg, var(--window-bg)); + background-color: var(--notification-bg, var(--canvas-default)); user-select: none; - border: 1px solid var(--medium-border); + border: 1px solid var(--border-default); border-radius: 5px; padding: 0.9rem 1.2rem; } diff --git a/ts/editor/legacy.scss b/ts/editor/legacy.scss index 817256259..186e8ff01 100644 --- a/ts/editor/legacy.scss +++ b/ts/editor/legacy.scss @@ -13,10 +13,10 @@ $padding: 2px; width: $size; height: $size; padding: $padding; - color: var(--text-fg); + color: var(--fg-default); font-size: calc($size * 0.6); background-color: $btn-base-color-day; - border: 1px solid var(--medium-border); + border: 1px solid var(--border-default); @include button.btn-border-radius; &:hover { background-color: color.scale($btn-base-color-day, $lightness: -20%); diff --git a/ts/editor/mathjax-overlay/MathjaxEditor.svelte b/ts/editor/mathjax-overlay/MathjaxEditor.svelte index a341c38ca..7fe723fb7 100644 --- a/ts/editor/mathjax-overlay/MathjaxEditor.svelte +++ b/ts/editor/mathjax-overlay/MathjaxEditor.svelte @@ -113,7 +113,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html font-family: sans-serif; font-size: 55%; text-align: center; - color: var(--slightly-grey-text); + color: var(--fg-subtle); } } diff --git a/ts/editor/mathjax-overlay/MathjaxMenu.svelte b/ts/editor/mathjax-overlay/MathjaxMenu.svelte index 2971c45d6..5538d0bda 100644 --- a/ts/editor/mathjax-overlay/MathjaxMenu.svelte +++ b/ts/editor/mathjax-overlay/MathjaxMenu.svelte @@ -74,18 +74,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html diff --git a/ts/editor/plain-text-input/PlainTextInput.svelte b/ts/editor/plain-text-input/PlainTextInput.svelte index 96792d56a..fd1581a1c 100644 --- a/ts/editor/plain-text-input/PlainTextInput.svelte +++ b/ts/editor/plain-text-input/PlainTextInput.svelte @@ -175,7 +175,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html border-radius: 5px; } :global(.CodeMirror) { - background: var(--code-bg); + border-radius: 0 0 5px 5px; + border-top: 1px solid var(--border-default); + background: var(--canvas-inset); } :global(.CodeMirror-lines) { padding: 8px 0; diff --git a/ts/graphs/CardCounts.svelte b/ts/graphs/CardCounts.svelte index bc261ca39..c8db491e3 100644 --- a/ts/graphs/CardCounts.svelte +++ b/ts/graphs/CardCounts.svelte @@ -143,7 +143,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html .search-link:hover { cursor: pointer; - color: var(--link); + color: var(--accent-link); text-decoration: underline; } diff --git a/ts/graphs/NoDataOverlay.svelte b/ts/graphs/NoDataOverlay.svelte index a4dbf029f..cc92a4122 100644 --- a/ts/graphs/NoDataOverlay.svelte +++ b/ts/graphs/NoDataOverlay.svelte @@ -19,7 +19,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html diff --git a/ts/reviewer/index.ts b/ts/reviewer/index.ts index e857df809..ad0b9a560 100644 --- a/ts/reviewer/index.ts +++ b/ts/reviewer/index.ts @@ -210,7 +210,7 @@ export function _showAnswer(a: string, bodyclass: string): void { export function _drawFlag(flag: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7): void { const elem = document.getElementById("_flag")!; elem.toggleAttribute("hidden", flag === 0); - elem.style.color = `var(--flag${flag}-fg)`; + elem.style.color = `var(--flag-${flag})`; } export function _drawMark(mark: boolean): void { diff --git a/ts/reviewer/reviewer.scss b/ts/reviewer/reviewer.scss index 2de00f6a6..afc770917 100644 --- a/ts/reviewer/reviewer.scss +++ b/ts/reviewer/reviewer.scss @@ -1,21 +1,22 @@ /* Copyright: Ankitects Pty Ltd and contributors * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +@use "vars" as *; hr { - background-color: #ccc; + background-color: palette(gray, 4); } body { margin: 20px; overflow-wrap: break-word; - background-color: var(--window-bg); + background-color: var(--canvas-default); } // explicit nightMode definition required // to override default .card styling body.nightMode { - background-color: var(--window-bg); - color: var(--text-fg); + background-color: var(--canvas-default); + color: var(--fg-default); } img { diff --git a/ts/tag-editor/AutocompleteItem.svelte b/ts/tag-editor/AutocompleteItem.svelte index 167b70cb4..a6d24f71f 100644 --- a/ts/tag-editor/AutocompleteItem.svelte +++ b/ts/tag-editor/AutocompleteItem.svelte @@ -37,6 +37,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html