Skip to content

Commit ef9b1cd

Browse files
committed
fix(core/caniuse): mark up browser support groups as a dl
1 parent a788eb3 commit ef9b1cd

3 files changed

Lines changed: 106 additions & 14 deletions

File tree

src/core/caniuse.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,21 +179,22 @@ async function processJson(json, { feature }) {
179179
]);
180180
const toBrowserCell = browserCellRenderer(feature);
181181
results.reduce(toBrowserCell, groups);
182-
const out = [...groups]
182+
const entries = [...groups]
183183
.filter(([, arr]) => arr.length)
184184
.map(
185+
// A dl only permits dt/dd, optionally grouped in a div. The dt comes
186+
// first for valid markup; the CSS puts the label back on the rule under
187+
// the browsers, where it reads as a legend.
185188
([key, arr]) =>
186189
html`<div class="caniuse-group">
187-
<div class="caniuse-browsers">${arr}</div>
188-
<div class="caniuse-type"><span>${key}</div>
190+
<dt class="caniuse-type"><span>${key}</span></dt>
191+
<dd class="caniuse-browsers">${arr}</dd>
189192
</div>`
190193
);
191-
out.push(
192-
html`<a class="caniuse-cell" href="https://caniuse.com/${feature}"
194+
return html`<dl class="caniuse-groups">${entries}</dl>
195+
<a class="caniuse-more-info" href="https://caniuse.com/${feature}"
193196
>More info</a
194-
>`
195-
);
196-
return out;
197+
>`;
197198
}
198199

