diff --git a/backend/wordmodels/neighbor_network.py b/backend/wordmodels/neighbor_network.py
index 7ccbfae19..1f58d4cb3 100644
--- a/backend/wordmodels/neighbor_network.py
+++ b/backend/wordmodels/neighbor_network.py
@@ -95,7 +95,6 @@ def _graph_vega_doc(timeframes, nodes, links):
"height": 500,
"padding": 0,
"autosize": "none",
-
"signals": [
{ "name": "cx", "update": "width / 2" },
{ "name": "cy", "update": "height / 2" },
@@ -154,7 +153,14 @@ def _graph_vega_doc(timeframes, nodes, links):
"on": [
{"events": {"signal": "fix"}, "update": "fix && fix.length"}
]
- }
+ },
+ {
+ 'name': 'theme',
+ 'description': 'Current site theme (light/dark)',
+ 'bind': {
+ 'element': '#current-theme',
+ }
+ },
],
"data": [
@@ -181,13 +187,13 @@ def _graph_vega_doc(timeframes, nodes, links):
]
}
],
-
"scales": [
{
'name': 'link-color',
'type': 'linear',
'domain': {"data": "link-data", "field": "value"},
'range': {'scheme': 'greys'},
+ 'reverse': { 'signal': 'theme === "dark"' },
},
{
'name': 'text-weight',
@@ -196,13 +202,11 @@ def _graph_vega_doc(timeframes, nodes, links):
'range': ['bold', 'normal'],
}
],
-
"marks": [
{
"name": "nodes",
"type": "text",
"zindex": 1,
-
"from": {"data": "node-data"},
"on": [
{
@@ -218,7 +222,6 @@ def _graph_vega_doc(timeframes, nodes, links):
"encode": {
"enter": {
"fontSize": {"value": 15},
- "fill": {"value": "black"},
"text": {"field": "term"},
"baseline": {"value": "middle"},
"align": {"value": "center"},
@@ -227,7 +230,8 @@ def _graph_vega_doc(timeframes, nodes, links):
},
},
"update": {
- "cursor": {"value": "pointer"}
+ "cursor": {"value": "pointer"},
+ "fill": {'signal': 'theme === "dark" ? "white" : "black"'},
},
},
"transform": [
diff --git a/documentation/Styling.md b/documentation/Styling.md
new file mode 100644
index 000000000..50fe4dcea
--- /dev/null
+++ b/documentation/Styling.md
@@ -0,0 +1,67 @@
+# Styling
+
+This document covers CSS styling for the frontend.
+
+Textcavator uses the CSS framework [bulma](https://bulma.io/documentation/).
+
+Initial and derived variables from bulma are customised in [_utilities.css](/frontend/src/_utilities.scss). This file only defines variables and mixins and can be imported in component stylesheets. [styles.csss](/frontend/src/styles.scss) includes site-wide selectors.
+
+## Dark mode
+
+Dark mode is managed by the [ThemeService](/frontend/src/app/services/theme.service.ts).
+
+The service sets `data-theme="dark"` / `data-theme="light"` on the HTML root node, which is used by CSS selectors. If you need to observe the theme in a component, you can also use `ThemeService.theme$`.
+
+## Other libraries
+
+Several other libraries are used to provide components, visualisations, etc. These are often customised to fit the site theme and/or adapt to dark mode.
+
+## PrimeNG components
+
+We use several components from [primeNG](https://v19.primeng.org/). [primeng-theme.ts](/frontend/src/app/primeng-theme.ts) defines the preset to customise primeNG styles, mostly using bulma CSS variables.
+
+## Chart.js
+
+[select-color.ts](/frontend/src/app/utils/select-color.ts) defines the colour palettes for data visualisations and a utility function to select the nth colour. There are several palettes; the default is chosen to fit the site theme. The [palette selector](/frontend/src/app/visualization/visualization-footer/palette-select/palette-select.component.ts) lets users choose a preferred palette; the `VisualizationComponent` and `WordModelsComponent` provide the chosen palette as input to visualisation components.
+
+Chart.js has no built-in "dark mode". The `ThemeService` adjusts several defaults to get readable charts in dark mode, and updates all chart instances when the theme changes.
+
+## Vega
+
+To let a Vega visualisation adapt to dark/light mode, relevant colours should depend on a signal.
+
+Add the following signal in your vega document:
+
+```json
+{
+ "name": "theme",
+ "description": "Current site theme (light/dark)",
+ "bind": { "element": "#current-theme" }
+}
+```
+
+The value of the `theme` signal will be `"dark"` or `"light"`. When you define colours in your document, let theme depend on this signal, e.g. with a mark like this:
+
+```json
+{
+ "marks": [
+ {
+ "type": "text",
+ // ...
+ "encode": {
+ "update": {
+ "fill": {"signal": "theme === \"dark\" ? \"white\" : \"black\""}
+ },
+ }
+ },
+ ]
+}
+```
+
+When you display the visualisation, include a [theme indicator](/frontend/src/app/visualization/theme-indicator.directive.ts) on your page like this:
+
+```html
+
+```
+
+The Vega graph will bind the `theme` signal to the value of this element. The element will not be visible to users.
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index 41ae577ef..8c9e97b63 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -51,6 +51,7 @@ import { MatomoConfig, matomoImports } from './routing/matomo';
import { stylePreset } from './primeng-theme';
import { CoreModule } from './core/core.module';
+
export const appRoutes: Routes = [
{
path: 'search/:corpus',
diff --git a/frontend/src/app/core/footer/footer.component.html b/frontend/src/app/core/footer/footer.component.html
index eda42927f..ad1c9af2c 100644
--- a/frontend/src/app/core/footer/footer.component.html
+++ b/frontend/src/app/core/footer/footer.component.html
@@ -6,7 +6,12 @@
Textcavator is developed by