Skip to content

Commit 0c93b7a

Browse files
authored
YouTube CTL - design fixes (#215)
* Update YT review links to use blue-30 color for better contrast * Update background color for button hover and pressed states * Add font face styles to root element of modal to avoid having modal styles overwritten by the website * Update padding of modal title so it is not as long * Update copy for YT CTL modal | Add more safe guards for YT CTL messages * Fix setYoutubePreviewsEnabled message format for preview toggle * Update message handlers for YT CTL * Update CTL style elements creation and button styles - Change button styles from style text to class name based and update usage - Add makeFontFaceStyleElement() and makeBaseStyleElement() to create base style elements with Font Face styles and base link and button styles for CTL - Move FB login button to Shadow Root to prevent page from changing the button styles * Add JSDoc to makeButton() | other small improvements Co-authored-by: francielefaccin <[email protected]>
1 parent dd3ba27 commit 0c93b7a

File tree

8 files changed

+1009
-386
lines changed

8 files changed

+1009
-386
lines changed

Sources/ContentScopeScripts/dist/contentScope.js

+144-55
Original file line numberDiff line numberDiff line change
@@ -1993,10 +1993,16 @@
19931993
color: #111111;
19941994
`,
19951995
linkFont: `
1996-
color: #5784FF;
1996+
color: #7295F6;
19971997
`,
19981998
buttonBackground: `
19991999
background: #5784FF;
2000+
`,
2001+
buttonBackgroundHover: `
2002+
background: #557FF3;
2003+
`,
2004+
buttonBackgroundPress: `
2005+
background: #3969EF;
20002006
`,
20012007
toggleButtonText: `
20022008
color: #EEEEEE;
@@ -2017,6 +2023,12 @@
20172023
`,
20182024
buttonBackground: `
20192025
background: #3969EF;
2026+
`,
2027+
buttonBackgroundHover: `
2028+
background: #2B55CA;
2029+
`,
2030+
buttonBackgroundPress: `
2031+
background: #1E42A4;
20202032
`,
20212033
toggleButtonText: `
20222034
color: #666666;
@@ -2036,6 +2048,12 @@
20362048
`,
20372049
buttonFont: `
20382050
color: #222222;
2051+
`,
2052+
buttonBackgroundHover: `
2053+
background: rgba(0, 0, 0, 0.12);
2054+
`,
2055+
buttonBackgroundPress: `
2056+
background: rgba(0, 0, 0, 0.18);
20392057
`
20402058
},
20412059
button: `
@@ -2089,6 +2107,7 @@
20892107
margin-top: 10px;
20902108
z-index: 2147483647;
20912109
position: absolute;
2110+
line-height: normal;
20922111
`,
20932112
textBubbleWidth: 360, // Should match the width rule in textBubble
20942113
textBubbleLeftShift: 100, // Should match the CSS left: rule in textBubble
@@ -2212,7 +2231,7 @@
22122231
margin: 10px auto;
22132232
text-align: center;
22142233
border: none;
2215-
padding: 0;
2234+
padding: 0px 32px;
22162235
`,
22172236
modalContentText: `
22182237
font-family: DuckDuckGoPrivacyEssentials;
@@ -2953,9 +2972,9 @@
29532972
// },
29542973
// informationalModal: {
29552974
// icon: blockedYTVideo,
2956-
// messageTitle: 'Enable YouTube previews and reduce privacy?',
2975+
// messageTitle: 'Enable all YouTube previews?',
29572976
// messageBody: 'Showing previews will allow Google (which owns YouTube) to see some of your device’s information, but is still more private than playing the video.',
2958-
// confirmButtonText: 'Enable Previews',
2977+
// confirmButtonText: 'Enable All Previews',
29592978
// rejectButtonText: 'No Thanks'
29602979
// }
29612980
// }
@@ -3609,20 +3628,99 @@
36093628
element.style.cssText += cssText;
36103629
}
36113630

3631+
/**
3632+
* Create a `<style/>` element with DDG font-face styles/CSS
3633+
* to be attached to DDG wrapper elements
3634+
* @returns HTMLStyleElement
3635+
*/
3636+
function makeFontFaceStyleElement () {
3637+
// Put our custom font-faces inside the wrapper element, since
3638+
// @font-face does not work inside a shadowRoot.
3639+
// See https://github.com/mdn/interactive-examples/issues/887.
3640+
const fontFaceStyleElement = document.createElement('style');
3641+
fontFaceStyleElement.textContent = styles.fontStyle;
3642+
return fontFaceStyleElement
3643+
}
3644+
3645+
/**
3646+
* Create a `<style/>` element with base styles for DDG social container and
3647+
* button to be attached to DDG wrapper elements/shadowRoot, also returns a wrapper
3648+
* class name for Social Container link styles
3649+
* @param {"lightMode" | "darkMode"} mode Light or Dark mode value
3650+
* @returns {{wrapperClass: string, styleElement: HTMLStyleElement; }}
3651+
*/
3652+
function makeBaseStyleElement (mode = 'lightMode') {
3653+
// Style element includes our font & overwrites page styles
3654+
const styleElement = document.createElement('style');
3655+
const wrapperClass = 'DuckDuckGoSocialContainer';
3656+
styleElement.textContent = `
3657+
.${wrapperClass} a {
3658+
${styles[mode].linkFont}
3659+
font-weight: bold;
3660+
}
3661+
.${wrapperClass} a:hover {
3662+
${styles[mode].linkFont}
3663+
font-weight: bold;
3664+
}
3665+
.DuckDuckGoButton {
3666+
${styles.button}
3667+
}
3668+
.DuckDuckGoButton > div {
3669+
${styles.buttonTextContainer}
3670+
}
3671+
.DuckDuckGoButton.primary {
3672+
${styles[mode].buttonBackground}
3673+
}
3674+
.DuckDuckGoButton.primary > div {
3675+
${styles[mode].buttonFont}
3676+
}
3677+
.DuckDuckGoButton.primary:hover {
3678+
${styles[mode].buttonBackgroundHover}
3679+
}
3680+
.DuckDuckGoButton.primary:active {
3681+
${styles[mode].buttonBackgroundPress}
3682+
}
3683+
.DuckDuckGoButton.secondary {
3684+
${styles.cancelMode.buttonBackground}
3685+
}
3686+
.DuckDuckGoButton.secondary > div {
3687+
${styles.cancelMode.buttonFont}
3688+
}
3689+
.DuckDuckGoButton.secondary:hover {
3690+
${styles.cancelMode.buttonBackgroundHover}
3691+
}
3692+
.DuckDuckGoButton.secondary:active {
3693+
${styles.cancelMode.buttonBackgroundPress}
3694+
}
3695+
`;
3696+
return { wrapperClass, styleElement }
3697+
}
3698+
36123699
function makeTextButton (linkText, mode) {
36133700
const linkElement = document.createElement('a');
36143701
linkElement.style.cssText = styles.headerLink + styles[mode].linkFont;
36153702
linkElement.textContent = linkText;
36163703
return linkElement
36173704
}
36183705

3619-
function makeButton (buttonText, mode) {
3706+
/**
3707+
* Create a button element.
3708+
* @param {string} buttonText Text to be displayed inside the button
3709+
* @param {'lightMode' | 'darkMode' | 'cancelMode'} mode Key for theme value to determine the styling of the button. Key matches `styles[mode]` keys.
3710+
* - `'lightMode'`: Primary colors styling for light theme
3711+
* - `'darkMode'`: Primary colors styling for dark theme
3712+
* - `'cancelMode'`: Secondary colors styling for all themes
3713+
* @returns {HTMLButtonElement} Button element
3714+
*/
3715+
function makeButton (buttonText, mode = 'lightMode') {
36203716
const button = document.createElement('button');
3621-
button.style.cssText = styles.button + styles[mode].buttonBackground;
3622-
const textContainer = document.createElement('div');
3623-
textContainer.style.cssText = styles.buttonTextContainer + styles[mode].buttonFont;
3624-
textContainer.textContent = buttonText;
3625-
button.appendChild(textContainer);
3717+
button.classList.add('DuckDuckGoButton');
3718+
button.classList.add(mode === 'cancelMode' ? 'secondary' : 'primary');
3719+
if (buttonText) {
3720+
const textContainer = document.createElement('div');
3721+
textContainer.textContent = buttonText;
3722+
button.appendChild(textContainer);
3723+
}
36263724
return button
36273725
}
36283726

@@ -3700,23 +3798,26 @@
37003798
function makeLoginButton (buttonText, mode, hoverTextTitle, hoverTextBody, icon, originalElement) {
37013799
const container = document.createElement('div');
37023800
container.style.cssText = 'position: relative;';
3801+
container.appendChild(makeFontFaceStyleElement());
3802+
3803+
const shadowRoot = container.attachShadow({ mode: devMode ? 'open' : 'closed' });
37033804
// inherit any class styles on the button
37043805
container.className = 'fb-login-button FacebookLogin__button';
3705-
const styleElement = document.createElement('style');
3706-
styleElement.textContent = `
3806+
const { styleElement } = makeBaseStyleElement(mode);
3807+
styleElement.textContent += `
37073808
#DuckDuckGoPrivacyEssentialsHoverableText {
37083809
display: none;
37093810
}
37103811
#DuckDuckGoPrivacyEssentialsHoverable:hover #DuckDuckGoPrivacyEssentialsHoverableText {
37113812
display: block;
37123813
}
37133814
`;
3714-
container.appendChild(styleElement);
3815+
shadowRoot.appendChild(styleElement);
37153816

37163817
const hoverContainer = document.createElement('div');
37173818
hoverContainer.id = 'DuckDuckGoPrivacyEssentialsHoverable';
37183819
hoverContainer.style.cssText = styles.hoverContainer;
3719-
container.appendChild(hoverContainer);
3820+
shadowRoot.appendChild(hoverContainer);
37203821

37213822
// Make the button
37223823
const button = makeButton(buttonText, mode);
@@ -3745,7 +3846,7 @@
37453846
const hoverText = document.createElement('div');
37463847
hoverText.style.cssText = styles.hoverTextBody;
37473848
hoverText.textContent = hoverTextBody + ' ';
3748-
hoverText.appendChild(getLearnMoreLink());
3849+
hoverText.appendChild(getLearnMoreLink(mode));
37493850
hoverBox.appendChild(hoverText);
37503851

37513852
hoverContainer.appendChild(hoverBox);
@@ -3786,6 +3887,8 @@
37863887
modalContainer.setAttribute('data-key', 'modal');
37873888
modalContainer.style.cssText = styles.modalContainer;
37883889

3890+
modalContainer.appendChild(makeFontFaceStyleElement());
3891+
37893892
const closeModal = () => {
37903893
document.body.removeChild(modalContainer);
37913894
cancelModal(entity);
@@ -3794,6 +3897,8 @@
37943897
// Protect the contents of our modal inside a shadowRoot, to avoid
37953898
// it being styled by the website's stylesheets.
37963899
const shadowRoot = modalContainer.attachShadow({ mode: devMode ? 'open' : 'closed' });
3900+
const { styleElement } = makeBaseStyleElement('lightMode');
3901+
shadowRoot.appendChild(styleElement);
37973902

37983903
const pageOverlay = document.createElement('div');
37993904
pageOverlay.style.cssText = styles.overlay;
@@ -3805,10 +3910,6 @@
38053910
const modalTitle = createTitleRow('DuckDuckGo', null, closeModal);
38063911
modal.appendChild(modalTitle);
38073912

3808-
// Content
3809-
const modalContent = document.createElement('div');
3810-
modalContent.style.cssText = styles.modalContent;
3811-
38123913
const iconElement = document.createElement('img');
38133914
iconElement.style.cssText = styles.icon + styles.modalIcon;
38143915
iconElement.setAttribute('src', icon);
@@ -3818,6 +3919,10 @@
38183919
title.style.cssText = styles.modalContentTitle;
38193920
title.textContent = entityData[entity].modalTitle;
38203921

3922+
// Content
3923+
const modalContent = document.createElement('div');
3924+
modalContent.style.cssText = styles.modalContent;
3925+
38213926
const message = document.createElement('div');
38223927
message.style.cssText = styles.modalContentText;
38233928
message.textContent = entityData[entity].modalText + ' ';
@@ -3903,12 +4008,7 @@
39034008
const contentBlock = document.createElement('div');
39044009
contentBlock.style.cssText = styles.wrapperDiv;
39054010

3906-
// Put our custom font-faces inside the wrapper element, since
3907-
// @font-face does not work inside a shadowRoot.
3908-
// See https://github.com/mdn/interactive-examples/issues/887.
3909-
const fontFaceStyleElement = document.createElement('style');
3910-
fontFaceStyleElement.textContent = styles.fontStyle;
3911-
contentBlock.appendChild(fontFaceStyleElement);
4011+
contentBlock.appendChild(makeFontFaceStyleElement());
39124012

39134013
// Put everything else inside the shadowRoot of the wrapper element to
39144014
// reduce the chances of the website's stylesheets messing up the
@@ -3917,18 +4017,7 @@
39174017
const shadowRoot = contentBlock.attachShadow({ mode: shadowRootMode });
39184018

39194019
// Style element includes our font & overwrites page styles
3920-
const styleElement = document.createElement('style');
3921-
const wrapperClass = 'DuckDuckGoSocialContainer';
3922-
styleElement.textContent = `
3923-
.${wrapperClass} a {
3924-
${styles[widget.getMode()].linkFont}
3925-
font-weight: bold;
3926-
}
3927-
.${wrapperClass} a:hover {
3928-
${styles[widget.getMode()].linkFont}
3929-
font-weight: bold;
3930-
}
3931-
`;
4020+
const { wrapperClass, styleElement } = makeBaseStyleElement(widget.getMode());
39324021
shadowRoot.appendChild(styleElement);
39334022

39344023
// Create overall grid structure
@@ -4054,12 +4143,7 @@
40544143
youTubePreview.id = `yt-ctl-preview-${widget.widgetID}`;
40554144
youTubePreview.style.cssText = styles.wrapperDiv + styles.placeholderWrapperDiv;
40564145

4057-
// Put our custom font-faces inside the wrapper element, since
4058-
// @font-face does not work inside a shadowRoot.
4059-
// See https://github.com/mdn/interactive-examples/issues/887.
4060-
const fontFaceStyleElement = document.createElement('style');
4061-
fontFaceStyleElement.textContent = styles.fontStyle;
4062-
youTubePreview.appendChild(fontFaceStyleElement);
4146+
youTubePreview.appendChild(makeFontFaceStyleElement());
40634147

40644148
// Size the placeholder element to match the original video element styles.
40654149
// If no styles are in place, it will get its current size
@@ -4069,9 +4153,12 @@
40694153
// Protect the contents of our placeholder inside a shadowRoot, to avoid
40704154
// it being styled by the website's stylesheets.
40714155
const shadowRoot = youTubePreview.attachShadow({ mode: devMode ? 'open' : 'closed' });
4156+
const { wrapperClass, styleElement } = makeBaseStyleElement(widget.getMode());
4157+
shadowRoot.appendChild(styleElement);
40724158

40734159
const youTubePreviewDiv = document.createElement('div');
40744160
youTubePreviewDiv.style.cssText = styles.youTubeDialogDiv;
4161+
youTubePreviewDiv.classList.add(wrapperClass);
40754162
shadowRoot.appendChild(youTubePreviewDiv);
40764163

40774164
/** Preview Image */
@@ -4099,7 +4186,9 @@
40994186
topSection.appendChild(titleElement);
41004187

41014188
/** Text Button on top section */
4102-
const textButton = makeTextButton(widget.replaceSettings.buttonText, widget.getMode());
4189+
// Use darkMode styles because the preview background is dark and causes poor contrast
4190+
// with lightMode button, making it hard to read.
4191+
const textButton = makeTextButton(widget.replaceSettings.buttonText, 'darkMode');
41034192
textButton.id = titleID + 'TextButton';
41044193

41054194
textButton.addEventListener(
@@ -4112,8 +4201,8 @@
41124201
const playButtonRow = document.createElement('div');
41134202
playButtonRow.style.cssText = styles.youTubePlayButtonRow;
41144203

4115-
const playButton = document.createElement('button');
4116-
playButton.style.cssText = styles.button + styles.youTubePlayButton + styles[widget.getMode()].buttonBackground;
4204+
const playButton = makeButton('', widget.getMode());
4205+
playButton.style.cssText += styles.youTubePlayButton;
41174206

41184207
const videoPlayImg = document.createElement('img');
41194208
const videoPlayIcon = widget.replaceSettings.placeholder.videoPlayIcon[widget.getMode()];
@@ -4141,17 +4230,15 @@
41414230
);
41424231
previewToggle.addEventListener(
41434232
'click',
4144-
() => sendMessage('setYoutubePreviewsEnabled', {
4145-
name: 'youtubePreviewsEnabled',
4146-
value: false
4147-
})
4233+
() => sendMessage('setYoutubePreviewsEnabled', false)
41484234
);
41494235

41504236
/** Preview Info Text */
41514237
const previewText = document.createElement('div');
41524238
previewText.style.cssText = styles.contentText + styles.toggleButtonText + styles.youTubePreviewInfoText;
41534239
previewText.innerText = widget.replaceSettings.placeholder.previewInfoText + ' ';
4154-
previewText.appendChild(getLearnMoreLink());
4240+
// Use darkMode styles because of preview background
4241+
previewText.appendChild(getLearnMoreLink('darkMode'));
41554242

41564243
previewToggleRow.appendChild(previewToggle);
41574244
previewToggleRow.appendChild(previewText);
@@ -4205,12 +4292,14 @@
42054292
isYoutubePreviewsEnabled = resp;
42064293
},
42074294
setYoutubePreviewsEnabled: function (resp) {
4208-
if (!resp.messageType || resp.value === undefined) { return }
4209-
originalWindowDispatchEvent(new OriginalCustomEvent(resp.messageType, { detail: resp.value }));
4295+
if (resp?.messageType && typeof resp?.value === 'boolean') {
4296+
originalWindowDispatchEvent(new OriginalCustomEvent(resp.messageType, { detail: resp.value }));
4297+
}
42104298
},
42114299
getYouTubeVideoDetails: function (resp) {
4212-
if (!resp.status || !resp.videoURL) { return }
4213-
originalWindowDispatchEvent(new OriginalCustomEvent('ddg-ctp-youTubeVideoDetails', { detail: resp }));
4300+
if (resp?.status && typeof resp.videoURL === 'string') {
4301+
originalWindowDispatchEvent(new OriginalCustomEvent('ddg-ctp-youTubeVideoDetails', { detail: resp }));
4302+
}
42144303
},
42154304
enableSocialTracker: function (resp) {
42164305
originalWindowDispatchEvent(new OriginalCustomEvent('ddg-ctp-enableSocialTracker-complete', { detail: resp }));

0 commit comments

Comments
 (0)