Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0e9de9a
fix(compat-table): render parallel support branches separately
caugner May 19, 2026
d776bd1
fix(compat-table): show branch modifier icons on the heading
caugner May 19, 2026
0f48ad6
fix(compat-table): rephrase branch headings as `Prefix: …` / `Alterna…
caugner May 19, 2026
d37662d
fix(compat-table): treat prefixed items as fully supported inside a b…
caugner May 19, 2026
3483b2a
fix(compat-table): show branch heading whenever a branch has a modifier
caugner May 19, 2026
91df518
fix(compat-table): render branch boxes as cells aligned to browser co…
caugner May 19, 2026
1ca8bf8
fix(compat-table): adapt branch boxes to the single-column mobile layout
caugner May 19, 2026
b520cb3
refactor(compat-table): prepend canonical branch directly in `groupSu…
caugner May 21, 2026
b495626
refactor(compat-table): type `groupSupportBranches` as non-empty bran…
caugner May 21, 2026
0554dcf
refactor(compat-table): thread `ignoreModifiers` through `isFullySupp…
caugner May 21, 2026
63503e8
docs(compat-table): clarify why mobile `.timeline` margin is symmetric
caugner May 21, 2026
12e6dfe
refactor(compat-table): rename `omit/ignoreModifiers` to `omit/ignore…
caugner May 21, 2026
508ef6a
refactor(compat-table): drop unreachable fallback in `_renderBranchHe…
caugner May 21, 2026
eb7754e
refactor(compat-table): drop redundant filters in `_renderNotes`
caugner May 21, 2026
a94a1e4
refactor(compat-table): use `/` as branch-key separator in `groupSupp…
caugner May 21, 2026
d18ee02
docs(compat-table): clarify branch-heading rendering comment
caugner May 21, 2026
42f97f5
refactor(compat-table): nest `.bc-branch-heading` under `.bc-notes-list`
caugner May 21, 2026
8fd02f6
refactor(compat-table): remove unreachable prefix/altname notes
caugner May 21, 2026
f900cbf
Merge branch 'main' into mdn-831-compat-parallel-branches
caugner May 25, 2026
51d869a
Merge branch 'main' into mdn-831-compat-parallel-branches
caugner May 26, 2026
09bfcb4
refactor(compat-table): swap timeline `dl`/`dt`/`dd` for neutral wrap…
caugner May 26, 2026
59e9798
refactor(compat-table): track branch timeline cap with one variable
caugner May 26, 2026
c39a03a
refactor(compat-table): sort non-canonical branches deterministically
caugner May 26, 2026
0cb7ceb
refactor(compat-table): collapse `_renderBranchHeading` to one l10n call
caugner May 26, 2026
dd0d2ef
docs(compat-table): explain the `i === 0 || hasNotes` branch row guard
caugner May 26, 2026
7cbe9f4
Merge branch 'main' into mdn-831-compat-parallel-branches
LeoMcA Jun 19, 2026
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
144 changes: 101 additions & 43 deletions components/compat-table/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
bugURLToString,
getCurrentSupport,
getFirst,
groupSupportBranches,
hasMore,
hasNoteworthyNotes,
isFullySupportedWithoutLimitation,
Expand Down Expand Up @@ -450,7 +451,7 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
${hasHistory
? html`<div id=${timelineId} class="timeline" tabindex="0">
${isExpanded
? html`<dl class="bc-notes-list">${notes}</dl>`
? html`<div class="bc-notes-list">${notes}</div>`
: nothing}
</div>`
: nothing}
Expand All @@ -476,17 +477,22 @@ export class MDNCompatTable extends L10nMixin(LitElement) {

/**
* @param {import("@bcd").SupportStatement} support
* @param {{ omitAliasModifiers?: boolean }} [options] - When `omitAliasModifiers` is
* true, the `prefix` and `altname` icons are skipped (because a branch
* heading already conveys the alias modifier).
*/
_renderCellIcons(support) {
_renderCellIcons(support, { omitAliasModifiers = false } = {}) {
const supportItem = getCurrentSupport(support);
if (!supportItem) {
return;
}

const icons = [
supportItem.prefix && this._renderIcon("prefix"),
!omitAliasModifiers && supportItem.prefix && this._renderIcon("prefix"),
hasNoteworthyNotes(supportItem) && this._renderIcon("footnote"),
supportItem.alternative_name && this._renderIcon("altname"),
!omitAliasModifiers &&
supportItem.alternative_name &&
this._renderIcon("altname"),
supportItem.flags && this._renderIcon("disabled"),
hasMore(support) && this._renderIcon("more"),
].filter(Boolean);
Expand Down Expand Up @@ -589,46 +595,106 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
* @param {import("@bcd").SupportStatement} support
*/
_renderNotes(browser, support) {
return [...asList(support)]
.reverse()
.flatMap((item, i) => {
const notes = this._getNotes(browser, support, item);
// Support arrays interleave parallel branches (e.g. unprefixed vs.
// `-webkit-` vs. `-moz-`). Render each branch as its own timeline so
// versions stay in chronological order within the branch. Any branch
// with a `prefix` or `alternative_name` gets a heading conveying the
// alias modifier, even when there is no canonical branch to contrast
// with.
const branches = groupSupportBranches(support);

return branches.map((branchItems) => {
const { prefix, alternative_name } = branchItems[0];
const hasAliasModifier = !!(prefix || alternative_name);
const heading = hasAliasModifier
? this._renderBranchHeading(prefix, alternative_name)
: nothing;

const wrappers = [...branchItems].reverse().flatMap((item, i) => {
// Suppress prefix/alt-name notes when the branch heading already
// conveys the alias modifier — avoids redundancy.
const notes = this._getNotes(browser, support, item, {
omitAliasModifiers: hasAliasModifier,
});

const notesItems = notes.map(({ iconName, label }) => {
return html`<dd class="bc-supports-dd">
return html`<div class="bc-supports-dd">
${this._renderIcon(iconName)}${typeof label === "string"
? html`<span>${unsafeHTML(label)}</span>`
: label}
</dd>`;
</div>`;
});

const hasNotes = notesItems.length > 0;

// Always render the first (most recent) item even when it has no
// notes, so each branch shows at least one row in its timeline.
return (
(i === 0 || hasNotes) &&
html`<div class="bc-notes-wrapper">
<dt
<div
class=${`bc-supports-${getSupportClassName(
item,
browser,
)} bc-supports`}
>
${this._renderCellText(item, browser, true)}
</dt>
${notesItems} ${hasNotes ? undefined : html`<dd></dd>`}
${this._renderCellText(item, browser, true, {
omitAliasModifiers: hasAliasModifier,
})}
</div>
${notesItems}
${hasNotes ? undefined : html`<div class="bc-notes-end"></div>`}
</div>`
);
})
.filter(Boolean);
});

return html`<div class="bc-branch">
${heading}
<div class="bc-branch-items">${wrappers}</div>
</div>`;
});
}

/**
* Called only when at least one of `prefix` / `alternativeName` is present.
* @param {string | undefined} prefix
* @param {string | undefined} alternativeName
*/
_renderBranchHeading(prefix, alternativeName) {
const id =
prefix && alternativeName
? "compat-branch-prefix-altname"
: prefix
? "compat-branch-prefix"
: "compat-branch-altname";
const label = this.l10n.raw({
id,
args: { prefix, altname: alternativeName },
elements: {
prefix: { tag: "code" },
altname: { tag: "code" },
},
});
const icons = [
prefix && this._renderIcon("prefix"),
alternativeName && this._renderIcon("altname"),
].filter(Boolean);
return html`<div class="bc-branch-heading">
<div class="bc-icons">${icons}</div>
<span>${label}</span>
</div>`;
}

/**
* @param {import("@bcd").BrowserStatement} browser
* @param {import("@bcd").SupportStatement} support
* @param {import("@bcd").SimpleSupportStatement} item
* @param {{ omitAliasModifiers?: boolean }} [options] - When `omitAliasModifiers` is
* true, prefix / alternative_name don't count as limitations when judging
* full support (because a branch heading already conveys the modifier).
* @returns
*/
_getNotes(browser, support, item) {
_getNotes(browser, support, item, { omitAliasModifiers = false } = {}) {
/**
* @type {Array<{iconName: import("@compat").IconName; label: string | import("@lit").L10nResult | undefined }>}
*/
Expand Down Expand Up @@ -658,29 +724,8 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
});
}

if (item.prefix) {
supportNotes.push({
iconName: "prefix",
label: this.l10n.raw({
id: "compat-support-prefix",
args: {
prefix: item.prefix,
},
}),
});
}

if (item.alternative_name) {
supportNotes.push({
iconName: "altname",
label: this.l10n.raw({
id: "compat-support-altname",
args: {
altname: item.alternative_name,
},
}),
});
}
// Note: prefix / alternative_name modifiers are conveyed by the branch
// heading (see `_renderBranchHeading`), so they aren't pushed here.

if (item.flags) {
for (const { type, name, value_to_set } of item.flags) {
Expand Down Expand Up @@ -763,8 +808,13 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
// If we encounter nothing else than the required `version_added` and
// `release_date` properties, assume full support.
// EDIT 1-5-21: if item.version_added doesn't exist, assume no support.
// When the branch heading already conveys the alias modifier, ignore prefix
// and alternative_name when judging full support — otherwise a plain
// `{ prefix, version_added }` item falls through to "Support unknown".
if (
isFullySupportedWithoutLimitation(item) &&
isFullySupportedWithoutLimitation(item, {
ignoreAliasModifiers: omitAliasModifiers,
}) &&
!versionIsPreview(item.version_added, browser)
) {
supportNotes.push({
Expand Down Expand Up @@ -793,8 +843,16 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
* @param {import("@bcd").SupportStatement | undefined} support
* @param {import("@bcd").BrowserStatement} browser
* @param {boolean} [timeline]
* @param {{ omitAliasModifiers?: boolean }} [options] - Forwarded to
* {@link _renderCellIcons} to suppress prefix/altname icons when a branch
* heading already conveys the alias modifier.
*/
_renderCellText(support, browser, timeline = false) {
_renderCellText(
support,
browser,
timeline = false,
{ omitAliasModifiers = false } = {},
) {
const currentSupport = getCurrentSupport(support);

const added = currentSupport?.version_added ?? undefined;
Expand Down Expand Up @@ -914,7 +972,7 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
: ""}
</span>
</div>
${support && this._renderCellIcons(support)}
${support && this._renderCellIcons(support, { omitAliasModifiers })}
</div>`;
}

Expand Down
Loading