Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions packages/hint-meta-theme-color/src/hint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ export default class MetaThemeColorHint implements IHint {
public constructor(context: HintContext) {

let bodyElementWasReached: boolean = false;
let firstThemeColorMetaElement: HTMLElement;
let themeColorElementsAndMedia: Map<string, HTMLElement> = new Map();

const checkIfThemeColorMetaElementWasSpecified = (event: TraverseEnd) => {
const pageDOM = context.pageDOM as HTMLDocument;
const { resource } = event;
const linksToManifest = pageDOM.querySelectorAll('link[rel="manifest"]').length > 0;

if (!firstThemeColorMetaElement && linksToManifest) {
if (themeColorElementsAndMedia.size === 0 && linksToManifest) {
context.report(
resource,
getMessage('metaElementNotSpecified', context.language),
Expand Down Expand Up @@ -150,16 +150,21 @@ export default class MetaThemeColorHint implements IHint {
}

/*
* Check if a `theme-color` meta element was already specified.
* Check if a `theme-color` meta element with the same media attribute was already specified.
*
* From https://html.spec.whatwg.org/multipage/semantics.html#meta-theme-color
* Multiple theme-color meta elements are allowed if they have different media attributes.
* From MDN: "Most meta properties can be used only once. However, theme-color can be
* used multiple times if unique media values are provided."
*
* " There must not be more than one meta element with its
* name attribute value set to an ASCII case-insensitive
* match for theme-color per document. "
* References:
* - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color
* - https://html.spec.whatwg.org/multipage/semantics.html#meta-theme-color
*/

if (firstThemeColorMetaElement) {
const mediaAttributeValue = normalizeString(element.getAttribute('media'), '');
const mediaKey = mediaAttributeValue || 'default'; // Use 'default' for elements without media attribute

if (themeColorElementsAndMedia.has(mediaKey)) {
context.report(
resource,
getMessage('metaElementDuplicated', context.language),
Expand All @@ -171,7 +176,7 @@ export default class MetaThemeColorHint implements IHint {
return;
}

firstThemeColorMetaElement = element;
themeColorElementsAndMedia.set(mediaKey, element);

// Check if the `theme-color` meta element:

Expand Down
47 changes: 45 additions & 2 deletions packages/hint-meta-theme-color/tests/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ const validColorValues = [
'transparent'
];

const generateThemeColorMetaElement = (contentValue = '#f00', nameValue = 'theme-color') => {
return `<meta name="${nameValue}" content="${contentValue}">`;
const generateThemeColorMetaElement = (contentValue = '#f00', nameValue = 'theme-color', mediaValue?: string) => {
const mediaAttr = mediaValue ? ` media="${mediaValue}"` : '';
return `<meta name="${nameValue}" content="${contentValue}"${mediaAttr}>`;
};

const generateTest = (colorValues: string[], valueType = 'valid', reason?: string) => {
Expand Down Expand Up @@ -112,6 +113,42 @@ const defaultTests: HintTest[] = [
}
];

// New tests for multiple theme-color elements with media attributes
const mediaAttributeTests: HintTest[] = [
{
name: `Multiple 'theme-color' elements with different media attributes should pass`,
serverConfig: generateHTMLPage(`${generateThemeColorMetaElement('#f00', 'theme-color', '(prefers-color-scheme: light)')}${generateThemeColorMetaElement('#333', 'theme-color', '(prefers-color-scheme: dark)')}`)
},
{
name: `Multiple 'theme-color' elements with same media attribute should fail`,
reports: [{
message: metaElementIsNotNeededErrorMessage,
severity: Severity.warning
}],
serverConfig: generateHTMLPage(`${generateThemeColorMetaElement('#f00', 'theme-color', '(prefers-color-scheme: light)')}${generateThemeColorMetaElement('#333', 'theme-color', '(prefers-color-scheme: light)')}`)
},
{
name: `One 'theme-color' without media and one with media should pass`,
serverConfig: generateHTMLPage(`${generateThemeColorMetaElement('#f00')}${generateThemeColorMetaElement('#333', 'theme-color', '(prefers-color-scheme: dark)')}`)
},
{
name: `Multiple 'theme-color' elements without media attributes should fail`,
reports: [{
message: metaElementIsNotNeededErrorMessage,
severity: Severity.warning
}],
serverConfig: generateHTMLPage(`${generateThemeColorMetaElement('#f00')}${generateThemeColorMetaElement('#333')}`)
},
{
name: `Multiple 'theme-color' elements with various different media attributes should pass`,
serverConfig: generateHTMLPage(`${generateThemeColorMetaElement('#f00', 'theme-color', '(prefers-color-scheme: light)')}${generateThemeColorMetaElement('#333', 'theme-color', '(prefers-color-scheme: dark)')}${generateThemeColorMetaElement('#666', 'theme-color', '(prefers-contrast: high)')}`)
},
{
name: `Issue #6001 scenario: theme-color with prefers-color-scheme dark and light should pass`,
serverConfig: generateHTMLPage(`${generateThemeColorMetaElement('#13171a', 'theme-color', '(prefers-color-scheme: dark)')}${generateThemeColorMetaElement('#f8f4f0', 'theme-color', '(prefers-color-scheme: light)')}`)
}
];

const testForNoSupportForHexWithAlpha: HintTest[] = [...generateTest(notAlwaysSupportedColorValues, 'unsupported', 'because of the targeted browsers')];

testHint(hintPath, defaultTests, {
Expand All @@ -120,4 +157,10 @@ testHint(hintPath, defaultTests, {
'firefox 60'
]
});
testHint(hintPath, mediaAttributeTests, {
browserslist: [
'chrome 65',
'firefox 60'
]
});
testHint(hintPath, testForNoSupportForHexWithAlpha, { browserslist: ['chrome 50'] });
Loading