diff --git a/tabby-core/src/services/themes.service.ts b/tabby-core/src/services/themes.service.ts index 4cbeb6e2..5938eaa1 100644 --- a/tabby-core/src/services/themes.service.ts +++ b/tabby-core/src/services/themes.service.ts @@ -26,9 +26,9 @@ export class ThemesService { this.applyThemeVariables() config.changed$.subscribe(() => { this.applyCurrentTheme() + this.applyThemeVariables() }) }) - config.changed$.subscribe(() => this.applyThemeVariables()) } private applyThemeVariables () { @@ -62,6 +62,7 @@ export class ThemesService { // const backgroundMore =more(theme.background, 0.25).string() const accentIndex = 4 const vars: Record = {} + const contrastPairs: string[][] = [] if (this.findCurrentTheme().followsColorScheme) { vars['--bs-body-bg'] = background.string() @@ -82,6 +83,8 @@ export class ThemesService { // vars['--bs-purple'] = theme.colors[13] // vars['--bs-cyan'] = theme.colors[14] + contrastPairs.push(['--bs-body-bg', '--bs-body-color']) + vars['--theme-fg-more-2'] = more(theme.foreground, 0.5).string() vars['--theme-fg-more'] = more(theme.foreground, 0.25).string() vars['--theme-fg'] = theme.foreground @@ -94,6 +97,12 @@ export class ThemesService { vars['--theme-bg-more'] = backgroundMore vars['--theme-bg-more-2'] = more(backgroundMore, 0.25).string() + contrastPairs.push(['--theme-bg', '--theme-fg']) + contrastPairs.push(['--theme-bg-less', '--theme-fg-less']) + contrastPairs.push(['--theme-bg-less-2', '--theme-fg-less-2']) + contrastPairs.push(['--theme-bg-more', '--theme-fg-more']) + contrastPairs.push(['--theme-bg-more-2', '--theme-fg-more-2']) + const themeColors = { primary: theme.colors[accentIndex], secondary: theme.colors[8], @@ -117,6 +126,9 @@ export class ThemesService { vars[`--theme-${key}`] = color vars[`--theme-${key}-less`] = less(color, 0.25).string() vars[`--theme-${key}-less-2`] = less(color, 0.75).string() + vars[`--theme-${key}-fg`] = more(color, 1).string() + + contrastPairs.push([`--theme-${key}`, `--theme-${key}-fg`]) } const switchBackground = less(theme.colors[accentIndex], 0.25).string() @@ -125,6 +137,24 @@ export class ThemesService { vars['--spaciness'] = this.config.store.appearance.spaciness + for (const [bg, fg] of contrastPairs) { + const colorBg = Color(vars[bg]).hsl() + const colorFg = Color(vars[fg]).hsl() + const bgContrast = colorBg.contrast(colorFg) + const isLightBG = colorBg.luminosity() > colorFg.luminosity() + if (bgContrast < this.config.store.terminal.minimumContrastRatio) { + const targetLuminosityDarkFG = (colorBg.luminosity() + 0.05) / this.config.store.terminal.minimumContrastRatio - 0.05 + const targetLuminosityLightFG = (colorBg.luminosity() + 0.05) * this.config.store.terminal.minimumContrastRatio - 0.05 + + let candidateLuminosities = isLightBG ? [targetLuminosityDarkFG, targetLuminosityLightFG] : [targetLuminosityLightFG, targetLuminosityDarkFG] + candidateLuminosities = candidateLuminosities.map(x => Math.max(0, Math.min(1, x))) + const targetLuminosity = candidateLuminosities.reduce((a, b) => Math.abs(b - colorBg.luminosity()) < Math.abs(a - colorBg.luminosity()) ? a : b, colorFg.color[2] / 100) + + colorFg.color[2] = targetLuminosity * 100 + vars[fg] = colorFg + } + } + for (const [key, value] of Object.entries(vars)) { document.documentElement.style.setProperty(key, value) } diff --git a/tabby-core/src/theme.new.scss b/tabby-core/src/theme.new.scss index 151958b3..ffd3c4c3 100644 --- a/tabby-core/src/theme.new.scss +++ b/tabby-core/src/theme.new.scss @@ -195,14 +195,14 @@ tab-body { --bs-btn-focus-shadow-rgb: 130, 138, 145; --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-color: var(--theme-#{$color}-more-2); - --bs-btn-hover-color: var(--theme-#{$color}-more-2); - --bs-btn-active-color: var(--theme-#{$color}-more-2); - --bs-btn-disabled-color: var(--theme-#{$color}-more-2); + --bs-btn-color: var(--theme-#{$color}-fg); + --bs-btn-hover-color: var(--theme-#{$color}-fg); + --bs-btn-active-color: var(--theme-#{$color}-fg); + --bs-btn-disabled-color: var(--theme-#{$color}-fg); } .alert-#{$color} { - --bs-alert-bg: var(--theme-#{$color}-more-2); + --bs-alert-bg: var(--theme-#{$color}-fg); --bs-alert-border-color: var(--theme-#{$color}-more); --bs-alert-color: var(--theme-#{$color}); } diff --git a/tabby-settings/src/components/settingsTab.component.pug b/tabby-settings/src/components/settingsTab.component.pug index 50cf2b4f..e923d29f 100644 --- a/tabby-settings/src/components/settingsTab.component.pug +++ b/tabby-settings/src/components/settingsTab.component.pug @@ -114,6 +114,18 @@ .title(translate) Enable animations toggle([(ngModel)]='config.store.accessibility.animations', (ngModelChange)='saveConfiguration()') + .form-line + .header + .title(translate) Minimum contrast ratio + input.form-control( + type='number', + min='1', + max='21', + step='0.5', + [(ngModel)]='config.store.terminal.minimumContrastRatio', + (ngModelChange)='config.save()' + ) + ng-container(*ngFor='let provider of settingsProviders') li(*ngIf='provider.prioritized', [ngbNavItem]='provider.id') a.d-flex.align-items-center(ngbNavLink)