Skip to content

Commit 59b11d5

Browse files
authored
fix(theme): override automatic text color with classes (#22475)
1 parent f611768 commit 59b11d5

File tree

8 files changed

+78
-33
lines changed

8 files changed

+78
-33
lines changed

packages/vuetify/src/composables/__tests__/__snapshots__/theme.spec.ts.snap

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ exports[`createTheme > should allow for themes to be scoped 1`] = `
77
type="text/css"
88
>
99
@layer vuetify-utilities.theme {
10+
@layer base {
1011
:root {
1112
--v-theme-background: 255,255,255;
1213
--v-theme-background-overlay-multiplier: 1;
@@ -63,6 +64,8 @@ exports[`createTheme > should allow for themes to be scoped 1`] = `
6364
--v-theme-on-kbd: 0, 0, 0;
6465
--v-theme-code: 245, 245, 245;
6566
--v-theme-on-code: 0, 0, 0;
67+
--v-theme-on-dark: 255, 255, 255;
68+
--v-theme-on-light: 0, 0, 0;
6669
}
6770
.v-theme--light {
6871
color-scheme: normal;
@@ -121,6 +124,8 @@ exports[`createTheme > should allow for themes to be scoped 1`] = `
121124
--v-theme-on-kbd: 0, 0, 0;
122125
--v-theme-code: 245, 245, 245;
123126
--v-theme-on-code: 0, 0, 0;
127+
--v-theme-on-dark: 255, 255, 255;
128+
--v-theme-on-light: 0, 0, 0;
124129
}
125130
.v-theme--dark {
126131
color-scheme: dark;
@@ -179,6 +184,9 @@ exports[`createTheme > should allow for themes to be scoped 1`] = `
179184
--v-theme-on-kbd: 255, 255, 255;
180185
--v-theme-code: 52, 52, 52;
181186
--v-theme-on-code: 204, 204, 204;
187+
--v-theme-on-dark: 255, 255, 255;
188+
--v-theme-on-light: 0, 0, 0;
189+
}
182190
}
183191
@layer background {
184192
.bg-background {
@@ -374,6 +382,7 @@ exports[`createTheme > should allow for themes to be scoped 1`] = `
374382
type="text/css"
375383
>
376384
@layer vuetify-utilities.theme {
385+
@layer base {
377386
:where(#my-app) {
378387
--v-theme-background: 255,255,255;
379388
--v-theme-background-overlay-multiplier: 1;
@@ -430,6 +439,8 @@ exports[`createTheme > should allow for themes to be scoped 1`] = `
430439
--v-theme-on-kbd: 0, 0, 0;
431440
--v-theme-code: 245, 245, 245;
432441
--v-theme-on-code: 0, 0, 0;
442+
--v-theme-on-dark: 255, 255, 255;
443+
--v-theme-on-light: 0, 0, 0;
433444
}
434445
:where(#my-app) .v-theme--light {
435446
color-scheme: normal;
@@ -488,6 +499,8 @@ exports[`createTheme > should allow for themes to be scoped 1`] = `
488499
--v-theme-on-kbd: 0, 0, 0;
489500
--v-theme-code: 245, 245, 245;
490501
--v-theme-on-code: 0, 0, 0;
502+
--v-theme-on-dark: 255, 255, 255;
503+
--v-theme-on-light: 0, 0, 0;
491504
}
492505
:where(#my-app) .v-theme--dark {
493506
color-scheme: dark;
@@ -546,6 +559,9 @@ exports[`createTheme > should allow for themes to be scoped 1`] = `
546559
--v-theme-on-kbd: 255, 255, 255;
547560
--v-theme-code: 52, 52, 52;
548561
--v-theme-on-code: 204, 204, 204;
562+
--v-theme-on-dark: 255, 255, 255;
563+
--v-theme-on-light: 0, 0, 0;
564+
}
549565
}
550566
@layer background {
551567
:where(#my-app) .bg-background {
@@ -746,6 +762,7 @@ exports[`createTheme > should create style element 1`] = `
746762
type="text/css"
747763
>
748764
@layer vuetify-utilities.theme {
765+
@layer base {
749766
:root {
750767
--v-theme-background: 255,255,255;
751768
--v-theme-background-overlay-multiplier: 1;
@@ -802,6 +819,8 @@ exports[`createTheme > should create style element 1`] = `
802819
--v-theme-on-kbd: 0, 0, 0;
803820
--v-theme-code: 245, 245, 245;
804821
--v-theme-on-code: 0, 0, 0;
822+
--v-theme-on-dark: 255, 255, 255;
823+
--v-theme-on-light: 0, 0, 0;
805824
}
806825
.v-theme--light {
807826
color-scheme: normal;
@@ -860,6 +879,8 @@ exports[`createTheme > should create style element 1`] = `
860879
--v-theme-on-kbd: 0, 0, 0;
861880
--v-theme-code: 245, 245, 245;
862881
--v-theme-on-code: 0, 0, 0;
882+
--v-theme-on-dark: 255, 255, 255;
883+
--v-theme-on-light: 0, 0, 0;
863884
}
864885
.v-theme--dark {
865886
color-scheme: dark;
@@ -918,6 +939,9 @@ exports[`createTheme > should create style element 1`] = `
918939
--v-theme-on-kbd: 255, 255, 255;
919940
--v-theme-code: 52, 52, 52;
920941
--v-theme-on-code: 204, 204, 204;
942+
--v-theme-on-dark: 255, 255, 255;
943+
--v-theme-on-light: 0, 0, 0;
944+
}
921945
}
922946
@layer background {
923947
.bg-background {

packages/vuetify/src/composables/__tests__/color.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ describe('color.ts', () => {
1818
[{ bg: null }, [[], {}]],
1919
[{ bg: '' }, [[], {}]],
2020
[{ bg: 'primary' }, [['bg-primary'], {}]],
21-
[{ bg: '#FF00FF' }, [[], { backgroundColor: '#FF00FF', color: '#fff', caretColor: '#fff' }]],
22-
// [{ bg: '#FF00FF' }, [[], { backgroundColor: '#FF00FF' }]],
21+
[{ bg: '#FF00FF' }, [['v-theme-on-dark'], { backgroundColor: '#FF00FF' }]],
2322
])('should return correct color classes and styles', (value, [classes, styles]) => {
2423
const { backgroundColorClasses, backgroundColorStyles } = useBackgroundColor(() => value.bg)
2524

@@ -33,7 +32,7 @@ describe('color.ts', () => {
3332
[{ background: null }, [[], {}]],
3433
[{ background: '' }, [[], {}]],
3534
[{ background: 'primary' }, [['bg-primary'], {}]],
36-
[{ background: '#FF00FF' }, [[], { backgroundColor: '#FF00FF', color: '#fff', caretColor: '#fff' }]],
35+
[{ background: '#FF00FF' }, [['v-theme-on-dark'], { backgroundColor: '#FF00FF' }]],
3736
[{ text: null }, [[], {}]],
3837
[{ text: '' }, [[], {}]],
3938
[{ text: 'primary' }, [['text-primary'], {}]],

packages/vuetify/src/composables/color.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Utilities
22
import { toValue } from 'vue'
3-
import { destructComputed, getForeground, isCssColor, isParsableColor, parseColor } from '@/util'
3+
import { destructComputed, hasLightForeground, isCssColor, isParsableColor, parseColor } from '@/util'
44

55
// Types
66
import type { CSSProperties, MaybeRefOrGetter, Ref } from 'vue'
@@ -63,10 +63,10 @@ export function computeColor (colors: MaybeRefOrGetter<{ background?: ColorValue
6363
if (!_colors.text && isParsableColor(_colors.background)) {
6464
const backgroundColor = parseColor(_colors.background)
6565
if (backgroundColor.a == null || backgroundColor.a === 1) {
66-
const textColor = getForeground(backgroundColor)
67-
68-
styles.color = textColor
69-
styles.caretColor = textColor
66+
classes.push(hasLightForeground(backgroundColor)
67+
? 'v-theme-on-dark'
68+
: 'v-theme-on-light'
69+
)
7070
}
7171
}
7272
} else {

packages/vuetify/src/composables/theme.ts

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import {
1717
darken,
1818
deprecate,
1919
getCurrentInstance,
20-
getForeground,
2120
getLuma,
21+
hasLightForeground,
2222
IN_BROWSER,
2323
lighten,
2424
mergeDeep,
@@ -32,6 +32,7 @@ import {
3232
import type { VueHeadClient } from '@unhead/vue/client'
3333
import type { HeadClient } from '@vueuse/head'
3434
import type { App, DeepReadonly, InjectionKey, Ref } from 'vue'
35+
import type { Color } from '@/util'
3536

3637
type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T
3738

@@ -71,29 +72,29 @@ interface InternalThemeDefinition {
7172
}
7273

7374
export interface Colors extends BaseColors, OnColors {
74-
[key: string]: string
75+
[key: string]: Color
7576
}
7677

7778
interface BaseColors {
78-
background: string
79-
surface: string
80-
primary: string
81-
secondary: string
82-
success: string
83-
warning: string
84-
error: string
85-
info: string
79+
background: Color
80+
surface: Color
81+
primary: Color
82+
secondary: Color
83+
success: Color
84+
warning: Color
85+
error: Color
86+
info: Color
8687
}
8788

8889
interface OnColors {
89-
'on-background': string
90-
'on-surface': string
91-
'on-primary': string
92-
'on-secondary': string
93-
'on-success': string
94-
'on-warning': string
95-
'on-error': string
96-
'on-info': string
90+
'on-background': Color
91+
'on-surface': Color
92+
'on-primary': Color
93+
'on-secondary': Color
94+
'on-success': Color
95+
'on-warning': Color
96+
'on-error': Color
97+
'on-info': Color
9798
}
9899

99100
export interface ThemeInstance {
@@ -166,6 +167,8 @@ function genDefaults () {
166167
'theme-on-kbd': '#000000',
167168
'theme-code': '#F5F5F5',
168169
'theme-on-code': '#000000',
170+
'theme-on-dark': '#FFF',
171+
'theme-on-light': '#000',
169172
},
170173
},
171174
dark: {
@@ -203,6 +206,8 @@ function genDefaults () {
203206
'theme-on-kbd': '#FFFFFF',
204207
'theme-code': '#343434',
205208
'theme-on-code': '#CCCCCC',
209+
'theme-on-dark': '#FFF',
210+
'theme-on-light': '#000',
206211
},
207212
},
208213
},
@@ -261,7 +266,7 @@ function genCssVariables (theme: InternalThemeDefinition, prefix: string) {
261266
return variables
262267
}
263268

264-
function genVariation (name: string, color: string, variations: VariationsOptions | false) {
269+
function genVariation (name: string, color: Color, variations: VariationsOptions | false) {
265270
const object: Record<string, string> = {}
266271
if (variations) {
267272
for (const variation of (['lighten', 'darken'] as const)) {
@@ -291,7 +296,7 @@ function genVariations (colors: InternalThemeDefinition['colors'], variations: V
291296
return variationColors
292297
}
293298

294-
function genOnColors (colors: InternalThemeDefinition['colors']) {
299+
function genOnColors (colors: InternalThemeDefinition['colors'], variables: InternalThemeDefinition['variables']) {
295300
const onColors = {} as InternalThemeDefinition['colors']
296301

297302
for (const color of Object.keys(colors)) {
@@ -300,7 +305,9 @@ function genOnColors (colors: InternalThemeDefinition['colors']) {
300305
const onColor = `on-${color}` as keyof OnColors
301306
const colorVal = parseColor(colors[color])
302307

303-
onColors[onColor] = getForeground(colorVal)
308+
onColors[onColor] = hasLightForeground(colorVal)
309+
? variables['theme-on-dark']
310+
: variables['theme-on-light']
304311
}
305312

306313
return onColors
@@ -368,7 +375,7 @@ export function createTheme (options?: ThemeOptions): ThemeInstance & { install:
368375
...original,
369376
colors: {
370377
...colors,
371-
...genOnColors(colors),
378+
...genOnColors(colors, original.variables),
372379
},
373380
}
374381
}
@@ -383,6 +390,8 @@ export function createTheme (options?: ThemeOptions): ThemeInstance & { install:
383390
const lines: string[] = []
384391
const scoped = parsedOptions.scoped ? parsedOptions.prefix : ''
385392

393+
lines.push('@layer base {\n')
394+
386395
if (current.value?.dark) {
387396
createCssClass(lines, ':root', ['color-scheme: dark'], parsedOptions.scope)
388397
}
@@ -396,6 +405,8 @@ export function createTheme (options?: ThemeOptions): ThemeInstance & { install:
396405
], parsedOptions.scope)
397406
}
398407

408+
lines.push('}\n')
409+
399410
if (parsedOptions.utilities) {
400411
const bgLines: string[] = []
401412
const fgLines: string[] = []

packages/vuetify/src/styles/generic/_index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@use './layers';
22
@use './animations';
3+
@use './theme';
34
@use './colors';
45
@use './reset';
56
@use './transitions';

packages/vuetify/src/styles/generic/_layers.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@layer vuetify-utilities {
1414
@layer helpers;
1515
@layer theme {
16-
@layer background, foreground;
16+
@layer base, background, foreground;
1717
}
1818
}
1919

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@use '../tools';
2+
3+
@include tools.layer('utilities.theme.base') {
4+
.v-theme-on-light {
5+
color: rgb(var(--v-theme-on-light));
6+
}
7+
.v-theme-on-dark {
8+
color: rgb(var(--v-theme-on-dark));
9+
}
10+
}

packages/vuetify/src/util/colorUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ export function getContrast (first: Color, second: Color) {
290290
return (light + 0.05) / (dark + 0.05)
291291
}
292292

293-
export function getForeground (color: Color) {
293+
export function hasLightForeground (color: Color) {
294294
const blackContrast = Math.abs(APCAcontrast(parseColor(0), parseColor(color)))
295295
const whiteContrast = Math.abs(APCAcontrast(parseColor(0xffffff), parseColor(color)))
296296

@@ -304,5 +304,5 @@ export function getForeground (color: Color) {
304304
// }
305305

306306
// Prefer white text if both have an acceptable contrast ratio
307-
return whiteContrast > Math.min(blackContrast, 50) ? '#fff' : '#000'
307+
return whiteContrast > Math.min(blackContrast, 50)
308308
}

0 commit comments

Comments
 (0)