-
Notifications
You must be signed in to change notification settings - Fork 3
Feature/dark mode #1973
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
lukavdplas
wants to merge
21
commits into
develop
Choose a base branch
from
feature/dark-mode
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Feature/dark mode #1973
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
67a59e9
create theme service
lukavdplas ea8cf86
set theme
lukavdplas b3132b2
switch logo based on theme
lukavdplas 8f83dcb
set barchart theme
lukavdplas d42985d
set defaults in ngram chart
lukavdplas a34dc4c
refresh similarity chart on theme change
lukavdplas 709d7ec
set all chart.js themes from theme service
lukavdplas 46f6dd8
adjust neighbor_network to theme
lukavdplas db94b14
outfactor theme indicator element to directive
lukavdplas 873c577
add theme toggle button to menu
lukavdplas debab7d
outfactor theme button component
lukavdplas e2c3c3a
update map colours with site theme
lukavdplas d8eaa8c
store theme in local storage
lukavdplas 2acd51a
add styling docs + docstrings
lukavdplas 7a6b58b
move theme button component to core module
lukavdplas 93d47ad
catch if stored theme is not a known value
lukavdplas 6c32deb
use dropdown for theme select
lukavdplas 087b787
dark theme styling
lukavdplas fa7d5c0
use light mode in peace portal
lukavdplas 9ffec28
themeservice code clarity
lukavdplas 03c3753
remove comment typo
lukavdplas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| <input iaThemeIndicator > | ||
| ``` | ||
|
|
||
| The Vega graph will bind the `theme` signal to the value of this element. The element will not be visible to users. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,6 +51,7 @@ import { MatomoConfig, matomoImports } from './routing/matomo'; | |
| import { stylePreset } from './primeng-theme'; | ||
| import { CoreModule } from './core/core.module'; | ||
|
|
||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Load-bearing whitespace? |
||
| export const appRoutes: Routes = [ | ||
| { | ||
| path: 'search/:corpus', | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { TestBed } from '@angular/core/testing'; | ||
|
|
||
| import { ThemeService } from './theme.service'; | ||
|
|
||
| describe('ThemeService', () => { | ||
| let service: ThemeService; | ||
|
|
||
| beforeEach(() => { | ||
| TestBed.configureTestingModule({}); | ||
| service = TestBed.inject(ThemeService); | ||
| }); | ||
|
|
||
| it('should be created', () => { | ||
| expect(service).toBeTruthy(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import { Injectable } from '@angular/core'; | ||
| import { BehaviorSubject, combineLatest, fromEvent, map, Observable, startWith } from 'rxjs'; | ||
| import { Chart } from 'chart.js'; | ||
| import _ from 'lodash'; | ||
| import { environment } from '@environments/environment'; | ||
|
|
||
| export enum Theme { | ||
| DARK = 'dark', | ||
| LIGHT = 'light', | ||
| } | ||
|
|
||
| @Injectable({ | ||
| providedIn: 'root' | ||
| }) | ||
| export class ThemeService { | ||
| /** Theme selection from the user. | ||
| * `null` means no explicit preference, i.e. use the system theme. | ||
| */ | ||
| selection$ = new BehaviorSubject<Theme | null>(null); | ||
|
|
||
| /** Theme used by the site */ | ||
| theme$: Observable<Theme>; | ||
|
|
||
| /** Default dark/light preference from the browser/OS */ | ||
| private systemTheme$: Observable<Theme>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They're called System Team |
||
| private storageKey = 'theme'; | ||
|
|
||
| constructor() { | ||
| const initialSelection = environment.runInIFrame ? Theme.LIGHT : this.readStoredSelection(); | ||
| this.selection$.next(initialSelection); | ||
| this.selection$.subscribe((theme) => this.storeSelection(theme)); | ||
|
|
||
| this.systemTheme$ = this.watchSystemTheme(); | ||
| this.theme$ = combineLatest([this.selection$, this.systemTheme$]).pipe( | ||
| map(([selection, system]) => selection || system) | ||
| ); | ||
| this.theme$.subscribe((theme) => this.applyTheme(theme)); | ||
| } | ||
|
|
||
| private watchSystemTheme(): Observable<Theme> { | ||
| const query = window.matchMedia('(prefers-color-scheme: dark)'); | ||
| return fromEvent<MediaQueryListEvent>(query, 'change').pipe( | ||
| startWith(query), | ||
| map(list => list.matches ? Theme.DARK : Theme.LIGHT) | ||
| ); | ||
| } | ||
|
|
||
| /** set theme in the site layout */ | ||
| private applyTheme(theme: Theme) { | ||
| const root = (document.getRootNode() as Document).documentElement; | ||
| root.setAttribute('data-theme', theme); | ||
| this.applyChartJSTheme(); | ||
| } | ||
|
|
||
| /** set chartjs defaults based on current style, and update active charts */ | ||
| private applyChartJSTheme() { | ||
| const style = window.getComputedStyle(document.body); | ||
| Chart.defaults.color = () => style.getPropertyValue('--bulma-text-strong'); | ||
| Chart.defaults.borderColor = () => style.getPropertyValue('--bulma-border'); | ||
|
|
||
| const active = _.values(Chart.instances); | ||
| for (let chart of active) { | ||
| chart.update(); | ||
| } | ||
| } | ||
|
|
||
| private readStoredSelection(): Theme | null { | ||
| const value = localStorage.getItem(this.storageKey); | ||
| if ([Theme.DARK, Theme.LIGHT].map(String).includes(value)) { | ||
| return value as Theme; | ||
| } else { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| private storeSelection(theme: Theme | null): void { | ||
| if (theme) { | ||
| localStorage.setItem(this.storageKey, theme); | ||
| } else { | ||
| localStorage.removeItem(this.storageKey); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| <div class="map-container"> | ||
| <div #vegaMap></div> | ||
| </div> | ||
|
|
||
| <input iaThemeIndicator > | ||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beyond impressed by this documentation.