Add comments to Sass variables and tweak main window (#2137)

* Prevent multiple inclusion of variables in CSS files

* Use dict instead of tuple for variables

* Add comments to variables

* Improve appearance of main window

* Tweak main window styles

* Use json.dumps over pprint.format

* Make study button primary

* Improve header margin

* Make bottom toolbar slimmer

* Make congrats page more balanced

* Fix type issue

* Replace day/night with light/dark

* Exclude top-level-drag-row from hover effect

* Create dataclass for variables

* Run formatter

* Apply CSS variables from Python side

Why go full-circle with the Sass variables? This way we only need one interface for add-on authors to interact with. It also makes it easier for us to apply additional themes in the future.

* Fix typing

* Fix rgba values in Qt

* Darken button background

* Fix palette not being applied in light theme

For some odd reason this problem arose much later than #2016.

* Tweak default button look

* Reformat

* Apply CSS vars to ts pages

* Include elevation in button_mixins_lib

* Cast opacity to int

* Add some margin to studiedToday info

* Tweak light theme button gradient

* Tweak highlight-bg for light theme

* Add back default button color

as it made the browser sidebar tool icons dark in light theme.

* Reformat

* Tweak light theme buttons once more

Sorry for the back-and-forth. Sass only compiles when there are changes in user files, not when I only change the vars.

* Fix bottom toolbar button indicators

* Make buttons more clicky

* Fix button padding

* Handle macOS separately again

* Decrease elevation effect for main window buttons to 1

* Imitate box-shadow for Qt elements

* Adjust shadow vars

* Adjust primary border color

because the save button in the deck options had a lighter color than its background gradient.

* Boost box-shadow color of primary buttons

* Format

* Adjust Qt box-shadow imitation and shadow colors

* Use more subtle default shadow color

* Add some more padding to top toolbar

* Revert "Apply CSS vars to ts pages"

This reverts commit 5d8e7f6b7f.

* Revert "Apply CSS variables from Python side"

This reverts commit 87db774412.

* Better match the standard macOS buttons

In the dark theme the standard color is a lighter grey, but at least
the size/shape is similar again.

This doesn't work for the editor buttons.

* Reduce the top margin of the congrats screen

* Fix illegible buttons when changing theme on macOS; match dark button style
This commit is contained in:
Matthias Metelka 2022-10-29 02:48:53 +02:00 committed by GitHub
parent 95d0c78b78
commit 0c340c4f74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 691 additions and 405 deletions

View file

@ -14,7 +14,7 @@ genrule(
genrule(
name = "extract_sass_vars",
srcs = [
"//sass:_vars.css",
"//sass:_root-vars.css",
],
outs = [
"colors.py",

View file

@ -49,7 +49,7 @@ class CellRow:
) -> None:
self.refreshed_at: float = time.time()
self.cells: tuple[Cell, ...] = tuple(Cell(*cell) for cell in cells)
self.color: tuple[str, str] | None = backend_color_to_aqt_color(color)
self.color: dict[str, str] | None = backend_color_to_aqt_color(color)
self.font_name: str = font_name or "arial"
self.font_size: int = font_size if font_size > 0 else 12
@ -76,7 +76,7 @@ class CellRow:
return row
def backend_color_to_aqt_color(color: BrowserRow.Color.V) -> tuple[str, str] | None:
def backend_color_to_aqt_color(color: BrowserRow.Color.V) -> dict[str, str] | None:
temp_color = None
if color == BrowserRow.COLOR_MARKED:
@ -101,12 +101,11 @@ def backend_color_to_aqt_color(color: BrowserRow.Color.V) -> tuple[str, str] | N
return adjusted_bg_color(temp_color)
def adjusted_bg_color(color: tuple[str, str]) -> tuple[str, str]:
def adjusted_bg_color(color: dict[str, str]) -> dict[str, str]:
if color:
return (
QColor(color[0]).lighter(150).name(),
QColor(color[1]).darker(150).name(),
)
color["light"] = QColor(color["light"]).lighter(150).name()
color["dark"] = QColor(color["dark"]).darker(150).name()
return color
else:
return None

View file

@ -37,11 +37,13 @@ with open(input_svg, "r") as f:
elif f"{prefix}-dark.svg" in path:
dark_svg = path
for (idx, filename) in enumerate((light_svg, dark_svg)):
data = svg_data
def substitute(data: str, filename: str, mode: str) -> None:
if "fill" in data:
data = re.sub(r"fill=\"#.+?\"", f'fill="{color[idx]}"', data)
data = re.sub(r"fill=\"#.+?\"", f'fill="{color[mode]}"', data)
else:
data = re.sub(r"<svg", f'<svg fill="{color[idx]}"', data, 1)
data = re.sub(r"<svg", f'<svg fill="{color[mode]}"', data, 1)
with open(filename, "w") as f:
f.write(data)
substitute(svg_data, light_svg, "light")
substitute(svg_data, dark_svg, "dark")

View file

@ -9,7 +9,7 @@ compile_sass(
group = "css_local",
visibility = ["//visibility:private"],
deps = [
"//sass:vars_lib",
"//sass:base_lib",
"//sass:buttons_lib",
"//sass:card_counts_lib",
"//sass:scrollbar_lib",

View file

@ -1,11 +1,11 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass/vars";
@use "root-vars";
@use "sass/vars" as *;
@use "sass/card-counts";
a.deck {
color: var(--fg);
color: color(fg);
text-decoration: none;
min-width: 5em;
display: inline-block;
@ -15,8 +15,13 @@ a.deck:hover {
text-decoration: underline;
}
th {
border-bottom: 1px solid color(border-subtle);
padding-bottom: 5px;
}
tr.deck td {
border-bottom: 1px solid var(--border-faint);
padding: 5px 12px;
}
tr.top-level-drag-row td {
@ -28,16 +33,30 @@ td {
}
tr.drag-hover td {
border-bottom: 1px solid var(--border);
border-bottom: 1px solid color(border);
}
body {
margin: 1em;
margin: 2em 1em 1em 1em;
-webkit-user-select: none;
}
.current {
background-color: var(--shadow-subtle);
.current,
tr:hover:not(.top-level-drag-row) {
td {
background: color(canvas-inset);
&:first-child {
border-top-left-radius: prop(border-radius-large);
border-bottom-left-radius: prop(border-radius-large);
}
&:last-child {
border-top-right-radius: prop(border-radius-large);
border-bottom-right-radius: prop(border-radius-large);
}
.gears {
visibility: visible;
}
}
}
.decktd {
@ -56,14 +75,14 @@ body {
}
.collapse {
color: var(--fg);
color: color(fg);
text-decoration: none;
display: inline-block;
width: 1em;
}
.filtered {
color: var(--accent-link) !important;
color: color(fg-link) !important;
}
.gears {
@ -72,6 +91,7 @@ body {
opacity: 0.5;
padding-top: 0.2em;
cursor: pointer;
visibility: hidden;
}
.nightMode {
@ -81,7 +101,7 @@ body {
}
.callout {
background: var(--border);
background: color(border);
padding: 1em;
margin: 1em;
@ -89,3 +109,7 @@ body {
margin: 1em;
}
}
#studiedToday {
margin: 2em 0;
}

View file

@ -1,7 +1,10 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "root-vars";
@use "sass/vars" as *;
@use "sass/card-counts";
@use "sass/button-mixins" as button;
.smallLink {
font-size: 10px;
@ -13,7 +16,7 @@ h3 {
.descfont {
padding: 1em;
color: var(--fg-subtle);
color: color(fg-subtle);
}
.description {
@ -33,3 +36,7 @@ h3 {
.dyn {
text-align: center;
}
#study {
@include button.base($primary: true);
}

View file

@ -1,11 +1,12 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "vars";
@use "root-vars";
@use "sass/vars" as *;
@use "sass/card-counts";
:root {
--focus-color: #{vars.palette-of(button-focus)};
--focus-color: #{palette-of(border-focus)};
.isMac {
--focus-color: rgba(0 103 244 / 0.247);
@ -17,6 +18,15 @@ body {
padding: 0;
}
#innertable {
padding-top: 10px;
}
#middle td[align="center"] {
padding-top: 10px;
position: relative;
}
button {
min-width: 60px;
white-space: nowrap;
@ -28,7 +38,7 @@ button {
}
.stat {
padding-top: 5px;
padding-top: 10px;
@media (max-width: 583px) {
display: none;
@ -36,13 +46,11 @@ button {
}
.stat2 {
padding-top: 3px;
padding-top: 10px;
font-weight: normal;
}
.stattxt {
padding-left: 5px;
padding-right: 5px;
white-space: nowrap;
}
@ -51,18 +59,17 @@ button {
}
:focus {
outline: 1px auto var(--focus-color);
.nightMode & {
outline: none;
box-shadow: 0 0 0 2px var(--focus-color);
}
border-color: color(border-focus);
}
.nobold {
.nobold,
.stattxt {
position: absolute;
top: -5px;
left: 50%;
transform: translateX(-50%);
font-weight: normal;
display: inline-block;
padding-top: 4px;
}
.spacer {
@ -74,13 +81,13 @@ button {
}
#outer {
border-top: 1px solid var(--border);
border-top: 1px solid color(border);
/* Better compatibility with graphics pad/touchscreen */
-webkit-user-select: none;
}
.nightMode {
#outer {
border-top-color: var(--border-subtle);
border-top-color: color(border-subtle);
}
}

View file

@ -1,14 +1,23 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "root-vars";
@use "sass/vars" as *;
@use "sass/elevation";
@use "sass/button-mixins" as button;
#header {
padding: 3px;
font-weight: bold;
border-bottom: 1px solid var(--border);
padding-bottom: 4px;
}
.tdcenter {
white-space: nowrap;
border-radius: prop(border-radius);
border-bottom-left-radius: prop(border-radius-large);
border-bottom-right-radius: prop(border-radius-large);
@include button.base($with-hover: false);
@include elevation.elevation(2);
overflow: hidden;
}
body {
@ -23,28 +32,25 @@ body {
}
.hitem {
padding-right: 12px;
padding-left: 12px;
font-weight: bold;
padding: 8px 14px;
text-decoration: none;
color: var(--fg);
color: color(fg);
display: inline-block;
@include button.base($elevation: 0);
border: none;
&:first-child {
padding-left: 18px;
}
&:last-child {
padding-right: 18px;
}
.hitem:hover {
text-decoration: underline;
}
.hitem:focus {
outline: 0;
}
.nightMode #header {
border-bottom-color: var(--border-subtle);
}
.isMac.nightMode #header {
border-bottom-color: var(--canvas-elevated);
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
@ -55,25 +61,27 @@ body {
}
.spin {
width: 16px;
animation: spin;
animation-duration: 2s;
animation-iteration-count: infinite;
display: inline-block;
visibility: visible !important;
animation-timing-function: linear;
transition: all 0.2s ease-in;
}
#sync-spinner {
width: 16px;
height: 16px;
margin-bottom: -3px;
visibility: hidden;
width: 0;
}
.normal-sync {
color: var(--state-new);
color: color(state-new);
}
.full-sync {
color: var(--state-learn);
color: color(state-learn);
}

View file

@ -24,7 +24,7 @@ body {
}
a {
color: var(--accent-link);
color: var(--fg-link);
text-decoration: none;
}

View file

@ -84,7 +84,7 @@ 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.var(colors.ACCENT_LINK)
blue = theme_manager.var(colors.FG_LINK)
grey = theme_manager.var(colors.FG_DISABLED)
self.setStyleSheet(
f"""QPushButton[label] {{ padding: 0; border: 0 }}

View file

@ -663,12 +663,11 @@ class Reviewer:
<table id=innertable width=100%% cellspacing=0 cellpadding=0>
<tr>
<td align=left width=50 valign=top class=stat>
<br>
<button title="%(editkey)s" onclick="pycmd('edit');">%(edit)s</button></td>
<td align=center valign=top id=middle>
</td>
<td width=50 align=right valign=top class=stat><span id=time class=stattxt>
</span><br>
</span>
<button onclick="pycmd('more');">%(more)s %(downArrow)s</button>
</td>
</tr>
@ -688,7 +687,7 @@ time = %(time)d;
def _showAnswerButton(self) -> None:
middle = """
<span class=stattxt>{}</span><br>
<span class=stattxt>{}</span>
<button title="{}" id="ansbut" onclick='pycmd("ans");'>{}</button>""".format(
self._remaining(),
tr.actions_shortcut_key(val=tr.studying_space()),
@ -805,7 +804,7 @@ time = %(time)d;
txt = v3_labels[i - 1]
else:
txt = self.mw.col.sched.nextIvlStr(self.card, i, True) or "&nbsp;"
return f"<span class=nobold>{txt}</span><br>"
return f"<span class=nobold>{txt}</span>"
# Leeches
##########################################################################

View file

@ -4,12 +4,13 @@ from aqt import colors, props
from aqt.theme import ThemeManager
def button_gradient(start: str, end: str) -> str:
def button_gradient(start: str, end: str, shadow: str) -> str:
return f"""
qlineargradient(
spread:pad, x1:0.5, y1:0, x2:0.5, y2:1.25,
spread:pad, x1:0.5, y1:0, x2:0.5, y2:1,
stop:0 {start},
stop:1 {end}
stop:0.94 {end}
stop:1 {shadow}
);
"""
@ -62,7 +63,7 @@ QSpinBox {{
def menu_styles(tm: ThemeManager) -> str:
return f"""
QMenuBar {{
border-bottom: 1px solid {tm.var(colors.BORDER_FAINT)};
border-bottom: 1px solid {tm.var(colors.BORDER_SUBTLE)};
}}
QMenuBar::item {{
background-color: transparent;
@ -111,9 +112,11 @@ QComboBox:!editable {{
background: {
button_gradient(
tm.var(colors.BUTTON_GRADIENT_START),
tm.var(colors.BUTTON_GRADIENT_END)
tm.var(colors.BUTTON_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
border-bottom: 1px solid {tm.var(colors.SHADOW)};
}}
QPushButton:hover,
QTabBar::tab:hover,
@ -121,18 +124,19 @@ QComboBox:!editable:hover {{
background: {
button_gradient(
tm.var(colors.BUTTON_HOVER_GRADIENT_START),
tm.var(colors.BUTTON_HOVER_GRADIENT_END)
tm.var(colors.BUTTON_HOVER_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
}}
QPushButton:pressed,
QComboBox:!editable:pressed {{
border: 1px solid {tm.var(colors.BUTTON_PRESSED_BORDER)};
border: 1px solid {tm.var(colors.BORDER_STRONG)};
background: {
button_pressed_gradient(
tm.var(colors.BUTTON_GRADIENT_START),
tm.var(colors.BUTTON_GRADIENT_END),
tm.var(colors.BUTTON_PRESSED_SHADOW)
tm.var(colors.SHADOW)
)
};
}}
@ -190,7 +194,7 @@ QComboBox::drop-down {{
padding: 2px;
width: 16px;
subcontrol-position: top right;
border: 1px solid {tm.var(colors.BUTTON_BORDER)};
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-top-{tm.right()}-radius: {tm.var(props.BORDER_RADIUS)};
border-bottom-{tm.right()}-radius: {tm.var(props.BORDER_RADIUS)};
}}
@ -201,17 +205,21 @@ QComboBox::drop-down {{
background: {
button_gradient(
tm.var(colors.BUTTON_GRADIENT_START),
tm.var(colors.BUTTON_GRADIENT_END)
tm.var(colors.BUTTON_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
border-bottom: 1px solid {tm.var(colors.SHADOW)};
}}
QComboBox::drop-down:hover {{
background: {
button_gradient(
tm.var(colors.BUTTON_HOVER_GRADIENT_START),
tm.var(colors.BUTTON_HOVER_GRADIENT_END)
tm.var(colors.BUTTON_HOVER_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
border-bottom: 1px solid {tm.var(colors.SHADOW)};
}}
"""
@ -239,6 +247,7 @@ QTabBar::tab {{
}}
QTabBar::tab {{
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-bottom-color: {tm.var(colors.SHADOW)};
}}
QTabBar::tab:first {{
border-top-{tm.left()}-radius: {tm.var(props.BORDER_RADIUS)};
@ -256,7 +265,8 @@ QTabBar::tab:selected {{
background: {
button_gradient(
tm.var(colors.BUTTON_PRIMARY_GRADIENT_START),
tm.var(colors.BUTTON_PRIMARY_GRADIENT_END)
tm.var(colors.BUTTON_PRIMARY_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
}}
@ -268,8 +278,8 @@ def table_styles(tm: ThemeManager) -> str:
QTableView {{
border-radius: {tm.var(props.BORDER_RADIUS)};
gridline-color: {tm.var(colors.BORDER_SUBTLE)};
selection-background-color: {tm.var(colors.SELECTION_BG)};
selection-color: {tm.var(colors.SELECTION_FG)};
selection-background-color: {tm.var(colors.SELECTED_BG)};
selection-color: {tm.var(colors.SELECTED_FG)};
}}
QHeaderView {{
background: {tm.var(colors.CANVAS)};
@ -279,18 +289,19 @@ QHeaderView::section {{
background: {
button_gradient(
tm.var(colors.BUTTON_GRADIENT_START),
tm.var(colors.BUTTON_GRADIENT_END)
tm.var(colors.BUTTON_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
}}
QHeaderView::section:pressed,
QHeaderView::section:pressed:!first {{
border: 1px solid {tm.var(colors.BUTTON_PRESSED_BORDER)};
border: 1px solid {tm.var(colors.BORDER_STRONG)};
background: {
button_pressed_gradient(
tm.var(colors.BUTTON_GRADIENT_START),
tm.var(colors.BUTTON_GRADIENT_END),
tm.var(colors.BUTTON_PRESSED_SHADOW)
tm.var(colors.SHADOW)
)
}
}}
@ -298,7 +309,8 @@ QHeaderView::section:hover {{
background: {
button_gradient(
tm.var(colors.BUTTON_HOVER_GRADIENT_START),
tm.var(colors.BUTTON_HOVER_GRADIENT_END)
tm.var(colors.BUTTON_HOVER_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
}}
@ -339,22 +351,23 @@ QSpinBox::up-button,
QSpinBox::down-button {{
subcontrol-origin: border;
width: 16px;
border: 1px solid {tm.var(colors.BUTTON_BORDER)};
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
background: {
button_gradient(
tm.var(colors.BUTTON_GRADIENT_START),
tm.var(colors.BUTTON_GRADIENT_END)
tm.var(colors.BUTTON_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
}}
QSpinBox::up-button:pressed,
QSpinBox::down-button:pressed {{
border: 1px solid {tm.var(colors.BUTTON_PRESSED_BORDER)};
border: 1px solid {tm.var(colors.BORDER_STRONG)};
background: {
button_pressed_gradient(
tm.var(colors.BUTTON_GRADIENT_START),
tm.var(colors.BUTTON_GRADIENT_END),
tm.var(colors.BUTTON_PRESSED_SHADOW)
tm.var(colors.SHADOW)
)
}
}}
@ -363,7 +376,8 @@ QSpinBox::down-button:hover {{
background: {
button_gradient(
tm.var(colors.BUTTON_HOVER_GRADIENT_START),
tm.var(colors.BUTTON_HOVER_GRADIENT_END)
tm.var(colors.BUTTON_HOVER_GRADIENT_END),
tm.var(colors.SHADOW)
)
};
}}
@ -418,7 +432,7 @@ QRadioButton {{
QCheckBox::indicator,
QRadioButton::indicator,
QMenu::indicator {{
border: 1px solid {tm.var(colors.BUTTON_BORDER)};
border: 1px solid {tm.var(colors.BORDER_SUBTLE)};
border-radius: {tm.var(props.BORDER_RADIUS)};
background: {tm.var(colors.CANVAS_INSET)};
width: 16px;

View file

@ -19,8 +19,8 @@ class Switch(QAbstractButton):
radius: int = 10,
left_label: str = "",
right_label: str = "",
left_color: tuple[str, str] = colors.ACCENT_CARD,
right_color: tuple[str, str] = colors.ACCENT_NOTE,
left_color: dict[str, str] = colors.ACCENT_CARD | {},
right_color: dict[str, str] = colors.ACCENT_NOTE | {},
parent: QWidget = None,
) -> None:
super().__init__(parent=parent)

View file

@ -30,16 +30,15 @@ from aqt.qt import (
@dataclass
class ColoredIcon:
path: str
# (day, night)
color: tuple[str, str]
color: dict[str, str]
def current_color(self, night_mode: bool) -> str:
if night_mode:
return self.color[1]
return self.color.get("dark", "")
else:
return self.color[0]
return self.color.get("light", "")
def with_color(self, color: tuple[str, str]) -> ColoredIcon:
def with_color(self, color: dict[str, str]) -> ColoredIcon:
return ColoredIcon(path=self.path, color=color)
@ -177,12 +176,22 @@ class ThemeManager:
"Returns body classes used when showing a card."
return f"card card{card_ord+1} {self.body_class(night_mode)}"
def var(self, vars: tuple[str, str]) -> str:
def var(self, vars: dict[str, str]) -> str:
"""Given day/night colors/props, return the correct one for the current theme."""
idx = 1 if self.night_mode else 0
return vars[idx]
return vars["dark" if self.night_mode else "light"]
def qcolor(self, colors: tuple[str, str]) -> QColor:
def qcolor(self, colors: dict[str, str]) -> QColor:
"""Create QColor instance from CSS string for the current theme."""
if m := re.match(
r"rgba\((\d+),\s*(\d+),\s*(\d+),\s*(\d+\.*\d+?)\)", self.var(colors)
):
return QColor(
int(m.group(1)),
int(m.group(2)),
int(m.group(3)),
int(255 * float(m.group(4))),
)
return QColor(self.var(colors))
def _determine_night_mode(self) -> bool:
@ -257,7 +266,7 @@ class ThemeManager:
def _apply_palette(self, app: QApplication) -> None:
set_macos_dark_mode(self.night_mode)
if not self.night_mode:
if is_mac:
app.setStyle(QStyleFactory.create(self._default_style)) # type: ignore
self.default_palette.setColor(
QPalette.ColorRole.Window, self.qcolor(colors.CANVAS)
@ -265,11 +274,9 @@ class ThemeManager:
app.setPalette(self.default_palette)
return
if not self.macos_dark_mode():
app.setStyle(QStyleFactory.create("fusion")) # type: ignore
palette = QPalette()
text = self.qcolor(colors.FG)
palette.setColor(QPalette.ColorRole.WindowText, text)
palette.setColor(QPalette.ColorRole.ToolTipText, text)
@ -277,7 +284,6 @@ class ThemeManager:
palette.setColor(QPalette.ColorRole.ButtonText, text)
hlbg = self.qcolor(colors.HIGHLIGHT_BG)
hlbg.setAlpha(64)
palette.setColor(
QPalette.ColorRole.HighlightedText, self.qcolor(colors.HIGHLIGHT_FG)
)
@ -287,11 +293,13 @@ class ThemeManager:
palette.setColor(QPalette.ColorRole.Window, canvas)
palette.setColor(QPalette.ColorRole.AlternateBase, canvas)
palette.setColor(QPalette.ColorRole.Button, QColor("#454545"))
palette.setColor(
QPalette.ColorRole.Button, self.qcolor(colors.BUTTON_GRADIENT_START)
)
canvas_inset = self.qcolor(colors.CANVAS_INSET)
palette.setColor(QPalette.ColorRole.Base, canvas_inset)
palette.setColor(QPalette.ColorRole.ToolTipBase, canvas_inset)
input_base = self.qcolor(colors.CANVAS_CODE)
palette.setColor(QPalette.ColorRole.Base, input_base)
palette.setColor(QPalette.ColorRole.ToolTipBase, input_base)
palette.setColor(
QPalette.ColorRole.PlaceholderText, self.qcolor(colors.FG_SUBTLE)
@ -310,7 +318,7 @@ class ThemeManager:
disabled_color,
)
palette.setColor(QPalette.ColorRole.Link, self.qcolor(colors.ACCENT_LINK))
palette.setColor(QPalette.ColorRole.Link, self.qcolor(colors.FG_LINK))
palette.setColor(QPalette.ColorRole.BrightText, Qt.GlobalColor.red)

View file

@ -196,7 +196,7 @@ class Toolbar:
_body = """
<center id=outer>
<table id=header width=100%%>
<table id=header>
<tr>
<td class=tdcenter align=center>%s</td>
</tr></table>

View file

@ -412,30 +412,32 @@ class AnkiWebView(QWebEngineView):
family = tr.qt_misc_segoe_ui()
button_style = f"""
button {{ font-family: {family}; }}
button:focus {{ outline: 5px auto {color_hl}; }}"""
"""
font = f"font-size:12px;font-family:{family};"
elif is_mac:
family = "Helvetica"
font = f'font-size:15px;font-family:"{family}";'
color = ""
if not theme_manager.night_mode:
color = "background: #fff; border: 1px solid #ccc;"
button_style = (
font = f'font-size:14px;font-family:"{family}";'
button_style = """
button {
--canvas: #fff;
-webkit-appearance: none;
background: var(--canvas);
border-radius: var(--border-radius);
padding: 3px 12px;
border: 0.5px solid var(--border);
box-shadow: 0px 1px 3px var(--border-subtle);
font-family: Helvetica
}
.night-mode button { --canvas: #606060; --fg: #eee; }
"""
button { -webkit-appearance: none; %s
border-radius:5px; font-family: Helvetica }"""
% color
)
else:
family = self.font().family()
color_hl_txt = palette.color(QPalette.ColorRole.HighlightedText).name()
font = f'font-size:14px;font-family:"{family}", sans-serif;'
button_style = """
/* Buttons */
button{{
font-family:"{family}", sans-serif; }}
button:focus{{ border-color: {color_hl} }}
button:active, button:active:hover {{ background-color: {color_hl}; color: {color_hl_txt};}}
font-family: "{family}", sans-serif;
}}
/* Input field focus outline */
textarea:focus, input:focus, input[type]:focus, .uneditable-input:focus,
div[contenteditable="true"]:focus {{
@ -444,7 +446,6 @@ div[contenteditable="true"]:focus {{
}}""".format(
family=family,
color_hl=color_hl,
color_hl_txt=color_hl_txt,
)
zoom = self.app_zoom_factor()
@ -453,8 +454,8 @@ div[contenteditable="true"]:focus {{
body {{ zoom: {zoom}; background-color: var(--canvas); }}
html {{ {font} }}
{button_style}
:root {{ --canvas: {colors.CANVAS[0]} }}
:root[class*=night-mode] {{ --canvas: {colors.CANVAS[1]} }}
:root {{ --canvas: {colors.CANVAS["light"]} }}
:root[class*=night-mode] {{ --canvas: {colors.CANVAS["dark"]} }}
"""
def stdHtml(

View file

@ -1,13 +1,12 @@
#!/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"
vars_css = sys.argv[1]
root_vars_css = sys.argv[1]
# bazel genrule "outs"
colors_py = sys.argv[2]
@ -16,14 +15,19 @@ props_py = sys.argv[3]
colors = {}
props = {}
reached_props = False
comment = ""
for line in re.split(r"[;\{\}]", open(vars_css).read()):
for line in re.split(r"[;\{\}]|\*\/", open(root_vars_css).read()):
line = line.strip()
if not line:
continue
if line.startswith("/*!"):
if "props" in line:
reached_props = True
else:
comment = re.match(r"\/\*!\s*(.*)$", line)[1]
continue
m = re.match(r"--(.+):(.+)$", line)
@ -38,39 +42,47 @@ for line in re.split(r"[;\{\}]", open(vars_css).read()):
print("failed to match", line)
continue
var = m.group(1)
var = m.group(1).replace("-", "_").upper()
val = m.group(2)
if reached_props:
props.setdefault(var, []).append(val)
if not var in props:
props.setdefault(var, {})["comment"] = comment
props[var]["light"] = val
else:
colors.setdefault(var, []).append(val)
props[var]["dark"] = val
else:
if not var in colors:
colors.setdefault(var, {})["comment"] = comment
colors[var]["light"] = val
else:
colors[var]["dark"] = val
comment = ""
copyright_notice = """\
# 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\n
"""
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")
buf.write("# This file was automatically generated from _root-vars.scss\n")
for color, val in colors.items():
day = val[0]
night = val[1] if len(val) > 1 else day
if not "dark" in val:
val["dark"] = val.light
color = color.replace("-", "_").upper()
buf.write(f'{color} = ("{day}", "{night}")\n')
buf.write(re.sub(r"\"\n", '",\n', f"{color} = {json.dumps(val, indent=4)}\n"))
with open(props_py, "w") as buf:
buf.write(copyright_notice)
buf.write("# this file is auto-generated from _vars.scss\n")
buf.write("# This file was automatically generated from _root-vars.scss\n")
for prop, val in props.items():
day = val[0]
night = val[1] if len(val) > 1 else day
if not "dark" in val:
val["dark"] = val.light
prop = prop.replace("-", "_").upper()
buf.write(f'{prop} = ("{day}", "{night}")\n')
buf.write(re.sub(r"\"\n", '",\n', f"{prop} = {json.dumps(val, indent=4)}\n"))

View file

@ -17,7 +17,7 @@ sass_library(
sass_library(
name = "vars_lib",
srcs = [
"_colors.scss",
"_color-palette.scss",
"_functions.scss",
"_vars.scss",
],
@ -104,7 +104,7 @@ sass_library(
)
compile_sass(
srcs = ["_vars.scss"],
srcs = ["_root-vars.scss"],
group = "vars_css",
visibility = ["//visibility:public"],
)

View file

@ -2,13 +2,14 @@
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "vars";
@use "sass:color";
@use "sass/elevation" as *;
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@mixin impressed-shadow($intensity) {
box-shadow: inset 0 calc(var(--buttons-size) / 15) calc(var(--buttons-size) / 5)
rgba(black, $intensity);
box-shadow: inset 0 calc(var(--buttons-size, 10px) / 15)
calc(var(--buttons-size, 10px) / 5) rgba(black, $intensity);
}
@mixin border-radius {
@ -19,13 +20,14 @@
border-bottom-right-radius: var(--border-right-radius);
}
@mixin background($primary: false) {
@mixin background($primary: false, $elevation: 1, $hover: true) {
@if $primary {
background: linear-gradient(
180deg,
var(--button-primary-gradient-start) 0%,
var(--button-primary-gradient-end) 100%
);
@if $hover {
&:hover {
background: linear-gradient(
180deg,
@ -34,12 +36,17 @@
);
border-color: var(--button-hover-border);
}
}
@if $elevation != 0 {
@include elevation($elevation, 0.2);
}
} @else {
background: linear-gradient(
180deg,
var(--button-gradient-start) 0%,
var(--button-gradient-end) 100%
);
@if $hover {
&:hover {
background: linear-gradient(
180deg,
@ -49,6 +56,10 @@
border-color: var(--button-hover-border);
}
}
@if $elevation != 0 {
@include elevation($elevation);
}
}
}
@mixin base(
@ -57,16 +68,21 @@
$with-hover: true,
$with-active: true,
$active-class: "",
$with-disabled: true
$with-disabled: true,
$elevation: 1
) {
-webkit-appearance: none;
cursor: pointer;
@if $border {
border: 1px solid var(--button-border);
@if $primary {
border: none;
} @else {
border: 1px solid var(--border-subtle);
}
} @else {
border: none;
}
@include background($primary);
@include background($primary, $elevation, $hover: $with-hover);
@if ($primary) {
color: white;
@ -77,12 +93,12 @@
@if ($with-active) {
&:active {
@include impressed-shadow(0.35);
border-color: var(--button-border);
border-color: var(--border-subtle);
}
@if ($active-class != "") {
&.#{$active-class} {
@include impressed-shadow(0.35);
border-color: var(--button-border);
border-color: var(--border);
}
}
}

View file

@ -5,7 +5,7 @@
* custom gray, rest from Tailwind CSS v3 palette
* */
$colors: (
$color-palette: (
lightgray: (
0: #fcfcfc,
1: #fafafa,
@ -26,7 +26,7 @@ $colors: (
4: #363636,
5: #2c2c2c,
6: #252525,
7: #1f1f1f,
7: #202020,
8: #141414,
9: #020202,
),

View file

@ -6,7 +6,17 @@
@function create-vars-from-map($map, $theme, $name: "-", $output: ()) {
@each $key, $value in $map {
@if $key == $theme {
@if $key ==
$theme or
(
$key ==
"default" and
type-of($value) !=
"map" and
type-of($value) !=
"list"
)
{
@return map.set($output, $name, map.get($map, $key));
}
@if type-of($value) == "map" {
@ -21,8 +31,24 @@
create-vars-from-map($value, $theme, #{$name}-#{$key}, $output)
);
}
} @else if $key == "default" {
@return map.set($output, $name, map.get($map, $key));
} @else if type-of($value) == "list" and list.length($value) > 1 {
$next-name: #{$name}-#{$key};
@if $key == "default" {
$next-name: $name;
}
$output: map-merge(
$output,
(#{"comment"}#{$next-name}: list.nth($value, 1))
);
$output: map-merge(
$output,
create-vars-from-map(
list.nth($value, 2),
$theme,
#{$next-name},
$output
)
);
}
}
@return $output;
@ -30,8 +56,12 @@
@function map-deep-get($map, $keys) {
@each $key in $keys {
@if type-of($map) == "list" and list.length($map) > 1 {
$map: map-get(list.nth($map, 2), $key);
} @else {
$map: map-get($map, $key);
}
}
@return $map;
}

50
sass/_root-vars.scss Normal file
View file

@ -0,0 +1,50 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later, http://www.gnu.org/licenses/agpl.html */
@use "sass:map";
@use "vars" as *;
@use "functions" as *;
/*! colors */
:root {
$colors: map.get($vars, colors);
@each $name, $val in create-vars-from-map($colors, light) {
@if str-index($name, "comment") == 1 {
/*! #{$val} */
} @else {
#{$name}: #{$val};
}
}
color-scheme: light;
&.night-mode {
@each $name, $val in create-vars-from-map($colors, dark) {
@if str-index($name, "comment") == 1 {
/*! #{$val} */
} @else {
#{$name}: #{$val};
}
}
color-scheme: dark;
}
}
/*! props */
:root {
$props: map.get($vars, props);
@each $name, $val in create-vars-from-map($props, light) {
@if str-index($name, "comment") == 1 {
/*! #{$val} */
} @else {
#{$name}: #{$val};
}
}
&.night-mode {
@each $name, $val in create-vars-from-map($props, dark) {
@if str-index($name, "comment") == 1 {
/*! #{$val} */
} @else {
#{$name}: #{$val};
}
}
}
}

View file

@ -4,296 +4,439 @@
@use "sass:map";
@use "sass:color";
@use "functions" as *;
@use "colors" as *;
@use "color-palette" as *;
@function palette($key, $shade) {
$color: map.get($colors, $key);
$color: map.get($color-palette, $key);
@return map.get($color, $shade);
}
$vars: (
props: (
border-radius: (
default: (
"Used to round corners of various UI elements",
(
default: 5px,
),
),
large: (
"Used for big centered buttons",
(
default: 15px,
),
),
),
),
colors: (
white: (
default: white,
),
black: (
default: black,
),
fg: (
default: (
"Default text/icon color",
(
light: palette(darkgray, 9),
dark: palette(lightgray, 0),
),
),
subtle: (
"Placeholder text, icons in idle state",
(
light: palette(darkgray, 6),
dark: palette(lightgray, 3),
),
),
disabled: (
"Foreground color of disabled UI elements",
(
light: palette(lightgray, 9),
dark: palette(darkgray, 0),
),
),
faint: (
"Foreground color that barely stands out against canvas",
(
light: palette(lightgray, 7),
dark: palette(darkgray, 2),
),
),
link: (
"Hyperlink foreground color",
(
light: palette(blue, 7),
dark: palette(blue, 2),
),
),
),
canvas: (
default: (
light: palette(lightgray, 3),
"Window background",
(
light: palette(lightgray, 2),
dark: palette(darkgray, 5),
),
),
elevated: (
"Slightly brighter than window background",
(
light: white,
dark: palette(darkgray, 4),
),
),
inset: (
light: palette(lightgray, 4),
"Slightly darker than window background",
(
light: palette(lightgray, 3),
dark: palette(darkgray, 6),
),
),
overlay: (
"Background of floating elements (menus, tooltips)",
(
light: palette(lightgray, 0),
dark: palette(darkgray, 5),
),
),
code: (
"Background of code editors",
(
light: white,
dark: palette(darkgray, 6),
),
),
),
border: (
default: (
"Border color with medium contrast against window background",
(
light: palette(lightgray, 6),
dark: palette(darkgray, 4),
),
strong: (
light: palette(lightgray, 9),
dark: palette(darkgray, 1),
),
subtle: (
light: palette(lightgray, 5),
dark: palette(darkgray, 7),
),
faint: (
),
subtle: (
"Border color with low contrast against window background",
(
light: palette(lightgray, 4),
dark: palette(darkgray, 6),
),
focus: (
light: palette(blue, 5),
dark: palette(blue, 5),
),
),
button: (
border: (
light: palette(lightgray, 6),
dark: palette(darkgray, 6),
),
focus: (
light: palette(cyan, 3),
dark: palette(cyan, 4),
),
pressed: (
shadow: (
light: palette(lightgray, 7),
dark: palette(darkgray, 7),
),
border: (
strong: (
"Border color with high contrast against window background",
(
light: palette(lightgray, 9),
dark: palette(darkgray, 9),
),
),
focus: (
"Border color of focused input elements",
(
light: palette(blue, 5),
dark: palette(blue, 5),
),
),
),
button: (
disabled: (
"Background color of disabled buttons",
(
light: color.scale(palette(lightgray, 5), $alpha: -50%),
dark: color.scale(palette(darkgray, 3), $alpha: -50%),
),
),
gradient: (
start: (
"Start value of default button gradient",
(
light: white,
dark: palette(darkgray, 3),
dark: palette(darkgray, 4),
),
),
end: (
light: palette(lightgray, 1),
dark: palette(darkgray, 4),
"End value of default button gradient",
(
light: palette(lightgray, 3),
dark: palette(darkgray, 5),
),
),
),
hover: (
gradient: (
start: (
"Start value of default button gradient in hover state",
(
light: palette(lightgray, 1),
dark: palette(darkgray, 2),
),
end: (
light: palette(lightgray, 4),
dark: palette(darkgray, 3),
),
),
end: (
"End value of default button gradient in hover state",
(
light: palette(lightgray, 2),
dark: palette(darkgray, 4),
),
),
),
border: (
"Border color of default button in hover state",
(
light: palette(lightgray, 8),
dark: palette(darkgray, 8),
),
),
),
primary: (
gradient: (
start: (
"Start value of primary button gradient",
(
light: palette(blue, 4),
dark: color.scale(palette(blue, 6), $saturation: -10%),
),
),
end: (
"End value of primary button gradient",
(
light: palette(blue, 6),
dark: color.scale(palette(blue, 8), $saturation: -10%),
),
),
),
hover: (
gradient: (
start: (
"Start value of primary button gradient in hover state",
(
light: palette(blue, 3),
dark: color.scale(palette(blue, 5), $saturation: -10%),
),
),
end: (
"End value of primary button gradient in hover state",
(
light: palette(blue, 5),
dark: color.scale(palette(blue, 7), $saturation: -10%),
),
),
),
),
disabled: (
"Background of primary button in disabled state",
(
light: palette(blue, 3),
dark: color.scale(palette(blue, 5), $saturation: -10%),
),
),
),
),
scrollbar: (
bg: (
default: (
"Background of scrollbar in idle state (Win/Lin only)",
(
light: palette(lightgray, 5),
dark: palette(darkgray, 4),
),
),
hover: (
"Background of scrollbar in hover state (Win/Lin only)",
(
light: palette(lightgray, 6),
dark: palette(darkgray, 3),
),
),
active: (
"Background of scrollbar in pressed state (Win/Lin only)",
(
light: palette(lightgray, 7),
dark: palette(darkgray, 1),
),
),
),
),
shadow: (
default: (
light: palette(lightgray, 8),
"Default box-shadow color",
(
light: palette(lightgray, 6),
dark: palette(darkgray, 7),
),
),
inset: (
light: palette(lightgray, 9),
"Inset box-shadow color",
(
light: palette(darkgray, 3),
dark: palette(darkgray, 7),
),
),
subtle: (
light: palette(lightgray, 5),
dark: palette(darkgray, 6),
"Box-shadow color with lower contrast against window background",
(
light: palette(darkgray, 0),
dark: palette(darkgray, 4),
),
),
focus: (
"Box-shadow color for elements in focused state",
(
default: palette(indigo, 5),
),
),
),
accent: (
card: (
"Accent color for cards",
(
light: palette(blue, 4),
dark: palette(blue, 3),
),
),
note: (
"Accent color for notes",
(
light: palette(green, 5),
dark: palette(green, 4),
),
link: (
light: palette(blue, 7),
dark: palette(blue, 2),
),
danger: (
"Saturated accent color to grab attention",
(
light: palette(red, 5),
dark: palette(red, 4),
),
),
),
flag: (
1: (
"Flag 1 (red)",
(
light: palette(red, 5),
dark: palette(red, 4),
),
),
2: (
"Flag 2 (orange)",
(
light: palette(orange, 4),
dark: palette(orange, 3),
),
),
3: (
"Flag 3 (green)",
(
light: palette(green, 4),
dark: palette(green, 3),
),
),
4: (
"Flag 4 (blue)",
(
light: palette(blue, 5),
dark: palette(blue, 4),
),
),
5: (
"Flag 5 (pink)",
(
light: palette(fuchsia, 4),
dark: palette(fuchsia, 3),
),
),
6: (
"Flag 6 (turquoise)",
(
light: palette(teal, 4),
dark: palette(teal, 3),
),
),
7: (
"Flag 7 (purple)",
(
light: palette(purple, 5),
dark: palette(purple, 4),
),
),
),
state: (
new: (
"Accent color for new cards",
(
light: palette(blue, 5),
dark: palette(blue, 3),
),
),
learn: (
"Accent color for cards in learning state",
(
light: palette(red, 6),
dark: palette(red, 4),
),
),
review: (
"Accent color for cards in review state",
(
light: palette(green, 6),
dark: palette(green, 5),
),
),
buried: (
"Accent color for buried cards",
(
light: palette(amber, 5),
dark: palette(amber, 8),
),
),
suspended: (
"Accent color for suspended cards",
(
light: palette(yellow, 4),
dark: palette(yellow, 1),
),
),
marked: (
"Accent color for marked cards",
(
light: palette(indigo, 2),
dark: palette(purple, 5),
),
),
),
highlight: (
bg: (
light: color.scale(palette(blue, 3), $alpha: -33%),
dark: color.scale(palette(blue, 4), $alpha: -33%),
"Background color of highlighted items",
(
light: color.scale(palette(blue, 6), $alpha: -50%),
dark: color.scale(palette(blue, 3), $alpha: -50%),
),
),
fg: (
"Foreground color of highlighted items",
(
light: black,
dark: white,
),
),
selection: (
),
selected: (
bg: (
"Background color of selected text",
(
light: color.scale(palette(lightgray, 5), $alpha: -50%),
dark: color.scale(palette(blue, 3), $alpha: -50%),
),
),
fg: (
"Foreground color of selected text",
(
light: black,
dark: white,
),
),
),
),
);
@function prop($keyword) {
@return var(--#{$keyword});
}
@function color($keyword) {
@return var(--#{$keyword});
}
@ -302,41 +445,3 @@ $vars: (
$colors: map.get($vars, colors);
@return get-value-from-map($colors, $keyword, $theme);
}
@function prop($keyword) {
@return var(--#{$keyword});
}
/*! colors */
:root {
$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;
}
}
/*! props */
: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, default) {
#{$name}: #{$val};
}
@each $name, $val in create-vars-from-map($props, dark) {
#{$name}: #{$val};
}
}
}

View file

@ -1,11 +1,12 @@
@use "vars";
@use "vars" as *;
@use "root-vars";
@use "scrollbar";
@use "button-mixins" as button;
$body-color: var(--fg);
$body-bg: var(--canvas);
$body-color: color(fg);
$body-bg: color(canvas);
$link-hover-color: var(--accent-link);
$link-hover-color: color(fg-link);
$link-hover-decoration: none;
$utilities: (
@ -53,6 +54,7 @@ body {
button {
/* override transition for instant hover response */
transition: color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;
border-radius: prop(border-radius);
}
pre,
@ -82,12 +84,12 @@ samp {
}
.form-select:focus {
outline: none;
border: 1px solid var(--border-focus);
border: 1px solid color(border-focus);
box-shadow: none !important;
}
.night-mode .form-select:disabled {
background-color: var(--fg-disabled);
background-color: color(fg-disabled);
}
.reduced-motion * {
@ -104,10 +106,10 @@ select {
&:focus,
&.focus {
border: 1px solid var(--border-focus);
border: 1px solid color(border-focus);
}
option {
background: var(--canvas-elevated);
color: var(--fg);
background: color(canvas-elevated);
color: color(fg);
}
}

View file

@ -1,5 +1,6 @@
@use "vars";
@use "button-mixins" as button;
@use "elevation" as *;
:root {
--focus-color: #{vars.palette-of(shadow-focus)};
@ -22,7 +23,11 @@
}
button {
outline: none !important;
@include button.base;
border-radius: var(--border-radius);
padding: 5px 10px;
@include elevation(1);
border-radius: var(--border-radius-large);
padding: 8px 10px;
font-weight: 500;
margin: 0 4px;
}

View file

@ -16,6 +16,6 @@ body {
}
a {
color: var(--accent-link);
color: var(--fg-link);
text-decoration: none;
}

View file

@ -1,4 +1,5 @@
// Heavily inspired by https://github.com/material-components/material-components-web/tree/master/packages/mdc-elevation
@use "sass:color";
@use "sass:map";
@use "sass:list";
@ -64,21 +65,23 @@ $ambient-opacity: 0.12;
$penumbra-z-value: map.get($penumbra-map, $level);
$ambient-z-value: map.get($ambient-map, $level);
$umbra-color: rgba($color, $umbra-opacity + $opacity-boost);
$penumbra-color: rgba($color, $penumbra-opacity + $opacity-boost);
$ambient-color: rgba($color, $ambient-opacity + $opacity-boost);
$umbra-color: color.adjust(rgba($color, $umbra-opacity), $alpha: $opacity-boost);
$penumbra-color: color.adjust(
rgba($color, $penumbra-opacity),
$alpha: $opacity-boost
);
$ambient-color: color.adjust(
rgba($color, $ambient-opacity),
$alpha: $opacity-boost
);
@return (
#{"#{$umbra-z-value} #{$umbra-color}"},
#{"#{$penumbra-z-value} #{$penumbra-color}"},
#{$umbra-z-value} $umbra-color,
#{$penumbra-z-value} $penumbra-color,
#{$ambient-z-value} $ambient-color
);
}
@mixin elevation($level, $opacity-boost: 0, $color: black) {
@mixin elevation($level, $opacity-boost: 0, $color: #141414) {
box-shadow: box-shadow($level, $opacity-boost, $color);
}
@mixin elevation-transition() {
transition: box-shadow 80ms cubic-bezier(0.33, 1, 0.68, 1);
}

View file

@ -1,4 +1,3 @@
@use "sass/vars";
@use "sass/bootstrap-dark";
@import "sass/base";

View file

@ -20,10 +20,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</div>
<style lang="scss">
@use "sass/vars";
@use "sass/vars" as *;
.popover {
border-radius: 5px;
background-color: var(--canvas-elevated);
background-color: color(canvas-elevated);
min-width: var(--popover-width, 1rem);
max-width: 95vw;
@ -31,20 +31,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
padding: var(--popover-padding-block, 6px) var(--popover-padding-inline, 6px);
font-size: 1rem;
color: var(--fg);
color: color(fg);
/* outer border */
border: 1px solid vars.palette(lightgray, 6);
border: 1px solid palette(lightgray, 6);
&.dark {
border-color: vars.palette(darkgray, 9);
border-color: palette(darkgray, 9);
}
/* inner border */
box-shadow: inset 0 0 0 1px vars.palette(lightgray, 3);
box-shadow: inset 0 0 0 1px palette(lightgray, 3);
&.dark {
box-shadow: inset 0 0 0 1px vars.palette(darkgray, 2);
box-shadow: inset 0 0 0 1px palette(darkgray, 2);
}
&.scrollable {
max-height: 80vh;

View file

@ -75,13 +75,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
.chevron {
position: absolute;
inset: 0 0 0 auto;
border-left: 1px solid var(--button-border);
border-left: 1px solid var(--border-subtle);
}
:global([dir="rtl"]) {
.chevron {
inset: 0 auto 0 0;
border-left: none;
border-right: 1px solid var(--button-border);
border-right: 1px solid var(--border-subtle);
}
}
</style>

View file

@ -102,6 +102,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
input {
width: 100%;
padding: 0.2rem 1.5rem 0.2rem 0.75rem;
background: var(--canvas-elevated);
color: var(--fg);
border: none;
outline: none;
text-align: center;
@ -120,7 +122,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
button {
opacity: 0;
position: absolute;
@include button.base($border: false);
@include button.base($border: false, $elevation: 0);
&.left {
inset: 0 auto 0 0;

View file

@ -29,7 +29,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<Container --gutter-block="1rem" --gutter-inline="2px" breakpoint="sm">
<Col --col-justify="center">
<div class="congrats">
<h3>{congrats}</h3>
<h1>{congrats}</h1>
<p>{nextLearnMsg}</p>
@ -66,17 +66,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<style lang="scss">
.congrats {
margin-top: 2em;
max-width: 30em;
font-size: var(--base-font-size);
:global(a) {
color: var(--accent-link);
color: var(--fg-link);
text-decoration: none;
}
h3 {
font-weight: bold;
}
}
.description {

View file

@ -1,3 +1,4 @@
@use "sass/root-vars";
@import "sass/base";
@import "sass/bootstrap/scss/containers";

View file

@ -118,7 +118,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
border-radius: 5px;
border: 1px solid var(--border);
@include elevation-transition;
@include elevation(1);
&:focus-within {

View file

@ -1,6 +1,5 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass/vars";
@use "sass:color";
@use "sass/button-mixins" as button;

View file

@ -1,4 +1,3 @@
@use "sass/vars";
@use "sass/bootstrap-dark";
@import "sass/base";

View file

@ -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(--accent-link);
color: var(--fg-link);
text-decoration: underline;
}
</style>

View file

@ -1,4 +1,4 @@
@use "sass/base";
@use "root-vars";
@use "sass/button-mixins" as button;
label,

View file

@ -1,4 +1,3 @@
@use "sass/vars";
@use "sass/bootstrap-dark";
@import "sass/base";

View file

@ -33,7 +33,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</div>
<style lang="scss">
@use "sass/vars";
@use "sass/button-mixins" as button;
.autocomplete-item {