diff --git a/src/core/best-practices.js b/src/core/best-practices.js
index ea48d5f2d1..f468291bd2 100644
--- a/src/core/best-practices.js
+++ b/src/core/best-practices.js
@@ -57,8 +57,11 @@ export function run() {
});
if (bps.length) {
if (bpSummary) {
- bpSummary.appendChild(html`
+
${textVersion}
`;
diff --git a/src/core/style.js b/src/core/style.js
index 8f3c4c2029..6d95a14375 100644
--- a/src/core/style.js
+++ b/src/core/style.js
@@ -25,7 +25,15 @@ function insertStyle() {
const styleElement = document.createElement("style");
styleElement.id = "respec-mainstyle";
styleElement.textContent = css;
- document.head.appendChild(styleElement);
+ // Insert before the first stylesheet or style element so author-provided
+ // stylesheets that are already in keep their later position, letting
+ // custom CSS override ReSpec's main stylesheet. Using "link[rel~='stylesheet'],
+ // style" avoids displacing preconnect, icon, or other non-stylesheet links.
+ // insertBefore(el, null) === appendChild when no match is found.
+ document.head.insertBefore(
+ styleElement,
+ document.head.querySelector("link[rel~='stylesheet'], style")
+ );
return styleElement;
}
diff --git a/tests/spec/SpecHelper.js b/tests/spec/SpecHelper.js
index 9eb6c0f2da..6b683ddc5b 100644
--- a/tests/spec/SpecHelper.js
+++ b/tests/spec/SpecHelper.js
@@ -146,6 +146,12 @@ function decorateDocument(doc, opts) {
});
}
+ function decorateHead({ head = "" }) {
+ if (head) {
+ doc.head.insertAdjacentHTML("beforeend", head);
+ }
+ }
+
if (opts.htmlAttrs) {
Object.keys(opts.htmlAttrs).reduce(
intoAttributes.bind(opts.htmlAttrs),
@@ -156,6 +162,7 @@ function decorateDocument(doc, opts) {
doc.title = opts.title;
}
decorateBody(opts);
+ decorateHead(opts);
addRespecConfig(opts);
if (!doc.querySelector("script[src]")) {
addReSpecLoader(opts);
diff --git a/tests/spec/core/best-practices-spec.js b/tests/spec/core/best-practices-spec.js
index 6d4a1fdd77..1446d5c095 100644
--- a/tests/spec/core/best-practices-spec.js
+++ b/tests/spec/core/best-practices-spec.js
@@ -81,4 +81,25 @@ describe("Core — Best Practices", () => {
);
expect(bps.querySelectorAll("ul li")).toHaveSize(3);
});
+
+ it("does not duplicate heading when bp-summary already has one", async () => {
+ const body = `
+
+ `;
+ const ops = {
+ config: makeBasicConfig(),
+ body,
+ };
+ const doc = await makeRSDoc(ops);
+ const bpSummary = doc.getElementById("bp-summary");
+ const headings = bpSummary.querySelectorAll("h1, h2, h3, h4, h5, h6");
+ expect(headings).toHaveSize(1);
+ expect(headings[0].textContent).toContain("Custom Heading");
+ });
});
diff --git a/tests/spec/core/caniuse-spec.js b/tests/spec/core/caniuse-spec.js
index f0fe7a6ae2..e1c676daa8 100644
--- a/tests/spec/core/caniuse-spec.js
+++ b/tests/spec/core/caniuse-spec.js
@@ -103,7 +103,11 @@ describe("Core — Can I Use", () => {
expect(firefox.width).toBe(20);
expect(firefox.height).toBe(20);
- expect(chrome.alt).toBe("Android Chrome logo");
+ expect(chrome.alt).toBe("");
+
+ // The parent cell has role=img with aria-label for accessibility
+ const firstCell = cells[0];
+ expect(firstCell.getAttribute("role")).toBe("img");
// The version numbers
const [firefoxVersion, chromeVersion, safariVersion] =
@@ -112,6 +116,12 @@ describe("Core — Can I Use", () => {
expect(firefoxVersion.textContent).toBe("66");
expect(safariVersion.textContent).toBe("—");
+ // aria-label for no-version cell uses "(version unknown)" to match visible "—"
+ const safariCell = safariVersion.closest(".caniuse-cell");
+ expect(safariCell.getAttribute("aria-label")).toBe(
+ "FEATURE is unknown support since iOS Safari (version unknown) on mobile."
+ );
+
// More info link
const moreInfoLink = cells.item(3);
expect(moreInfoLink.href).toBe("https://caniuse.com/FEATURE");
diff --git a/tests/spec/core/style-spec.js b/tests/spec/core/style-spec.js
index 06048ac159..fd7f06f586 100644
--- a/tests/spec/core/style-spec.js
+++ b/tests/spec/core/style-spec.js
@@ -7,4 +7,21 @@ describe("Core — Style", () => {
const style = doc.getElementById("respec-mainstyle");
expect(style).toBeTruthy();
});
+
+ it("inserts respec-mainstyle before author-provided stylesheets", async () => {
+ const ops = makeStandardOps(
+ {},
+ "
"
+ );
+ ops.head =
+ '
';
+ const doc = await makeRSDoc(ops);
+ const respecStyle = doc.getElementById("respec-mainstyle");
+ const authorLink = doc.querySelector("link.custom-author-style");
+ expect(respecStyle).toBeTruthy();
+ expect(authorLink).toBeTruthy();
+ // Bitmask: Node.DOCUMENT_POSITION_FOLLOWING = 4 means respecStyle comes before authorLink
+ const position = respecStyle.compareDocumentPosition(authorLink);
+ expect(position & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
+ });
});