199200
/**

src/styles/caniuse.css.js

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,64 @@ export default css`
1010
column-gap: 2em;
1111
}
1212
13+
/* The dl is a new wrapper around the groups, so it has to be transparent to
14+
the flex row that used to hold them directly. */
15+
.caniuse-groups {
16+
display: flex;
17+
flex: 1;
18+
column-gap: 2em;
19+
margin: 0;
20+
}
21+
22+
/* Narrow screens: stack the groups full width so the pills wrap instead of
23+
forming tall single-file columns, and give "More info" its own row.
24+
767px matches the only other breakpoint in ReSpec (respec.css.js). */
25+
@media (max-width: 767px) {
26+
.caniuse-stats {
27+
display: block;
28+
}
29+
30+
.caniuse-groups {
31+
flex-direction: column;
32+
}
33+
34+
/* Space every group equally, including the first. Using padding rather than
35+
row-gap avoids the second group getting gap plus padding while the first
36+
gets padding alone. Generous, because each group's label hangs below its
37+
own rule, and a tight gap reads as the label belonging to the group
38+
beneath it. */
39+
.caniuse-group {
40+
padding-top: 1.5em;
41+
}
42+
43+
.caniuse-more-info {
44+
display: block;
45+
text-align: right;
46+
margin-top: 0.5em;
47+
}
48+
}
49+
50+
/* column-reverse, not column: a dl requires the dt before its dd, but the
51+
label belongs visually below, straddling the rule under the browsers. */
1352
.caniuse-group {
1453
display: flex;
1554
flex: 1;
16-
flex-direction: column;
55+
flex-direction: column-reverse;
1756
justify-content: flex-end;
1857
flex-basis: auto;
58+
/* Establish a stacking context so the label can be lifted above the dd's
59+
bottom border. Reversing the visual order does not reverse paint order:
60+
the dd is a later sibling, so its border would paint over the label and
61+
strike the text through. */
62+
position: relative;
1963
}
2064
2165
.caniuse-browsers {
2266
display: flex;
2367
align-items: baseline;
2468
justify-content: space-between;
2569
flex-wrap: wrap;
26-
margin-top: .2em;
70+
margin: .2em 0 0;
2771
column-gap: .4em;
2872
border-bottom: 1px solid #ccc;
2973
row-gap: .4em;
@@ -37,10 +81,20 @@ export default css`
3781
font-size: .8em;
3882
margin-top: -.8em;
3983
font-weight: bold;
84+
/* Above the dd's border, so the label's background breaks the rule instead
85+
of the rule striking through the text. */
86+
position: relative;
87+
z-index: 1;
4088
}
4189
90+
/* The label punches a hole in the rule above it, so its background has to match
91+
the page. This was var(--bg, white), but --bg is defined nowhere in ReSpec,
92+
so it always resolved to literal white: a white patch on a dark page.
93+
Canvas/CanvasText are system colours that follow the used color-scheme, so
94+
this tracks both the OS preference and ReSpec's own dark toggle. */
4295
.caniuse-type span {
43-
background-color: var(--bg, white);
96+
background-color: Canvas;
97+
color: CanvasText;
4498
padding: 0 0.4em;
4599
}
46100
@@ -83,7 +137,7 @@ img.caniuse-browser {
83137
font-size: .9em;
84138
}
85139
86-
.caniuse-stats a[href] {
140+
.caniuse-more-info {
87141
white-space: nowrap;
88142
align-self: flex-end;
89143
}
@@ -118,6 +172,17 @@ see https://github.com/Fyrd/caniuse/blob/master/CONTRIBUTING.md for stats */
118172
119173
/* handle case when printing */
120174
@media print {
175+
/* Browsers drop backgrounds when printing, so the label can no longer punch
176+
a hole in the rule and the line would strike through the text. Drop the
177+
rule instead and let the label sit under its group. */
178+
.caniuse-browsers {
179+
border-bottom: none;
180+
}
181+
182+
.caniuse-type {
183+
margin-top: 0;
184+
}
185+
121186
.caniuse-cell.y::before {
122187
content: "✔️";
123188
padding: 0.5em;

tests/spec/core/caniuse-spec.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ describe("Core — Can I Use", () => {
8383
const doc = await makeRSDoc(ops);
8484
const stats = doc.querySelector(".caniuse-stats");
8585
const cells = stats.querySelectorAll(".caniuse-cell");
86-
expect(cells).toHaveSize(4);
86+
expect(cells).toHaveSize(3);
8787

8888
// Check a cell
8989
const [cell] = cells;
@@ -123,9 +123,13 @@ describe("Core — Can I Use", () => {
123123
);
124124

125125
// More info link
126-
const moreInfoLink = cells.item(3);
126+
const moreInfoLink = stats.querySelector("a[href*='caniuse.com']");
127127
expect(moreInfoLink.href).toBe("https://caniuse.com/FEATURE");
128128
expect(moreInfoLink.textContent.trim()).toBe("More info");
129+
// It must not carry .caniuse-cell: that sets color:#fff and a gradient from
130+
// --caniuse-bg, which is only defined on the support-level classes, so the
131+
// link rendered white on white.
132+
expect(moreInfoLink.classList.contains("caniuse-cell")).toBeFalse();
129133
});
130134

131135
it("removes irrelevant config for caniuse feature", async () => {
@@ -231,4 +235,26 @@ describe("Core — Can I Use", () => {
231235
"mobile"
232236
);
233237
});
238+
239+
it("marks up the groups as a dl, with the More info link outside it", async () => {
240+
const ops = makeStandardOps({
241+
caniuse: {
242+
feature: "FEATURE",
243+
apiURL,
244+
},
245+
});
246+
const doc = await makeRSDoc(ops);
247+
const stats = doc.querySelector(".caniuse-stats");
248+
const list = stats.querySelector("dl.caniuse-groups");
249+
// A dl only allows dt/dd, optionally wrapped in a div.
250+
expect(list.querySelectorAll(":scope > div.caniuse-group")).toHaveSize(2);
251+
expect(
252+
list.querySelectorAll(":scope > div > dt.caniuse-type:first-child")
253+
).toHaveSize(2);
254+
expect(
255+
list.querySelectorAll(":scope > div > dd.caniuse-browsers:last-child")
256+
).toHaveSize(2);
257+
// So the link is a sibling of the list, not inside it.
258+
expect(stats.querySelector("a[href]").parentElement).toBe(stats);
259+
});
234260
});

0 commit comments

Comments
 (0)