From c306290ba9e46c57e82272af51b6c6b35bc5b49c Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Thu, 13 Aug 2020 13:36:27 +0200 Subject: [PATCH 01/12] feat: display an a11y warning when slide's colors do not meet contrast ratio --- .../app-slide-contrast.scss | 31 ++++++ .../app-slide-contrast/app-slide-contrast.tsx | 95 +++++++++++++++++++ .../pages/editor/app-editor/app-editor.tsx | 1 + .../src/app/utils/editor/contrast.utils.tsx | 67 +++++++++++++ studio/src/components.d.ts | 13 +++ .../theme/editor/editor-fullscreen.scss | 9 ++ 6 files changed, 216 insertions(+) create mode 100644 studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.scss create mode 100644 studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx create mode 100644 studio/src/app/utils/editor/contrast.utils.tsx diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.scss b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.scss new file mode 100644 index 000000000..4b7a6838f --- /dev/null +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.scss @@ -0,0 +1,31 @@ +app-slide-contrast { + position: absolute; + top: 16px; + left: 16px; + + display: flex; + justify-content: center; + align-items: center; + + background: var(--ion-color-warning); + color: var(--ion-color-warning-contrast); + + padding: 8px 16px; + border-radius: 16px; + + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); + + ion-label { + margin-right: 4px; + } + + transition: opacity 0.5s; + + visibility: initial; + opacity: 1; + + &:not(.warning) { + visibility: hidden; + opacity: 0; + } +} diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx new file mode 100644 index 000000000..b64a92ae0 --- /dev/null +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx @@ -0,0 +1,95 @@ +import {Component, h, Host, Listen, State} from '@stencil/core'; + +import {ContrastUtils, ParentsColors} from '../../../utils/editor/contrast.utils'; + +@Component({ + tag: 'app-slide-contrast', + styleUrl: 'app-slide-contrast.scss', +}) +export class AppSlideContrast { + private readonly lowestAACompliantLevel: number = 1 / 3; + + @State() + private warning: boolean = false; + + @Listen('slidesDidLoad', {target: 'document'}) + async onSlidesDidLoad() { + await this.analyzeContrast(); + } + + @Listen('slideDidUpdate', {target: 'document'}) + async onSlideDidUpdate() { + await this.analyzeContrast(); + } + + @Listen('slideNextDidChange', {target: 'document'}) + @Listen('slidePrevDidChange', {target: 'document'}) + @Listen('slideToChange', {target: 'document'}) + async onSlideNavigate() { + await this.analyzeContrast(); + } + + private async analyzeContrast() { + this.warning = await this.hasLowContrast(); + } + + private async hasLowContrast(): Promise { + const deck: HTMLElement = document.querySelector('main > deckgo-deck'); + + if (!deck) { + return false; + } + + const index = await (deck as any).getActiveIndex(); + + const slide: HTMLElement = deck.querySelector('.deckgo-slide-container:nth-child(' + (index + 1) + ')'); + + if (!slide) { + return false; + } + + const slots: NodeListOf = slide.querySelectorAll( + '[slot="title"]:not(:empty),[slot="content"]:not(:empty),[slot="start"]:not(:empty),[slot="end"]:not(:empty),[slot="header"]:not(:empty),[slot="footer"]:not(:empty)' + ); + + // TODO drr + // const slots: NodeListOf = slide.querySelectorAll('[slot="title"],[slot="content"],[slot="start"],[slot="end"],[slot="header"],[slot="footer"],deckgo-drr'); + + if (!slots || slots.length <= 0) { + return false; + } + + const parentsColors: ParentsColors = { + slideBgColor: slide.style.background, + slideColor: slide.style.color, + deckBgColor: deck.style.getPropertyValue('--background'), + deckColor: deck.style.getPropertyValue('--color'), + }; + + const promises: Promise[] = Array.from(slots).map((slot: HTMLElement) => ContrastUtils.calculateContrastRatio(slot, parentsColors)); + + const contrasts: number[] = await Promise.all(promises); + + if (!contrasts || contrasts.length <= 0) { + return false; + } + + const lowContrast: number | undefined = contrasts.find((contrast: number) => contrast > this.lowestAACompliantLevel); + + console.log(lowContrast, contrasts); + + return lowContrast !== undefined; + } + + render() { + return ( + + Low contrast + + + ); + } +} diff --git a/studio/src/app/pages/editor/app-editor/app-editor.tsx b/studio/src/app/pages/editor/app-editor/app-editor.tsx index 7df7243ba..c96b5dc61 100644 --- a/studio/src/app/pages/editor/app-editor/app-editor.tsx +++ b/studio/src/app/pages/editor/app-editor/app-editor.tsx @@ -652,6 +652,7 @@ export class AppEditor { {this.footer} + , diff --git a/studio/src/app/utils/editor/contrast.utils.tsx b/studio/src/app/utils/editor/contrast.utils.tsx new file mode 100644 index 000000000..e54c1bb0c --- /dev/null +++ b/studio/src/app/utils/editor/contrast.utils.tsx @@ -0,0 +1,67 @@ +export interface ParentsColors { + slideBgColor: string | undefined; + slideColor: string | undefined; + deckBgColor: string | undefined; + deckColor: string | undefined; +} + +export class ContrastUtils { + static async calculateContrastRatio(element: HTMLElement, parentsColors: ParentsColors): Promise { + const style: CSSStyleDeclaration = window.getComputedStyle(element); + + // TODO: how to handle alpha? both in custom color as in default backgroundColor rba(0,0,0,0) + + const bgColor: string = + element.style.background !== '' + ? style.backgroundColor + : parentsColors.slideBgColor !== '' + ? parentsColors.slideBgColor + : parentsColors.deckBgColor !== '' + ? parentsColors.deckBgColor + : style.backgroundColor; + const color: string = + element.style.color !== '' + ? style.color + : parentsColors.slideColor !== '' + ? parentsColors.slideColor + : parentsColors.deckColor !== '' + ? parentsColors.deckColor + : style.color; + + // TODO utils + const extractRgb = (rgb: string): number[] | undefined => { + const match: RegExpMatchArray | null = rgb.match(/(\d+),\s*(\d+),\s*(\d+)/); + + if (!match) { + return undefined; + } + + return match.splice(1, 3).map((v) => Number(v)); + }; + + const bgRgb: number[] | undefined = extractRgb(bgColor); + const colorRgb: number[] | undefined = extractRgb(color); + + if (bgColor === undefined || colorRgb === undefined) { + // 0 being AA and AAA level friendly. We assume that if for some reason we can't extract color, we better not display a warning about it. + return 0; + } + + const lumBg: number = this.calculateLuminance(bgRgb); + const lumColor: number = this.calculateLuminance(colorRgb); + + return this.calculateColorContrastRatio(lumBg, lumColor); + } + + private static calculateLuminance(rgb: number[]): number { + const a = rgb.map((v) => { + v /= 255; + return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); + }); + return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; + } + + private static calculateColorContrastRatio(firstColor: number, secondColor: number): number { + return firstColor > secondColor ? (secondColor + 0.05) / (firstColor + 0.05) : (firstColor + 0.05) / (secondColor + 0.05); + } +} diff --git a/studio/src/components.d.ts b/studio/src/components.d.ts index 833c205e8..024458398 100644 --- a/studio/src/components.d.ts +++ b/studio/src/components.d.ts @@ -367,6 +367,8 @@ export namespace Components { "redirect": string; "redirectId": string; } + interface AppSlideContrast { + } interface AppSlideNavigate { } interface AppSlotType { @@ -1000,6 +1002,12 @@ declare global { prototype: HTMLAppSigninElement; new (): HTMLAppSigninElement; }; + interface HTMLAppSlideContrastElement extends Components.AppSlideContrast, HTMLStencilElement { + } + var HTMLAppSlideContrastElement: { + prototype: HTMLAppSlideContrastElement; + new (): HTMLAppSlideContrastElement; + }; interface HTMLAppSlideNavigateElement extends Components.AppSlideNavigate, HTMLStencilElement { } var HTMLAppSlideNavigateElement: { @@ -1162,6 +1170,7 @@ declare global { "app-share-deck": HTMLAppShareDeckElement; "app-share-options": HTMLAppShareOptionsElement; "app-signin": HTMLAppSigninElement; + "app-slide-contrast": HTMLAppSlideContrastElement; "app-slide-navigate": HTMLAppSlideNavigateElement; "app-slot-type": HTMLAppSlotTypeElement; "app-team": HTMLAppTeamElement; @@ -1568,6 +1577,8 @@ declare namespace LocalJSX { "redirect"?: string; "redirectId"?: string; } + interface AppSlideContrast { + } interface AppSlideNavigate { "onReorder"?: (event: CustomEvent) => void; } @@ -1697,6 +1708,7 @@ declare namespace LocalJSX { "app-share-deck": AppShareDeck; "app-share-options": AppShareOptions; "app-signin": AppSignin; + "app-slide-contrast": AppSlideContrast; "app-slide-navigate": AppSlideNavigate; "app-slot-type": AppSlotType; "app-team": AppTeam; @@ -1814,6 +1826,7 @@ declare module "@stencil/core" { "app-share-deck": LocalJSX.AppShareDeck & JSXBase.HTMLAttributes; "app-share-options": LocalJSX.AppShareOptions & JSXBase.HTMLAttributes; "app-signin": LocalJSX.AppSignin & JSXBase.HTMLAttributes; + "app-slide-contrast": LocalJSX.AppSlideContrast & JSXBase.HTMLAttributes; "app-slide-navigate": LocalJSX.AppSlideNavigate & JSXBase.HTMLAttributes; "app-slot-type": LocalJSX.AppSlotType & JSXBase.HTMLAttributes; "app-team": LocalJSX.AppTeam & JSXBase.HTMLAttributes; diff --git a/studio/src/global/theme/editor/editor-fullscreen.scss b/studio/src/global/theme/editor/editor-fullscreen.scss index 1a5047142..aa3832122 100644 --- a/studio/src/global/theme/editor/editor-fullscreen.scss +++ b/studio/src/global/theme/editor/editor-fullscreen.scss @@ -60,6 +60,15 @@ box-shadow: none; } } + + app-slide-contrast { + display: none; + } + } + + app-slide-contrast { + top: 32px; + left: 32px; } } From b8c2143ad4eac101b0039035c474c1142fd2c95f Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Thu, 13 Aug 2020 14:03:27 +0200 Subject: [PATCH 02/12] feat: iterate through slots children --- .../app-slide-contrast/app-slide-contrast.tsx | 21 ++++++++++++++++--- .../src/app/utils/editor/contrast.utils.tsx | 4 ++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx index b64a92ae0..ebc31b1fe 100644 --- a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx @@ -22,6 +22,11 @@ export class AppSlideContrast { await this.analyzeContrast(); } + @Listen('deckDidChange', {target: 'document'}) + async onDeckDidChange() { + await this.analyzeContrast(); + } + @Listen('slideNextDidChange', {target: 'document'}) @Listen('slidePrevDidChange', {target: 'document'}) @Listen('slideToChange', {target: 'document'}) @@ -59,6 +64,18 @@ export class AppSlideContrast { return false; } + const slotsChildren = Array.from(slots).reduce((acc: HTMLElement[], slot: HTMLElement) => { + const children: NodeListOf = slot.querySelectorAll('*'); + + if (children && children.length > 0) { + acc.push(...Array.from(children)); + } + + return acc; + }, []); + + const elements: HTMLElement[] = slotsChildren && slotsChildren.length > 0 ? [...Array.from(slots), ...slotsChildren] : Array.from(slots); + const parentsColors: ParentsColors = { slideBgColor: slide.style.background, slideColor: slide.style.color, @@ -66,7 +83,7 @@ export class AppSlideContrast { deckColor: deck.style.getPropertyValue('--color'), }; - const promises: Promise[] = Array.from(slots).map((slot: HTMLElement) => ContrastUtils.calculateContrastRatio(slot, parentsColors)); + const promises: Promise[] = Array.from(elements).map((slot: HTMLElement) => ContrastUtils.calculateContrastRatio(slot, parentsColors)); const contrasts: number[] = await Promise.all(promises); @@ -76,8 +93,6 @@ export class AppSlideContrast { const lowContrast: number | undefined = contrasts.find((contrast: number) => contrast > this.lowestAACompliantLevel); - console.log(lowContrast, contrasts); - return lowContrast !== undefined; } diff --git a/studio/src/app/utils/editor/contrast.utils.tsx b/studio/src/app/utils/editor/contrast.utils.tsx index e54c1bb0c..c3a4e3319 100644 --- a/studio/src/app/utils/editor/contrast.utils.tsx +++ b/studio/src/app/utils/editor/contrast.utils.tsx @@ -12,7 +12,7 @@ export class ContrastUtils { // TODO: how to handle alpha? both in custom color as in default backgroundColor rba(0,0,0,0) const bgColor: string = - element.style.background !== '' + element.style.background !== '' && element.style.background !== 'initial' ? style.backgroundColor : parentsColors.slideBgColor !== '' ? parentsColors.slideBgColor @@ -20,7 +20,7 @@ export class ContrastUtils { ? parentsColors.deckBgColor : style.backgroundColor; const color: string = - element.style.color !== '' + element.style.color !== '' && element.style.color !== 'initial' ? style.color : parentsColors.slideColor !== '' ? parentsColors.slideColor From 4f8b4a060ab728eb530762cf0b20f44bb575450c Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sat, 15 Aug 2020 09:14:47 +0200 Subject: [PATCH 03/12] feat: handle opacity in contrast warning --- .../app-slide-contrast/app-slide-contrast.tsx | 4 +- .../src/app/utils/editor/contrast.utils.tsx | 150 ++++++++++++++++-- 2 files changed, 136 insertions(+), 18 deletions(-) diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx index ebc31b1fe..043394188 100644 --- a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx @@ -7,7 +7,7 @@ import {ContrastUtils, ParentsColors} from '../../../utils/editor/contrast.utils styleUrl: 'app-slide-contrast.scss', }) export class AppSlideContrast { - private readonly lowestAACompliantLevel: number = 1 / 3; + private readonly lowestAACompliantLevel: number = 3; @State() private warning: boolean = false; @@ -91,7 +91,7 @@ export class AppSlideContrast { return false; } - const lowContrast: number | undefined = contrasts.find((contrast: number) => contrast > this.lowestAACompliantLevel); + const lowContrast: number | undefined = contrasts.find((contrast: number) => contrast < this.lowestAACompliantLevel); return lowContrast !== undefined; } diff --git a/studio/src/app/utils/editor/contrast.utils.tsx b/studio/src/app/utils/editor/contrast.utils.tsx index c3a4e3319..80ff77b7c 100644 --- a/studio/src/app/utils/editor/contrast.utils.tsx +++ b/studio/src/app/utils/editor/contrast.utils.tsx @@ -9,8 +9,6 @@ export class ContrastUtils { static async calculateContrastRatio(element: HTMLElement, parentsColors: ParentsColors): Promise { const style: CSSStyleDeclaration = window.getComputedStyle(element); - // TODO: how to handle alpha? both in custom color as in default backgroundColor rba(0,0,0,0) - const bgColor: string = element.style.background !== '' && element.style.background !== 'initial' ? style.backgroundColor @@ -19,6 +17,7 @@ export class ContrastUtils { : parentsColors.deckBgColor !== '' ? parentsColors.deckBgColor : style.backgroundColor; + const color: string = element.style.color !== '' && element.style.color !== 'initial' ? style.color @@ -28,9 +27,140 @@ export class ContrastUtils { ? parentsColors.deckColor : style.color; - // TODO utils + // The text color may or may not be semi-transparent, but that doesn't matter + const extractRgba = (rgb: string): number[] | undefined => { + const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+),\s*([.\d]+)/); + + if (!match) { + return undefined; + } + + return match.splice(1, 4).map((v) => Number(v)); + }; + + const bgRgba = extractRgba(bgColor); + + if (!bgRgba || bgRgba.length < 4 || bgRgba[3] >= 1) { + return this.calculateContrastRatioOpaque(bgColor, color); + } + + return this.calculateContrastRatioAlpha(bgColor, color); + } + + private static calculateLuminance(rgb: number[]): number { + const a = rgb.map((v) => { + v /= 255; + return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); + }); + return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; + } + + private static calculateColorContrastRatio(firstColorLum: number, secondColorLum: number): number { + // return firstColorLum > secondColorLum ? (secondColorLum + 0.05) / (firstColorLum + 0.05) : (firstColorLum + 0.05) / (secondColorLum + 0.05); + + const l1 = firstColorLum + 0.05; + const l2 = secondColorLum + 0.05; + + let ratio = l1 / l2; + + if (l2 > l1) { + ratio = 1 / ratio; + } + + return ratio; + } + + // Source: https://github.com/LeaVerou/contrast-ratio/blob/eb7fe8f16206869f8d36d517d7eb0962830d0e81/color.js#L86 + private static async convertAlphaRgba(color: string, base: number[]): Promise { + const extractRgba = (rgb: string): number[] | undefined => { + const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+),\s*([.\d]+)/); + + if (!match) { + return undefined; + } + + return match.splice(1, 4).map((v) => Number(v)); + }; + + const rgba: number[] | undefined = extractRgba(color); + + if (!rgba || rgba.length < 4) { + return color; + } + + const alpha: number = rgba[3]; + + const rgb: number[] = []; + + for (let i = 0; i < 3; i++) { + rgb.push(rgba[i] * alpha + base[i] * base[3] * (1 - alpha)); + } + + // Not used here + // rgb[3] = alpha + base[3] * (1 - alpha); + + return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; + } + + private static async calculateColorContrastRatioWithBase( + bgColor: string, + lumColor: number, + base: number[] + ): Promise<{luminanceOverlay: number; ratio: number}> { + // TODO extract utils + const extractRgb = (rgb: string): number[] | undefined => { + const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+)/); + + if (!match) { + return undefined; + } + + return match.splice(1, 3).map((v) => Number(v)); + }; + + const overlay = extractRgb(await this.convertAlphaRgba(bgColor, base)); + + const lumOverlay: number = this.calculateLuminance(overlay); + + return { + luminanceOverlay: lumOverlay, + ratio: this.calculateColorContrastRatio(lumOverlay, lumColor), + }; + } + + private static async calculateContrastRatioAlpha(bgColor: string, color: string): Promise { + // TODO extract utils const extractRgb = (rgb: string): number[] | undefined => { - const match: RegExpMatchArray | null = rgb.match(/(\d+),\s*(\d+),\s*(\d+)/); + const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+)/); + + if (!match) { + return undefined; + } + + return match.splice(1, 3).map((v) => Number(v)); + }; + + const lumColor: number = this.calculateLuminance(extractRgb(color)); + + const onBlack: {luminanceOverlay: number; ratio: number} = await this.calculateColorContrastRatioWithBase(bgColor, lumColor, [0, 0, 0, 1]); + const onWhite: {luminanceOverlay: number; ratio: number} = await this.calculateColorContrastRatioWithBase(bgColor, lumColor, [255, 255, 255, 1]); + + const max = Math.max(onBlack.ratio, onWhite.ratio); + + let min = 1; + if (onBlack.luminanceOverlay > lumColor) { + min = onBlack.ratio; + } else if (onWhite.luminanceOverlay < lumColor) { + min = onWhite.ratio; + } + + return (min + max) / 2; + } + + private static async calculateContrastRatioOpaque(bgColor: string, color: string): Promise { + // TODO extract utils + const extractRgb = (rgb: string): number[] | undefined => { + const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+)/); if (!match) { return undefined; @@ -52,16 +182,4 @@ export class ContrastUtils { return this.calculateColorContrastRatio(lumBg, lumColor); } - - private static calculateLuminance(rgb: number[]): number { - const a = rgb.map((v) => { - v /= 255; - return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); - }); - return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; - } - - private static calculateColorContrastRatio(firstColor: number, secondColor: number): number { - return firstColor > secondColor ? (secondColor + 0.05) / (firstColor + 0.05) : (firstColor + 0.05) / (secondColor + 0.05); - } } From bc79b6a88736d603cb64101c6daba91b83bc975e Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sat, 15 Aug 2020 09:19:36 +0200 Subject: [PATCH 04/12] feat: extractRGB with decimals and expose functions --- utils/utils/src/utils/color-utils.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/utils/utils/src/utils/color-utils.ts b/utils/utils/src/utils/color-utils.ts index 7667e8e23..8bae4b254 100644 --- a/utils/utils/src/utils/color-utils.ts +++ b/utils/utils/src/utils/color-utils.ts @@ -23,15 +23,25 @@ export async function rgbToHex(rgb: string | undefined): Promise v.toString(16).padStart(2, '0')).join('')}`; }; - const extractRgb = (rgb: string): number[] | undefined => { - const match: RegExpMatchArray | null = rgb.match(/(\d+),\s*(\d+),\s*(\d+)/); + return toHex(extractRgb(rgb)); +} - if (!match) { - return undefined; - } +export function extractRgb(rgb: string): number[] | undefined { + const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+)/); - return match.splice(1, 3).map((v) => Number(v)); - }; + if (!match) { + return undefined; + } - return toHex(extractRgb(rgb)); + return match.splice(1, 3).map((v) => Number(v)); +} + +export function extractRgba(rgb: string): number[] | undefined { + const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+),\s*([.\d]+)/); + + if (!match) { + return undefined; + } + + return match.splice(1, 4).map((v) => Number(v)); } From d120e040f9a13e44e3c04bda9829e1f3d8d07231 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sat, 15 Aug 2020 09:22:17 +0200 Subject: [PATCH 05/12] release: utils v1.3.0 --- CHANGELOG.md | 2 +- utils/utils/CHANGELOG.md | 8 ++++++++ utils/utils/package-lock.json | 2 +- utils/utils/package.json | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6257ce44..36e57f2a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ ### Others - deck-utils: v2.4.0 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/master/utils/deck/CHANGELOG.md)) -- utils: v1.2.0 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/master/utils/utils/CHANGELOG.md)) +- utils: v1.3.0 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/master/utils/utils/CHANGELOG.md)) - starter kit: v2.6.4 ([CHANGELOG](https://github.com/deckgo/deckdeckgo-starter/blob/master/CHANGELOG.md)) diff --git a/utils/utils/CHANGELOG.md b/utils/utils/CHANGELOG.md index 906e26a20..9d9bf6438 100644 --- a/utils/utils/CHANGELOG.md +++ b/utils/utils/CHANGELOG.md @@ -1,3 +1,11 @@ + + +# 1.3.0 (2020-08-15) + +- improve `extractRgb` to support decimals value (as for example `rgb(5.5, 4.7, 4)`) +- expose function `extractRgb` +- add and expose function `extractRgba` + # 1.2.0 (2020-07-31) diff --git a/utils/utils/package-lock.json b/utils/utils/package-lock.json index 7df9c7b0d..c2e6f5f07 100644 --- a/utils/utils/package-lock.json +++ b/utils/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@deckdeckgo/utils", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/utils/utils/package.json b/utils/utils/package.json index 821dffdf1..72e158016 100644 --- a/utils/utils/package.json +++ b/utils/utils/package.json @@ -1,6 +1,6 @@ { "name": "@deckdeckgo/utils", - "version": "1.2.0", + "version": "1.3.0", "author": "David Dal Busco", "description": "A collection of utils methods and functions developed for DeckDeckGo", "license": "MIT", From 54f6f8e8a685a1bb13628dc5a455f2217e980a75 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sat, 15 Aug 2020 09:26:21 +0200 Subject: [PATCH 06/12] feat: use extracted functions --- studio/package-lock.json | 13 ++++-- studio/package.json | 2 +- .../src/app/utils/editor/contrast.utils.tsx | 45 +------------------ 3 files changed, 13 insertions(+), 47 deletions(-) diff --git a/studio/package-lock.json b/studio/package-lock.json index 89a54bce7..e79b3ab9b 100644 --- a/studio/package-lock.json +++ b/studio/package-lock.json @@ -102,6 +102,13 @@ "requires": { "@deckdeckgo/utils": "1.2.0", "prismjs": "^1.21.0" + }, + "dependencies": { + "@deckdeckgo/utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@deckdeckgo/utils/-/utils-1.2.0.tgz", + "integrity": "sha512-VAZAOeHdMKITOMawcur5Ih71F7VouAZyihSZda9s24q6dqf3K+3nHjtLa+v8cOzTIUkFkVzWGggvTSrcKjQMkw==" + } } }, "@deckdeckgo/inline-editor": { @@ -263,9 +270,9 @@ "dev": true }, "@deckdeckgo/utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@deckdeckgo/utils/-/utils-1.2.0.tgz", - "integrity": "sha512-VAZAOeHdMKITOMawcur5Ih71F7VouAZyihSZda9s24q6dqf3K+3nHjtLa+v8cOzTIUkFkVzWGggvTSrcKjQMkw==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@deckdeckgo/utils/-/utils-1.3.0.tgz", + "integrity": "sha512-QrV3jyqIlL+u1NUqSyMsnIsfORLqBuzEhK2+rGYCT89TLVhnPpOP0b11aP/XvHyJMzG9mZvjadLN/MQ9ls9UGw==" }, "@deckdeckgo/youtube": { "version": "1.1.2", diff --git a/studio/package.json b/studio/package.json index 3270c94ff..66235f4be 100644 --- a/studio/package.json +++ b/studio/package.json @@ -40,7 +40,7 @@ "@deckdeckgo/slide-title": "^1.1.3", "@deckdeckgo/slide-youtube": "^1.1.2", "@deckdeckgo/social": "^2.0.0", - "@deckdeckgo/utils": "^1.2.0", + "@deckdeckgo/utils": "^1.3.0", "@deckdeckgo/youtube": "^1.1.2", "@ionic/core": "^5.3.1", "firebase": "^7.17.2", diff --git a/studio/src/app/utils/editor/contrast.utils.tsx b/studio/src/app/utils/editor/contrast.utils.tsx index 80ff77b7c..bf77b6602 100644 --- a/studio/src/app/utils/editor/contrast.utils.tsx +++ b/studio/src/app/utils/editor/contrast.utils.tsx @@ -1,3 +1,5 @@ +import {extractRgb, extractRgba} from '@deckdeckgo/utils'; + export interface ParentsColors { slideBgColor: string | undefined; slideColor: string | undefined; @@ -72,16 +74,6 @@ export class ContrastUtils { // Source: https://github.com/LeaVerou/contrast-ratio/blob/eb7fe8f16206869f8d36d517d7eb0962830d0e81/color.js#L86 private static async convertAlphaRgba(color: string, base: number[]): Promise { - const extractRgba = (rgb: string): number[] | undefined => { - const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+),\s*([.\d]+)/); - - if (!match) { - return undefined; - } - - return match.splice(1, 4).map((v) => Number(v)); - }; - const rgba: number[] | undefined = extractRgba(color); if (!rgba || rgba.length < 4) { @@ -107,17 +99,6 @@ export class ContrastUtils { lumColor: number, base: number[] ): Promise<{luminanceOverlay: number; ratio: number}> { - // TODO extract utils - const extractRgb = (rgb: string): number[] | undefined => { - const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+)/); - - if (!match) { - return undefined; - } - - return match.splice(1, 3).map((v) => Number(v)); - }; - const overlay = extractRgb(await this.convertAlphaRgba(bgColor, base)); const lumOverlay: number = this.calculateLuminance(overlay); @@ -129,17 +110,6 @@ export class ContrastUtils { } private static async calculateContrastRatioAlpha(bgColor: string, color: string): Promise { - // TODO extract utils - const extractRgb = (rgb: string): number[] | undefined => { - const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+)/); - - if (!match) { - return undefined; - } - - return match.splice(1, 3).map((v) => Number(v)); - }; - const lumColor: number = this.calculateLuminance(extractRgb(color)); const onBlack: {luminanceOverlay: number; ratio: number} = await this.calculateColorContrastRatioWithBase(bgColor, lumColor, [0, 0, 0, 1]); @@ -158,17 +128,6 @@ export class ContrastUtils { } private static async calculateContrastRatioOpaque(bgColor: string, color: string): Promise { - // TODO extract utils - const extractRgb = (rgb: string): number[] | undefined => { - const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+)/); - - if (!match) { - return undefined; - } - - return match.splice(1, 3).map((v) => Number(v)); - }; - const bgRgb: number[] | undefined = extractRgb(bgColor); const colorRgb: number[] | undefined = extractRgb(color); From 2d941ed17d48891ce2f2e6a7e5cfe8c64f89d699 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sat, 15 Aug 2020 10:09:29 +0200 Subject: [PATCH 07/12] feat: contrast explanations --- .../app-slide-contrast.scss | 41 +++++++++++-------- .../app-slide-contrast/app-slide-contrast.tsx | 19 ++++++++- .../app-contrast-info/app-contrast-info.tsx | 35 ++++++++++++++++ .../editor/app-get-help/app-get-help.tsx | 2 +- studio/src/components.d.ts | 13 ++++++ 5 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 studio/src/app/popovers/editor/app-contrast-info/app-contrast-info.tsx diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.scss b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.scss index 4b7a6838f..5d980530b 100644 --- a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.scss +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.scss @@ -3,22 +3,6 @@ app-slide-contrast { top: 16px; left: 16px; - display: flex; - justify-content: center; - align-items: center; - - background: var(--ion-color-warning); - color: var(--ion-color-warning-contrast); - - padding: 8px 16px; - border-radius: 16px; - - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); - - ion-label { - margin-right: 4px; - } - transition: opacity 0.5s; visibility: initial; @@ -28,4 +12,29 @@ app-slide-contrast { visibility: hidden; opacity: 0; } + + button { + display: flex; + justify-content: center; + align-items: center; + + background: var(--ion-color-warning); + color: var(--ion-color-warning-contrast); + + padding: 6px 12px; + border-radius: 64px; + + position: relative; + overflow: hidden; + + outline: 0; + + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); + + font-size: var(--font-size-small); + + ion-label { + margin-right: 4px; + } + } } diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx index 043394188..d8c255d6b 100644 --- a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx @@ -1,6 +1,7 @@ import {Component, h, Host, Listen, State} from '@stencil/core'; import {ContrastUtils, ParentsColors} from '../../../utils/editor/contrast.utils'; +import {popoverController} from '@ionic/core'; @Component({ tag: 'app-slide-contrast', @@ -96,14 +97,28 @@ export class AppSlideContrast { return lowContrast !== undefined; } + private async openInformation($event: UIEvent) { + const popover: HTMLIonPopoverElement = await popoverController.create({ + component: 'app-contrast-info', + event: $event, + mode: 'ios', + cssClass: 'info', + }); + + await popover.present(); + } + render() { return ( - Low contrast - + ); } diff --git a/studio/src/app/popovers/editor/app-contrast-info/app-contrast-info.tsx b/studio/src/app/popovers/editor/app-contrast-info/app-contrast-info.tsx new file mode 100644 index 000000000..e9f21471d --- /dev/null +++ b/studio/src/app/popovers/editor/app-contrast-info/app-contrast-info.tsx @@ -0,0 +1,35 @@ +import {Component, Element, h} from '@stencil/core'; + +@Component({ + tag: 'app-contrast-info', +}) +export class AppContrastInfo { + @Element() el: HTMLElement; + + private async closePopover() { + await (this.el.closest('ion-popover') as HTMLIonPopoverElement).dismiss(); + } + + render() { + return ( +
+

Low contrast

+

We noticed that (a part of) the text color of this slide does not meet contrast ratio standards.

+

+ Elements are compared according{' '} + + WCAG + {' '} + Level AA. +

+ +

Note that if you are using semi-transparent background, the contrast ratio cannot be precise.

+
+ this.closePopover()}> + Got it + +
+
+ ); + } +} diff --git a/studio/src/app/popovers/editor/app-get-help/app-get-help.tsx b/studio/src/app/popovers/editor/app-get-help/app-get-help.tsx index cc8e7bd84..4494fa8f6 100644 --- a/studio/src/app/popovers/editor/app-get-help/app-get-help.tsx +++ b/studio/src/app/popovers/editor/app-get-help/app-get-help.tsx @@ -29,7 +29,7 @@ export class AppGetHelp { .

-
+
this.closePopover()}> Got it diff --git a/studio/src/components.d.ts b/studio/src/components.d.ts index 024458398..c105e527f 100644 --- a/studio/src/components.d.ts +++ b/studio/src/components.d.ts @@ -109,6 +109,8 @@ export namespace Components { } interface AppContactForm { } + interface AppContrastInfo { + } interface AppCreateSlide { } interface AppCustomData { @@ -510,6 +512,12 @@ declare global { prototype: HTMLAppContactFormElement; new (): HTMLAppContactFormElement; }; + interface HTMLAppContrastInfoElement extends Components.AppContrastInfo, HTMLStencilElement { + } + var HTMLAppContrastInfoElement: { + prototype: HTMLAppContrastInfoElement; + new (): HTMLAppContrastInfoElement; + }; interface HTMLAppCreateSlideElement extends Components.AppCreateSlide, HTMLStencilElement { } var HTMLAppCreateSlideElement: { @@ -1088,6 +1096,7 @@ declare global { "app-color-text-background": HTMLAppColorTextBackgroundElement; "app-contact": HTMLAppContactElement; "app-contact-form": HTMLAppContactFormElement; + "app-contrast-info": HTMLAppContrastInfoElement; "app-create-slide": HTMLAppCreateSlideElement; "app-custom-data": HTMLAppCustomDataElement; "app-custom-images": HTMLAppCustomImagesElement; @@ -1294,6 +1303,8 @@ declare namespace LocalJSX { } interface AppContactForm { } + interface AppContrastInfo { + } interface AppCreateSlide { "onSignIn"?: (event: CustomEvent) => void; } @@ -1626,6 +1637,7 @@ declare namespace LocalJSX { "app-color-text-background": AppColorTextBackground; "app-contact": AppContact; "app-contact-form": AppContactForm; + "app-contrast-info": AppContrastInfo; "app-create-slide": AppCreateSlide; "app-custom-data": AppCustomData; "app-custom-images": AppCustomImages; @@ -1744,6 +1756,7 @@ declare module "@stencil/core" { "app-color-text-background": LocalJSX.AppColorTextBackground & JSXBase.HTMLAttributes; "app-contact": LocalJSX.AppContact & JSXBase.HTMLAttributes; "app-contact-form": LocalJSX.AppContactForm & JSXBase.HTMLAttributes; + "app-contrast-info": LocalJSX.AppContrastInfo & JSXBase.HTMLAttributes; "app-create-slide": LocalJSX.AppCreateSlide & JSXBase.HTMLAttributes; "app-custom-data": LocalJSX.AppCustomData & JSXBase.HTMLAttributes; "app-custom-images": LocalJSX.AppCustomImages & JSXBase.HTMLAttributes; From f16a3d76c3e2936e297a5b254b0f0e4fe57b09f1 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sun, 16 Aug 2020 09:59:39 +0200 Subject: [PATCH 08/12] refactor: use utils --- studio/src/app/utils/editor/contrast.utils.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/studio/src/app/utils/editor/contrast.utils.tsx b/studio/src/app/utils/editor/contrast.utils.tsx index bf77b6602..d405f398d 100644 --- a/studio/src/app/utils/editor/contrast.utils.tsx +++ b/studio/src/app/utils/editor/contrast.utils.tsx @@ -30,17 +30,7 @@ export class ContrastUtils { : style.color; // The text color may or may not be semi-transparent, but that doesn't matter - const extractRgba = (rgb: string): number[] | undefined => { - const match: RegExpMatchArray | null = rgb.match(/([.\d]+),\s*([.\d]+),\s*([.\d]+),\s*([.\d]+)/); - - if (!match) { - return undefined; - } - - return match.splice(1, 4).map((v) => Number(v)); - }; - - const bgRgba = extractRgba(bgColor); + const bgRgba: number[] | undefined = extractRgba(bgColor); if (!bgRgba || bgRgba.length < 4 || bgRgba[3] >= 1) { return this.calculateContrastRatioOpaque(bgColor, color); From 82379534c9fa36a4de40bb0cbae1a311e6c016ad Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Mon, 17 Aug 2020 16:42:11 +0200 Subject: [PATCH 09/12] feat: recursively iterate through children to find correct colors and handle text node --- .../app-slide-contrast/app-slide-contrast.tsx | 43 ++++++++------ .../src/app/utils/editor/contrast.utils.tsx | 36 ++---------- studio/src/app/utils/editor/node.utils.tsx | 57 +++++++++++++++++++ 3 files changed, 89 insertions(+), 47 deletions(-) create mode 100644 studio/src/app/utils/editor/node.utils.tsx diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx index d8c255d6b..baa3d034b 100644 --- a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx @@ -1,8 +1,10 @@ import {Component, h, Host, Listen, State} from '@stencil/core'; -import {ContrastUtils, ParentsColors} from '../../../utils/editor/contrast.utils'; import {popoverController} from '@ionic/core'; +import {ContrastUtils} from '../../../utils/editor/contrast.utils'; +import {NodeUtils} from '../../../utils/editor/node.utils'; + @Component({ tag: 'app-slide-contrast', styleUrl: 'app-slide-contrast.scss', @@ -65,26 +67,26 @@ export class AppSlideContrast { return false; } - const slotsChildren = Array.from(slots).reduce((acc: HTMLElement[], slot: HTMLElement) => { - const children: NodeListOf = slot.querySelectorAll('*'); - - if (children && children.length > 0) { - acc.push(...Array.from(children)); - } + // Slots with direct text children + const slotsWithText: HTMLElement[] = await NodeUtils.childrenTextNode(slots); - return acc; - }, []); + // All children () of the slots + const children: HTMLElement[] = await NodeUtils.children(slots); - const elements: HTMLElement[] = slotsChildren && slotsChildren.length > 0 ? [...Array.from(slots), ...slotsChildren] : Array.from(slots); + const elements: HTMLElement[] = + children && children.length > 0 + ? slotsWithText && slotsWithText.length > 0 + ? [...Array.from(slotsWithText), ...children] + : [...children] + : slotsWithText && slotsWithText.length > 0 + ? [...slotsWithText] + : null; - const parentsColors: ParentsColors = { - slideBgColor: slide.style.background, - slideColor: slide.style.color, - deckBgColor: deck.style.getPropertyValue('--background'), - deckColor: deck.style.getPropertyValue('--color'), - }; + if (!elements) { + return false; + } - const promises: Promise[] = Array.from(elements).map((slot: HTMLElement) => ContrastUtils.calculateContrastRatio(slot, parentsColors)); + const promises: Promise[] = Array.from(elements).map((element: HTMLElement) => this.calculateRatio(element, deck, slide)); const contrasts: number[] = await Promise.all(promises); @@ -97,6 +99,13 @@ export class AppSlideContrast { return lowContrast !== undefined; } + private async calculateRatio(element: HTMLElement, deck: HTMLElement, slide: HTMLElement) { + const bgColor = await NodeUtils.findColors(element, 'background', deck, slide); + const color = await NodeUtils.findColors(element, 'color', deck, slide); + + return ContrastUtils.calculateContrastRatio(bgColor, color); + } + private async openInformation($event: UIEvent) { const popover: HTMLIonPopoverElement = await popoverController.create({ component: 'app-contrast-info', diff --git a/studio/src/app/utils/editor/contrast.utils.tsx b/studio/src/app/utils/editor/contrast.utils.tsx index d405f398d..cd9de1568 100644 --- a/studio/src/app/utils/editor/contrast.utils.tsx +++ b/studio/src/app/utils/editor/contrast.utils.tsx @@ -1,42 +1,18 @@ import {extractRgb, extractRgba} from '@deckdeckgo/utils'; -export interface ParentsColors { - slideBgColor: string | undefined; - slideColor: string | undefined; - deckBgColor: string | undefined; - deckColor: string | undefined; -} - export class ContrastUtils { - static async calculateContrastRatio(element: HTMLElement, parentsColors: ParentsColors): Promise { - const style: CSSStyleDeclaration = window.getComputedStyle(element); - - const bgColor: string = - element.style.background !== '' && element.style.background !== 'initial' - ? style.backgroundColor - : parentsColors.slideBgColor !== '' - ? parentsColors.slideBgColor - : parentsColors.deckBgColor !== '' - ? parentsColors.deckBgColor - : style.backgroundColor; - - const color: string = - element.style.color !== '' && element.style.color !== 'initial' - ? style.color - : parentsColors.slideColor !== '' - ? parentsColors.slideColor - : parentsColors.deckColor !== '' - ? parentsColors.deckColor - : style.color; + static async calculateContrastRatio(bgColor: string | undefined, color: string | undefined): Promise { + const bgColorWithDefault: string = bgColor === undefined || bgColor === '' ? `rgb(255, 255, 255)` : bgColor; + const colorWithDefault: string = color === undefined || color === '' ? `rgb(0, 0, 0)` : color; // The text color may or may not be semi-transparent, but that doesn't matter - const bgRgba: number[] | undefined = extractRgba(bgColor); + const bgRgba: number[] | undefined = extractRgba(bgColorWithDefault); if (!bgRgba || bgRgba.length < 4 || bgRgba[3] >= 1) { - return this.calculateContrastRatioOpaque(bgColor, color); + return this.calculateContrastRatioOpaque(bgColorWithDefault, colorWithDefault); } - return this.calculateContrastRatioAlpha(bgColor, color); + return this.calculateContrastRatioAlpha(bgColorWithDefault, colorWithDefault); } private static calculateLuminance(rgb: number[]): number { diff --git a/studio/src/app/utils/editor/node.utils.tsx b/studio/src/app/utils/editor/node.utils.tsx new file mode 100644 index 000000000..553cd4803 --- /dev/null +++ b/studio/src/app/utils/editor/node.utils.tsx @@ -0,0 +1,57 @@ +export interface NodeColors { + bgColor: string | undefined; + color: string | undefined; +} + +export class NodeUtils { + static async childrenTextNode(elements: NodeListOf): Promise { + return Array.from(elements).reduce((acc: HTMLElement[], slot: HTMLElement) => { + const text = Array.from(slot.childNodes).find((child) => child.nodeType === Node.TEXT_NODE); + + if (text !== null && text !== undefined && text.textContent.replace(/(?:\r\n|\r|\n|\s)/g, '') !== '') { + acc.push(slot); + } + + return acc; + }, []); + } + + static async children(elements: NodeListOf): Promise { + return Array.from(elements).reduce((acc: HTMLElement[], slot: HTMLElement) => { + const children: NodeListOf = slot.querySelectorAll('*'); + + if (children && children.length > 0) { + acc.push(...Array.from(children)); + } + + return acc; + }, []); + } + + static async findColors(node: HTMLElement, color: 'color' | 'background', slide: HTMLElement, deck: HTMLElement): Promise { + // Just in case + if (node.nodeName.toUpperCase() === 'HTML' || node.nodeName.toUpperCase() === 'BODY') { + return undefined; + } + + if (!node.parentNode) { + return undefined; + } + + if (node.isEqualNode(deck)) { + return deck.style.getPropertyValue(`--${color}`); + } + + if (node.isEqualNode(slide) && slide.style[color] !== '') { + return slide.style[color]; + } + + const styleAttr: string = color === 'background' ? 'background-color' : 'color'; + + if (node.style[styleAttr] !== '' && node.style[styleAttr] !== 'initial') { + return node.style[color]; + } + + return await this.findColors(node.parentElement, color, slide, deck); + } +} From 20ed7db58e90aae0d07a8a3f2585e06d35ca55b7 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Mon, 17 Aug 2020 16:47:03 +0200 Subject: [PATCH 10/12] feat: check contrast author slot --- .../components/editor/app-slide-contrast/app-slide-contrast.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx index baa3d034b..1a8970ea1 100644 --- a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx @@ -57,7 +57,7 @@ export class AppSlideContrast { } const slots: NodeListOf = slide.querySelectorAll( - '[slot="title"]:not(:empty),[slot="content"]:not(:empty),[slot="start"]:not(:empty),[slot="end"]:not(:empty),[slot="header"]:not(:empty),[slot="footer"]:not(:empty)' + '[slot="title"]:not(:empty),[slot="content"]:not(:empty),[slot="start"]:not(:empty),[slot="end"]:not(:empty),[slot="header"]:not(:empty),[slot="footer"]:not(:empty),[slot="author"]:not(:empty)' ); // TODO drr From dd442e4584f66b03d48b43252e519add7f7e3ec6 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Mon, 17 Aug 2020 17:02:34 +0200 Subject: [PATCH 11/12] fix: color attribute --- studio/src/app/utils/editor/node.utils.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/src/app/utils/editor/node.utils.tsx b/studio/src/app/utils/editor/node.utils.tsx index 553cd4803..1be09e516 100644 --- a/studio/src/app/utils/editor/node.utils.tsx +++ b/studio/src/app/utils/editor/node.utils.tsx @@ -49,7 +49,7 @@ export class NodeUtils { const styleAttr: string = color === 'background' ? 'background-color' : 'color'; if (node.style[styleAttr] !== '' && node.style[styleAttr] !== 'initial') { - return node.style[color]; + return node.style[styleAttr]; } return await this.findColors(node.parentElement, color, slide, deck); From 8206cfdaee41c6ee84f456bc372ec9cd60582c9e Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Mon, 17 Aug 2020 17:06:46 +0200 Subject: [PATCH 12/12] feat: add drr to check contrast --- .../editor/app-slide-contrast/app-slide-contrast.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx index 1a8970ea1..2f47f2fa1 100644 --- a/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx +++ b/studio/src/app/components/editor/app-slide-contrast/app-slide-contrast.tsx @@ -57,12 +57,9 @@ export class AppSlideContrast { } const slots: NodeListOf = slide.querySelectorAll( - '[slot="title"]:not(:empty),[slot="content"]:not(:empty),[slot="start"]:not(:empty),[slot="end"]:not(:empty),[slot="header"]:not(:empty),[slot="footer"]:not(:empty),[slot="author"]:not(:empty)' + '[slot="title"]:not(:empty),[slot="content"]:not(:empty),[slot="start"]:not(:empty),[slot="end"]:not(:empty),[slot="header"]:not(:empty),[slot="footer"]:not(:empty),[slot="author"]:not(:empty),deckgo-drr > section:not(:empty)' ); - // TODO drr - // const slots: NodeListOf = slide.querySelectorAll('[slot="title"],[slot="content"],[slot="start"],[slot="end"],[slot="header"],[slot="footer"],deckgo-drr'); - if (!slots || slots.length <= 0) { return false; } @@ -103,6 +100,8 @@ export class AppSlideContrast { const bgColor = await NodeUtils.findColors(element, 'background', deck, slide); const color = await NodeUtils.findColors(element, 'color', deck, slide); + console.log('yo', bgColor, color); + return ContrastUtils.calculateContrastRatio(bgColor, color); }