@mdn/fred 2.4.3 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -1
- package/CHANGELOG.md +48 -0
- package/README.md +2 -0
- package/components/a11y-menu/server.js +2 -2
- package/components/baseline-indicator/server.js +86 -32
- package/components/breadcrumbs/server.js +6 -1
- package/components/collection-save-button/element.js +15 -0
- package/components/color-theme/element.js +17 -1
- package/components/compat-section/server.js +45 -0
- package/components/compat-table/element.js +10 -1
- package/components/compat-table/utils.js +10 -0
- package/components/compat-table-lazy/element.js +2 -5
- package/components/content-section/server.css +5 -0
- package/components/content-section/server.js +4 -22
- package/components/curriculum/sidebar.css +1 -1
- package/components/curriculum-landing/server.js +4 -1
- package/components/dropdown/element.js +12 -1
- package/components/env/index.js +1 -1
- package/components/featured-articles/server.js +10 -3
- package/components/footer/server.js +6 -1
- package/components/generic-about/server.js +1 -0
- package/components/generic-toc/server.js +6 -1
- package/components/language-always-redirect-button/element.js +2 -0
- package/components/language-switcher/element.js +5 -0
- package/components/latest-news/server.js +10 -3
- package/components/left-sidebar/server.css +1 -1
- package/components/login-button/element.js +1 -1
- package/components/menu/server.js +3 -1
- package/components/observatory-form/element.js +3 -0
- package/components/observatory-results/element.js +6 -1
- package/components/observatory-results/rating.js +6 -1
- package/components/outer-layout/server.js +3 -3
- package/components/placement-top/element.js +16 -4
- package/components/playground/element.js +17 -4
- package/components/recent-contributions/server.js +4 -1
- package/components/reference-toc/server.js +5 -1
- package/components/scrim-inline/element.js +13 -1
- package/components/server/index.js +4 -0
- package/components/sidebar-filter/element.js +11 -0
- package/components/sidebar-filter/sidebar-filterer.js +4 -2
- package/components/specifications-list/server.js +72 -0
- package/components/translation-banner/server.js +4 -1
- package/components/user-menu/element.js +1 -0
- package/components/viewed-controller/viewed-controller.js +15 -59
- package/components/viewed-controller/viewed-observer.js +79 -0
- package/hooks/glean-init.js +49 -6
- package/l10n/locales/zh-CN.ftl +317 -1
- package/out/service-worker.js +1 -1
- package/out/static/client/{2173.c74593faf7e14cbc.js → 2173.a662dd40657c8426.js} +2 -2
- package/out/static/client/{2173.c74593faf7e14cbc.js.map → 2173.a662dd40657c8426.js.map} +1 -1
- package/out/static/client/{251.db6a4b10ef58ad3a.js → 251.f498960ce719d884.js} +6 -6
- package/out/static/client/251.f498960ce719d884.js.map +1 -0
- package/out/static/client/{2868.b09544148fff25a1.js → 2868.03299f12d5927ed2.js} +2 -2
- package/out/static/client/{2868.b09544148fff25a1.js.map → 2868.03299f12d5927ed2.js.map} +1 -1
- package/out/static/client/{291.91650ffed3a60424.js → 291.c90e43b3c10a9a1f.js} +27 -16
- package/out/static/client/291.c90e43b3c10a9a1f.js.map +1 -0
- package/out/static/client/{3259.ac8a5539251c647a.js → 3259.c4b809cd884ad515.js} +29 -24
- package/out/static/client/3259.c4b809cd884ad515.js.map +1 -0
- package/out/static/client/{3775.c64e09b2fc1d9b21.js → 3775.cf93e968f166d25a.js} +2 -1
- package/out/static/client/{3775.c64e09b2fc1d9b21.js.map → 3775.cf93e968f166d25a.js.map} +1 -1
- package/out/static/client/{4060.9a7bb116e3112c13.js → 4060.a23acd149888555c.js} +2 -2
- package/out/static/client/{4060.9a7bb116e3112c13.js.map → 4060.a23acd149888555c.js.map} +1 -1
- package/out/static/client/4249.19f86ee6b57a13b0.js +2 -0
- package/out/static/client/4249.19f86ee6b57a13b0.js.map +1 -0
- package/out/static/client/{4759.8981c79d33088d8f.js → 4759.5852733a45132491.js} +2 -2
- package/out/static/client/{4759.8981c79d33088d8f.js.map → 4759.5852733a45132491.js.map} +1 -1
- package/out/static/client/{4920.033c028023478df5.js → 4920.929203da4613c89a.js} +3 -2
- package/out/static/client/4920.929203da4613c89a.js.map +1 -0
- package/out/static/client/{5183.6c532a6e7b0d0bbb.js → 5183.48a6059ffe2931f0.js} +12 -12
- package/out/static/client/5183.48a6059ffe2931f0.js.map +1 -0
- package/out/static/client/{5408.a0622390fa1726dc.css → 5408.77c9b71d6a5df982.css} +2 -2
- package/out/static/client/{5408.a0622390fa1726dc.css.map → 5408.77c9b71d6a5df982.css.map} +1 -1
- package/out/static/client/{544.1ed814181b24bcc8.js → 544.2ccdc928c5292631.js} +3 -3
- package/out/static/client/544.2ccdc928c5292631.js.map +1 -0
- package/out/static/client/{5667.332b2c6c10792ad6.js → 5667.edd77c81ec38a7e2.js} +5 -4
- package/out/static/client/5667.edd77c81ec38a7e2.js.map +1 -0
- package/out/static/client/5685.d30658d4c274ac13.js +4 -0
- package/out/static/client/5685.d30658d4c274ac13.js.map +1 -0
- package/out/static/client/6002.bb83fdc66501b6e4.js +167 -0
- package/out/static/client/6002.bb83fdc66501b6e4.js.map +1 -0
- package/out/static/client/{6067.a60fb66b75579c98.js → 6067.169a9ec1f47871ae.js} +3 -3
- package/out/static/client/6067.169a9ec1f47871ae.js.map +1 -0
- package/out/static/client/6216.2a29b5ce2a1bf6f6.js +2 -0
- package/out/static/client/6216.2a29b5ce2a1bf6f6.js.map +1 -0
- package/out/static/client/{8364.13d1d6770264c970.js → 8364.f7862244a6ff6a1b.js} +3 -3
- package/out/static/client/8364.f7862244a6ff6a1b.js.map +1 -0
- package/out/static/client/{9204.f8aa5d9f8a79f3d4.js → 9204.f45896951abfbd28.js} +3 -3
- package/out/static/client/{9204.f8aa5d9f8a79f3d4.js.map → 9204.f45896951abfbd28.js.map} +1 -1
- package/out/static/client/9324.9646c5839948c8ff.js +27 -0
- package/out/static/client/9324.9646c5839948c8ff.js.map +1 -0
- package/out/static/client/9456.a17794342ff9086a.js +319 -0
- package/out/static/client/9456.a17794342ff9086a.js.map +1 -0
- package/out/static/client/index.c41c45d10cf2b50b.js +221 -0
- package/out/static/client/index.c41c45d10cf2b50b.js.map +1 -0
- package/out/static/client/{runtime.8ebca7827bf4c35c.js → runtime.796d2e8ab20fb93f.js} +2 -2
- package/out/static/client/runtime.796d2e8ab20fb93f.js.map +1 -0
- package/out/static/client/stats.json +142 -142
- package/out/static/client/{styles-content-section.049c2352791fac9b.css → styles-content-section.cd5635d274b730e9.css} +2 -2
- package/out/static/client/{styles-content-section.049c2352791fac9b.css.map → styles-content-section.cd5635d274b730e9.css.map} +1 -1
- package/out/static/client/{styles-global.445b59e1ff23f88c.js → styles-global.2a17ab4cc47bca1f.js} +1 -1
- package/out/static/client/{styles-global.3da376739551015c.css → styles-global.79b14873ea7ab386.css} +2 -2
- package/out/static/client/{styles-global.3da376739551015c.css.map → styles-global.79b14873ea7ab386.css.map} +1 -1
- package/out/static/client/{styles-left-sidebar.6f954e8939555f77.css → styles-left-sidebar.517b9efefbdeed1d.css} +2 -2
- package/out/static/client/{styles-left-sidebar.6f954e8939555f77.css.map → styles-left-sidebar.517b9efefbdeed1d.css.map} +1 -1
- package/out/static/legacy/6753.072394f4f09652a0.js +27 -0
- package/out/static/legacy/6753.072394f4f09652a0.js.map +1 -0
- package/out/static/legacy/asset-manifest.json +5 -5
- package/out/static/legacy/{index.85161ba7fe682ee8.html → index.c05c933009dda5b3.html} +1 -1
- package/out/static/legacy/stats.json +9 -9
- package/out/static/legacy/{yari.01d97d56e30935ec.js → yari.e457418f21984506.js} +3 -3
- package/out/static/legacy/{yari.01d97d56e30935ec.js.map → yari.e457418f21984506.js.map} +1 -1
- package/out/static/ssr/9456.js +317 -1
- package/out/static/ssr/9456.js.map +1 -1
- package/out/static/ssr/index.js +315 -235
- package/out/static/ssr/index.js.map +1 -1
- package/out/static/ssr/stats.json +4 -4
- package/package.json +14 -8
- package/components/specifications-list/index.js +0 -60
- package/out/static/client/251.db6a4b10ef58ad3a.js.map +0 -1
- package/out/static/client/291.91650ffed3a60424.js.map +0 -1
- package/out/static/client/3259.ac8a5539251c647a.js.map +0 -1
- package/out/static/client/4920.033c028023478df5.js.map +0 -1
- package/out/static/client/5183.6c532a6e7b0d0bbb.js.map +0 -1
- package/out/static/client/544.1ed814181b24bcc8.js.map +0 -1
- package/out/static/client/5649.00f25ce181894101.js +0 -2
- package/out/static/client/5649.00f25ce181894101.js.map +0 -1
- package/out/static/client/5667.332b2c6c10792ad6.js.map +0 -1
- package/out/static/client/5685.46c8d178d072b8ac.js +0 -4
- package/out/static/client/5685.46c8d178d072b8ac.js.map +0 -1
- package/out/static/client/6067.a60fb66b75579c98.js.map +0 -1
- package/out/static/client/6216.f526aaf8b3dedd72.js +0 -2
- package/out/static/client/6216.f526aaf8b3dedd72.js.map +0 -1
- package/out/static/client/718.3a790c4c45336906.js +0 -167
- package/out/static/client/718.3a790c4c45336906.js.map +0 -1
- package/out/static/client/8364.13d1d6770264c970.js.map +0 -1
- package/out/static/client/9324.9bb064ea7377127e.js +0 -26
- package/out/static/client/9324.9bb064ea7377127e.js.map +0 -1
- package/out/static/client/9456.dd8c12ddf52e2a7d.js +0 -3
- package/out/static/client/9456.dd8c12ddf52e2a7d.js.map +0 -1
- package/out/static/client/index.b2ccf995e945e9dc.js +0 -221
- package/out/static/client/index.b2ccf995e945e9dc.js.map +0 -1
- package/out/static/client/runtime.8ebca7827bf4c35c.js.map +0 -1
- package/out/static/legacy/6753.811fcf40c897d487.js +0 -27
- package/out/static/legacy/6753.811fcf40c897d487.js.map +0 -1
- /package/out/static/client/{291.91650ffed3a60424.js.LICENSE.txt → 291.c90e43b3c10a9a1f.js.LICENSE.txt} +0 -0
- /package/out/static/client/{5183.6c532a6e7b0d0bbb.js.LICENSE.txt → 5183.48a6059ffe2931f0.js.LICENSE.txt} +0 -0
- /package/out/static/client/{6067.a60fb66b75579c98.js.LICENSE.txt → 6067.169a9ec1f47871ae.js.LICENSE.txt} +0 -0
- /package/out/static/client/{8364.13d1d6770264c970.js.LICENSE.txt → 8364.f7862244a6ff6a1b.js.LICENSE.txt} +0 -0
- /package/out/static/client/{index.b2ccf995e945e9dc.js.LICENSE.txt → index.c41c45d10cf2b50b.js.LICENSE.txt} +0 -0
- /package/out/static/legacy/{6753.811fcf40c897d487.js.LICENSE.txt → 6753.072394f4f09652a0.js.LICENSE.txt} +0 -0
- /package/out/static/legacy/{yari.01d97d56e30935ec.js.LICENSE.txt → yari.e457418f21984506.js.LICENSE.txt} +0 -0
package/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v24
|
|
1
|
+
v24.15.0
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.6.0](https://github.com/mdn/fred/compare/v2.5.0...v2.6.0) (2026-04-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **glean:** port missing measurements ([#1279](https://github.com/mdn/fred/issues/1279)) ([bdbc1a1](https://github.com/mdn/fred/commit/bdbc1a166cbd4fd2dd01ce898b0f5ff7837599d9))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **content-section:** add iframe styling ([#1414](https://github.com/mdn/fred/issues/1414)) ([2696dbc](https://github.com/mdn/fred/commit/2696dbc9ad769855ef9c3356a54ef06ebdcd132c))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Miscellaneous
|
|
17
|
+
|
|
18
|
+
* **deps-dev:** bump @mdn/browser-compat-data from 7.3.12 to 7.3.13 ([#1517](https://github.com/mdn/fred/issues/1517)) ([6dbfba4](https://github.com/mdn/fred/commit/6dbfba4c396c0490f753c7a0cbc8e29f267adacb))
|
|
19
|
+
* **deps-dev:** bump postcss from 8.5.10 to 8.5.12 in the npm-dev group ([#1516](https://github.com/mdn/fred/issues/1516)) ([b605a17](https://github.com/mdn/fred/commit/b605a170bb1c003c728ecee9695197add9e07957))
|
|
20
|
+
* **deps-dev:** bump the npm-dev group with 2 updates ([#1519](https://github.com/mdn/fred/issues/1519)) ([e1f4d25](https://github.com/mdn/fred/commit/e1f4d258df237b192d391e337b72e5026ed022d0))
|
|
21
|
+
* **deps:** bump @mdn/rari from 0.2.20 to 0.2.21 ([#1525](https://github.com/mdn/fred/issues/1525)) ([63a0803](https://github.com/mdn/fred/commit/63a08039823627b6816469f7798ed54941b33a30))
|
|
22
|
+
* **I10n:** add Simplified Chinese translation ([#1510](https://github.com/mdn/fred/issues/1510)) ([ae1bf08](https://github.com/mdn/fred/commit/ae1bf083d88e3579d99e3587b687245ce26a8107))
|
|
23
|
+
|
|
24
|
+
## [2.5.0](https://github.com/mdn/fred/compare/v2.4.3...v2.5.0) (2026-04-23)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* **baseline-indicator:** add renderSimplified method ([#1500](https://github.com/mdn/fred/issues/1500)) ([556412d](https://github.com/mdn/fred/commit/556412d6ac6b5398e773eedd89fa4ae710ad6f43))
|
|
30
|
+
* **bcd:** render links to JSON in simplified HTML ([#1502](https://github.com/mdn/fred/issues/1502)) ([d82fd33](https://github.com/mdn/fred/commit/d82fd33921bea39e23c2b1aae3123848ddae0b5c))
|
|
31
|
+
* **specifications:** render a list in simplified mode ([#1504](https://github.com/mdn/fred/issues/1504)) ([abf1784](https://github.com/mdn/fred/commit/abf17843f6dc0f6a18f6bff6f523255ed74e37b2))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
* **sidebar-filter:** remove only highlight spans on reset ([#1496](https://github.com/mdn/fred/issues/1496)) ([c647e79](https://github.com/mdn/fred/commit/c647e794552c08f1574545378ba5986699633e7f))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Miscellaneous
|
|
40
|
+
|
|
41
|
+
* **deps-dev:** bump @codemirror/view from 6.41.0 to 6.41.1 in the npm-dev group ([#1494](https://github.com/mdn/fred/issues/1494)) ([06be168](https://github.com/mdn/fred/commit/06be168411bf3efe7a38bc6a291e9a91b1b6c309))
|
|
42
|
+
* **deps-dev:** bump @codemirror/view in the npm-dev group ([06be168](https://github.com/mdn/fred/commit/06be168411bf3efe7a38bc6a291e9a91b1b6c309))
|
|
43
|
+
* **deps-dev:** bump npm from 11.6.2 to 11.12.1 ([#1490](https://github.com/mdn/fred/issues/1490)) ([fbc5e9a](https://github.com/mdn/fred/commit/fbc5e9a78d5332ade4f99d931011fc681c653f77))
|
|
44
|
+
* **deps-dev:** bump stylelint from 17.8.0 to 17.9.0 in the npm-dev group ([#1514](https://github.com/mdn/fred/issues/1514)) ([87617be](https://github.com/mdn/fred/commit/87617be8e0f3c712385ccce424edf07756b9bf8c))
|
|
45
|
+
* **deps-dev:** bump stylelint in the npm-dev group ([87617be](https://github.com/mdn/fred/commit/87617be8e0f3c712385ccce424edf07756b9bf8c))
|
|
46
|
+
* **deps-dev:** bump typescript-eslint from 8.58.2 to 8.59.0 in the npm-dev group ([#1499](https://github.com/mdn/fred/issues/1499)) ([b25912c](https://github.com/mdn/fred/commit/b25912ceb942de26ac5df8153317b8de172a4e2c))
|
|
47
|
+
* **deps-dev:** bump typescript-eslint in the npm-dev group ([b25912c](https://github.com/mdn/fred/commit/b25912ceb942de26ac5df8153317b8de172a4e2c))
|
|
48
|
+
* **deps:** bump @mdn/rari from 0.2.19 to 0.2.20 ([#1515](https://github.com/mdn/fred/issues/1515)) ([d292b8a](https://github.com/mdn/fred/commit/d292b8aedf464a17ab7f5a3224fbeaef330ade2e))
|
|
49
|
+
* **transcend:** use TRANSCEND_AIRGAP_URL ([#1497](https://github.com/mdn/fred/issues/1497)) ([5e8a62a](https://github.com/mdn/fred/commit/5e8a62a1437718631ca1ee010e53cda377126a1b))
|
|
50
|
+
|
|
3
51
|
## [2.4.3](https://github.com/mdn/fred/compare/v2.4.2...v2.4.3) (2026-04-17)
|
|
4
52
|
|
|
5
53
|
|
package/README.md
CHANGED
|
@@ -172,6 +172,8 @@ This issue is tracked upstream: https://github.com/lit/lit/issues/1434
|
|
|
172
172
|
|
|
173
173
|
Any server component can define a `renderSimplified` method to define the simplified form of that component. When in the top-level `renderSimplified` context, any calls of `ServerComponent.render()` will automatically call the `renderSimplified` method of that component, falling back to the `render` method. This is so we can nest components with a `renderSimplified` method ("simplified components") within ones without.
|
|
174
174
|
|
|
175
|
+
Server components also have a `simplifiedMode` property set, which is `true` when rendered from the top-level `renderSimplified` function: this allows a small tweak within a `render` method without having to totally re-implement logic in the `renderSimplified` method.
|
|
176
|
+
|
|
175
177
|
There shouldn't be standalone simplified components: the nesting of components should be defined by the requirements of the `render` method. `renderSimplified` should only be added to a component which already exists with a `render` method to give a simplified view of it. This is especially important as, in the future, we may need to add options of what is/isn't rendered within `renderSimplified` for use in different contexts (one context may require a sidebar, another may not, for instance).
|
|
176
178
|
|
|
177
179
|
You can preview the rendering locally by setting `FRED_SIMPLE_HTML`:
|
|
@@ -9,14 +9,14 @@ export class A11yMenu extends ServerComponent {
|
|
|
9
9
|
render(context) {
|
|
10
10
|
return html`<ul class="a11y-menu">
|
|
11
11
|
<li>
|
|
12
|
-
<a href="#content"
|
|
12
|
+
<a href="#content" data-glean-id="a11y_menu: click #content"
|
|
13
13
|
>${context.l10n(
|
|
14
14
|
"a11y-menu-skip-to-main-content",
|
|
15
15
|
)`Skip to main content`}</a
|
|
16
16
|
>
|
|
17
17
|
</li>
|
|
18
18
|
<li>
|
|
19
|
-
<a href="#search"
|
|
19
|
+
<a href="#search" data-glean-id="a11y_menu: click #search"
|
|
20
20
|
>${context.l10n("a11y-menu-skip-to-search")`Skip to search`}</a
|
|
21
21
|
>
|
|
22
22
|
</li>
|
|
@@ -51,7 +51,43 @@ export class BaselineIndicator extends ServerComponent {
|
|
|
51
51
|
static inlineScript = inlineScript;
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
54
|
+
* @param {string | null | undefined} date
|
|
55
|
+
*/
|
|
56
|
+
parseDate(date) {
|
|
57
|
+
const lowDateRange = date?.match(/^([^0-9])/)?.[0];
|
|
58
|
+
return date ? new Date(date.slice(lowDateRange ? 1 : 0)) : undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {import("@fred").Context<import("@rari").DocPage>} context
|
|
63
|
+
* @param {string} level
|
|
64
|
+
* @param {Date} [lowDate]
|
|
65
|
+
*/
|
|
66
|
+
getExtraText(context, level, lowDate) {
|
|
67
|
+
return level === "high" && lowDate
|
|
68
|
+
? context.l10n.raw({
|
|
69
|
+
id: "baseline-high-extra",
|
|
70
|
+
args: {
|
|
71
|
+
date: lowDate.toLocaleDateString(context.locale, {
|
|
72
|
+
year: "numeric",
|
|
73
|
+
month: "long",
|
|
74
|
+
}),
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
: level === "low" && lowDate
|
|
78
|
+
? context.l10n.raw({
|
|
79
|
+
id: "baseline-low-extra",
|
|
80
|
+
args: {
|
|
81
|
+
date: lowDate.toLocaleDateString(DEFAULT_LOCALE, {
|
|
82
|
+
year: "numeric",
|
|
83
|
+
month: "long",
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
: context.l10n("baseline-not-extra");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
55
91
|
* @param {import("@fred").Context<import("@rari").DocPage>} context
|
|
56
92
|
*/
|
|
57
93
|
render(context) {
|
|
@@ -71,11 +107,7 @@ export class BaselineIndicator extends ServerComponent {
|
|
|
71
107
|
LOCALIZED_BCD_IDS[context.locale] || LOCALIZED_BCD_IDS[DEFAULT_LOCALE]
|
|
72
108
|
}`;
|
|
73
109
|
|
|
74
|
-
const
|
|
75
|
-
const low_date = status.baseline_low_date
|
|
76
|
-
? new Date(status.baseline_low_date.slice(low_date_range ? 1 : 0))
|
|
77
|
-
: undefined;
|
|
78
|
-
|
|
110
|
+
const lowDate = this.parseDate(status.baseline_low_date);
|
|
79
111
|
const level = status.baseline || "not";
|
|
80
112
|
|
|
81
113
|
const feedbackLink = `${SURVEY_URL}?page=${encodeURIComponent(context.url)}&level=${level}`;
|
|
@@ -154,7 +186,7 @@ export class BaselineIndicator extends ServerComponent {
|
|
|
154
186
|
? context.l10n(
|
|
155
187
|
"baseline-indicator-widely-available",
|
|
156
188
|
)`Widely available`
|
|
157
|
-
:
|
|
189
|
+
: lowDate?.getFullYear()}
|
|
158
190
|
</span>
|
|
159
191
|
${status.asterisk && " *"}
|
|
160
192
|
`}
|
|
@@ -191,31 +223,7 @@ export class BaselineIndicator extends ServerComponent {
|
|
|
191
223
|
<span class="icon icon-chevron"></span>
|
|
192
224
|
</summary>
|
|
193
225
|
<div class="extra">
|
|
194
|
-
|
|
195
|
-
? html`<p>
|
|
196
|
-
${context.l10n.raw({
|
|
197
|
-
id: "baseline-high-extra",
|
|
198
|
-
args: {
|
|
199
|
-
date: low_date.toLocaleDateString(context.locale, {
|
|
200
|
-
year: "numeric",
|
|
201
|
-
month: "long",
|
|
202
|
-
}),
|
|
203
|
-
},
|
|
204
|
-
})}
|
|
205
|
-
</p>`
|
|
206
|
-
: level === "low" && low_date
|
|
207
|
-
? html`<p>
|
|
208
|
-
${context.l10n.raw({
|
|
209
|
-
id: "baseline-low-extra",
|
|
210
|
-
args: {
|
|
211
|
-
date: low_date.toLocaleDateString(DEFAULT_LOCALE, {
|
|
212
|
-
year: "numeric",
|
|
213
|
-
month: "long",
|
|
214
|
-
}),
|
|
215
|
-
},
|
|
216
|
-
})}
|
|
217
|
-
</p>`
|
|
218
|
-
: html`<p>${context.l10n("baseline-not-extra")}</p>`}
|
|
226
|
+
<p>${this.getExtraText(context, level, lowDate)}</p>
|
|
219
227
|
${status.asterisk
|
|
220
228
|
? html`<p>* ${context.l10n("baseline-asterisk")}</p>`
|
|
221
229
|
: nothing}
|
|
@@ -254,4 +262,50 @@ export class BaselineIndicator extends ServerComponent {
|
|
|
254
262
|
</div>
|
|
255
263
|
</details>`;
|
|
256
264
|
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @param {import("@fred").Context<import("@rari").DocPage>} context
|
|
268
|
+
*/
|
|
269
|
+
renderSimplified(context) {
|
|
270
|
+
const { doc } = context;
|
|
271
|
+
|
|
272
|
+
if (!doc) {
|
|
273
|
+
return nothing;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const status = doc.baseline;
|
|
277
|
+
|
|
278
|
+
if (!status) {
|
|
279
|
+
return nothing;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const lowDate = this.parseDate(status.baseline_low_date);
|
|
283
|
+
const level = status.baseline || "not";
|
|
284
|
+
|
|
285
|
+
return html`<p>
|
|
286
|
+
<strong>
|
|
287
|
+
${level === "not"
|
|
288
|
+
? context.l10n(
|
|
289
|
+
"baseline-indicator-limited-availability",
|
|
290
|
+
)`Limited availability`
|
|
291
|
+
: context.l10n("baseline-indicator-baseline")`Baseline`}
|
|
292
|
+
${level === "high"
|
|
293
|
+
? context.l10n(
|
|
294
|
+
"baseline-indicator-widely-available",
|
|
295
|
+
)`Widely available`
|
|
296
|
+
: level === "low"
|
|
297
|
+
? html`${lowDate?.getFullYear()}
|
|
298
|
+
${context.l10n(
|
|
299
|
+
"baseline-indicator-newly-available",
|
|
300
|
+
)`Newly available`}`
|
|
301
|
+
: nothing}
|
|
302
|
+
${status.asterisk ? " *" : nothing}
|
|
303
|
+
</strong>
|
|
304
|
+
<br />
|
|
305
|
+
${this.getExtraText(context, level, lowDate)}
|
|
306
|
+
${status.asterisk
|
|
307
|
+
? html`<br />* ${context.l10n("baseline-asterisk")}`
|
|
308
|
+
: nothing}
|
|
309
|
+
</p>`;
|
|
310
|
+
}
|
|
257
311
|
}
|
|
@@ -18,6 +18,7 @@ export class Breadcrumbs extends ServerComponent {
|
|
|
18
18
|
return nothing;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const total = parents.length;
|
|
21
22
|
return html`
|
|
22
23
|
<ol
|
|
23
24
|
class="breadcrumbs"
|
|
@@ -27,7 +28,11 @@ export class Breadcrumbs extends ServerComponent {
|
|
|
27
28
|
${parents.map(
|
|
28
29
|
({ uri, title }, index) => html`
|
|
29
30
|
<li property="itemListElement" typeof="ListItem">
|
|
30
|
-
<a
|
|
31
|
+
<a
|
|
32
|
+
href=${uri}
|
|
33
|
+
property="item"
|
|
34
|
+
typeof="WebPage"
|
|
35
|
+
data-glean-id="breadcrumb_click: ${total - index}/${total}"
|
|
31
36
|
><span property="name">${title}</span></a
|
|
32
37
|
>
|
|
33
38
|
<meta property="position" content=${index + 1} />
|
|
@@ -2,6 +2,7 @@ import { Task } from "@lit/task";
|
|
|
2
2
|
import { LitElement, html, nothing } from "lit";
|
|
3
3
|
|
|
4
4
|
import { L10nMixin } from "../../l10n/mixin.js";
|
|
5
|
+
import { gleanClick } from "../../utils/glean.js";
|
|
5
6
|
import { globalUser } from "../user/context.js";
|
|
6
7
|
|
|
7
8
|
import styles from "./element.css?lit";
|
|
@@ -89,16 +90,26 @@ export class MDNCollectionSaveButton extends L10nMixin(LitElement) {
|
|
|
89
90
|
});
|
|
90
91
|
|
|
91
92
|
_open() {
|
|
93
|
+
this._selectFocusEventTriggered = false;
|
|
94
|
+
gleanClick("article_actions_collections_opened");
|
|
92
95
|
this._bookmarks.run();
|
|
93
96
|
this._collections.run();
|
|
94
97
|
this.shadowRoot?.querySelector("mdn-modal")?.showModal();
|
|
95
98
|
}
|
|
96
99
|
|
|
100
|
+
_selectOpen() {
|
|
101
|
+
if (!this._selectFocusEventTriggered) {
|
|
102
|
+
gleanClick("article_actions_collection_select_opened");
|
|
103
|
+
this._selectFocusEventTriggered = true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
97
107
|
/** @param {Event} event */
|
|
98
108
|
_selectChange({ target }) {
|
|
99
109
|
if (target instanceof HTMLSelectElement) {
|
|
100
110
|
const { value } = target;
|
|
101
111
|
if (value === ADD_VALUE) {
|
|
112
|
+
gleanClick("article_actions_new_collection");
|
|
102
113
|
this.shadowRoot?.querySelector("mdn-modal")?.close();
|
|
103
114
|
open("/en-US/plus/collections");
|
|
104
115
|
} else {
|
|
@@ -149,6 +160,9 @@ export class MDNCollectionSaveButton extends L10nMixin(LitElement) {
|
|
|
149
160
|
this._lastAction = "save";
|
|
150
161
|
|
|
151
162
|
const url = `/api/v2/collections/${collectionId}/items/${item ? `${item.id}/` : ""}`;
|
|
163
|
+
|
|
164
|
+
gleanClick("new_collection_modal_submit_article_actions");
|
|
165
|
+
|
|
152
166
|
const res = await fetch(url, {
|
|
153
167
|
body: JSON.stringify({
|
|
154
168
|
url: this.docUrl,
|
|
@@ -229,6 +243,7 @@ export class MDNCollectionSaveButton extends L10nMixin(LitElement) {
|
|
|
229
243
|
)`Collection:`}
|
|
230
244
|
<select
|
|
231
245
|
.value=${this._item?.collection_id}
|
|
246
|
+
@focus=${this._selectOpen}
|
|
232
247
|
@change=${this._selectChange}
|
|
233
248
|
>
|
|
234
249
|
${collections.map(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LitElement, html } from "lit";
|
|
2
2
|
|
|
3
3
|
import { L10nMixin } from "../../l10n/mixin.js";
|
|
4
|
+
import { gleanClick } from "../../utils/glean.js";
|
|
4
5
|
|
|
5
6
|
import styles from "./element.css?lit";
|
|
6
7
|
|
|
@@ -17,6 +18,7 @@ export class MDNColorTheme extends L10nMixin(LitElement) {
|
|
|
17
18
|
super();
|
|
18
19
|
/** @type {import("./types.js").ColorScheme} */
|
|
19
20
|
this._mode = "light dark";
|
|
21
|
+
this._closingFromSelect = false;
|
|
20
22
|
this._options = Object.entries({
|
|
21
23
|
"light dark": this.l10n("theme-default")`OS default`,
|
|
22
24
|
light: this.l10n("color-theme-light")`Light`,
|
|
@@ -30,6 +32,8 @@ export class MDNColorTheme extends L10nMixin(LitElement) {
|
|
|
30
32
|
const mode = target.dataset.mode;
|
|
31
33
|
if (mode === "light dark" || mode === "light" || mode === "dark") {
|
|
32
34
|
this._mode = mode;
|
|
35
|
+
const gleanMode = mode === "light dark" ? "os-default" : mode;
|
|
36
|
+
gleanClick(`theme_switcher: switch -> ${gleanMode}`);
|
|
33
37
|
try {
|
|
34
38
|
localStorage.setItem("theme", mode);
|
|
35
39
|
} catch (error) {
|
|
@@ -37,12 +41,24 @@ export class MDNColorTheme extends L10nMixin(LitElement) {
|
|
|
37
41
|
}
|
|
38
42
|
const dropdown = this.shadowRoot?.querySelector("mdn-dropdown");
|
|
39
43
|
if (dropdown) {
|
|
44
|
+
this._closingFromSelect = true;
|
|
40
45
|
dropdown.open = false;
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
/** @param {Event} event */
|
|
52
|
+
_onDropdownToggle({ target }) {
|
|
53
|
+
if (target instanceof HTMLElement && "open" in target) {
|
|
54
|
+
if (!target.open && this._closingFromSelect) {
|
|
55
|
+
this._closingFromSelect = false;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
gleanClick(`theme_switcher: ${target.open ? "open" : "close"}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
/**
|
|
47
63
|
* @param {import("lit").PropertyValues<this>} changedProperties
|
|
48
64
|
*/
|
|
@@ -61,7 +77,7 @@ export class MDNColorTheme extends L10nMixin(LitElement) {
|
|
|
61
77
|
|
|
62
78
|
render() {
|
|
63
79
|
return html`<div class="color-theme">
|
|
64
|
-
<mdn-dropdown>
|
|
80
|
+
<mdn-dropdown @toggle=${this._onDropdownToggle}>
|
|
65
81
|
<button
|
|
66
82
|
part="button"
|
|
67
83
|
slot="button"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { html } from "@lit-labs/ssr";
|
|
2
|
+
import { nothing } from "lit";
|
|
3
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
4
|
+
|
|
5
|
+
import { queryToUrl } from "../compat-table/utils.js";
|
|
6
|
+
import { HeadingAnchor } from "../heading-anchor/server.js";
|
|
7
|
+
import { ServerComponent } from "../server/index.js";
|
|
8
|
+
|
|
9
|
+
export class CompatSection extends ServerComponent {
|
|
10
|
+
/**
|
|
11
|
+
* @param {import("@fred").Context} _context
|
|
12
|
+
* @param {import("@rari").Compat} section
|
|
13
|
+
*/
|
|
14
|
+
render(_context, { id, title, query, isH3 }) {
|
|
15
|
+
const level = isH3 ? 3 : 2;
|
|
16
|
+
return html`<section
|
|
17
|
+
class="content-section"
|
|
18
|
+
aria-labelledby=${ifDefined(id ?? undefined)}
|
|
19
|
+
>
|
|
20
|
+
${HeadingAnchor.render(level, id ? String(id) : null, String(title))}
|
|
21
|
+
<mdn-compat-table-lazy
|
|
22
|
+
locale="en-US"
|
|
23
|
+
query=${query}
|
|
24
|
+
></mdn-compat-table-lazy>
|
|
25
|
+
</section>`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {import("@fred").Context} _context
|
|
30
|
+
* @param {import("@rari").Compat} section
|
|
31
|
+
*/
|
|
32
|
+
renderSimplified(_context, { id, title, query, isH3 }) {
|
|
33
|
+
return html`<section
|
|
34
|
+
class="content-section"
|
|
35
|
+
aria-labelledby=${ifDefined(id ?? undefined)}
|
|
36
|
+
>
|
|
37
|
+
${isH3
|
|
38
|
+
? nothing
|
|
39
|
+
: HeadingAnchor.render(2, id ? String(id) : null, String(title))}
|
|
40
|
+
<a href=${queryToUrl(query)} target="_blank" rel="noopener"
|
|
41
|
+
><code>${query}</code></a
|
|
42
|
+
>
|
|
43
|
+
</section>`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { LitElement, html, nothing } from "lit";
|
|
2
2
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
3
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
3
4
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
4
5
|
|
|
5
6
|
import { L10nMixin } from "../../l10n/mixin.js";
|
|
7
|
+
import { gleanClick } from "../../utils/glean.js";
|
|
8
|
+
import { ViewedController } from "../viewed-controller/viewed-controller.js";
|
|
6
9
|
|
|
7
10
|
import { DEFAULT_LOCALE, ISSUE_METADATA_TEMPLATE } from "./constants.js";
|
|
8
11
|
import styles from "./element.css?lit";
|
|
@@ -73,6 +76,9 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
|
|
|
73
76
|
|
|
74
77
|
static styles = styles;
|
|
75
78
|
|
|
79
|
+
/** @type {import("lit/directives/ref.js").Ref<HTMLElement>} */
|
|
80
|
+
_ref = createRef();
|
|
81
|
+
|
|
76
82
|
constructor() {
|
|
77
83
|
super();
|
|
78
84
|
this.query = "";
|
|
@@ -88,6 +94,9 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
|
|
|
88
94
|
this._browsers = [];
|
|
89
95
|
/** @type {string|undefined} */
|
|
90
96
|
this._showTimelineId = undefined;
|
|
97
|
+
new ViewedController(this, this._ref, () => {
|
|
98
|
+
gleanClick(`bcd: view -> ${this.query}`);
|
|
99
|
+
});
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
get _breadcrumbs() {
|
|
@@ -255,7 +264,7 @@ export class MDNCompatTable extends L10nMixin(LitElement) {
|
|
|
255
264
|
}
|
|
256
265
|
|
|
257
266
|
_renderTable() {
|
|
258
|
-
return html`<figure class="table-container">
|
|
267
|
+
return html`<figure ${ref(this._ref)} class="table-container">
|
|
259
268
|
<figure class="table-container-inner">
|
|
260
269
|
${this._renderIssueLink()}
|
|
261
270
|
<table
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BCD_BASE_URL } from "../env/index.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* A list of browsers to be hidden.
|
|
3
5
|
* @constant {string[]}
|
|
@@ -293,3 +295,11 @@ export function getCurrentSupport(support) {
|
|
|
293
295
|
// Default (likely never reached).
|
|
294
296
|
return getFirst(support);
|
|
295
297
|
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get the URL to the JSON file for a given BCD key
|
|
301
|
+
* @param {string} query
|
|
302
|
+
*/
|
|
303
|
+
export function queryToUrl(query) {
|
|
304
|
+
return `${BCD_BASE_URL}/bcd/api/v0/current/${query}.json`;
|
|
305
|
+
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
DEFAULT_LOCALE,
|
|
9
9
|
ISSUE_METADATA_TEMPLATE,
|
|
10
10
|
} from "../compat-table/constants.js";
|
|
11
|
-
import {
|
|
11
|
+
import { queryToUrl } from "../compat-table/utils.js";
|
|
12
12
|
|
|
13
13
|
import styles from "./element.css?lit";
|
|
14
14
|
|
|
@@ -76,10 +76,7 @@ export class MDNCompatTableLazy extends L10nMixin(LitElement) {
|
|
|
76
76
|
_dataTask = new Task(this, {
|
|
77
77
|
args: () => [this.query],
|
|
78
78
|
task: async ([query], { signal }) => {
|
|
79
|
-
const response = await fetch(
|
|
80
|
-
`${BCD_BASE_URL}/bcd/api/v0/current/${query}.json`,
|
|
81
|
-
{ signal },
|
|
82
|
-
);
|
|
79
|
+
const response = await fetch(queryToUrl(query), { signal });
|
|
83
80
|
if (!response.ok) {
|
|
84
81
|
console.error("Failed to fetch BCD data:", response);
|
|
85
82
|
|
|
@@ -2,20 +2,20 @@ import { html } from "@lit-labs/ssr";
|
|
|
2
2
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
3
3
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
4
4
|
|
|
5
|
+
import { CompatSection } from "../compat-section/server.js";
|
|
5
6
|
import { HeadingAnchor } from "../heading-anchor/server.js";
|
|
6
7
|
import { ServerComponent } from "../server/index.js";
|
|
7
|
-
import { SpecificationsList } from "../specifications-list/
|
|
8
|
+
import { SpecificationsList } from "../specifications-list/server.js";
|
|
8
9
|
|
|
9
10
|
export class ContentSection extends ServerComponent {
|
|
10
11
|
/**
|
|
11
12
|
* @param {import("@fred").Context} context
|
|
12
13
|
* @param {import("@rari").Section} section
|
|
13
|
-
* @returns {import("@lit").TemplateResult}
|
|
14
14
|
*/
|
|
15
15
|
render(context, { type, value }) {
|
|
16
16
|
switch (type) {
|
|
17
17
|
case "browser_compatibility": {
|
|
18
|
-
return
|
|
18
|
+
return CompatSection.render(context, value);
|
|
19
19
|
}
|
|
20
20
|
case "specifications": {
|
|
21
21
|
return SpecificationsSection(context, value);
|
|
@@ -43,24 +43,6 @@ function Prose({ id, title, content, isH3 }) {
|
|
|
43
43
|
</section>`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/**
|
|
47
|
-
* @param {import("@rari").Compat} section
|
|
48
|
-
* @returns {import("@lit").TemplateResult}
|
|
49
|
-
*/
|
|
50
|
-
function BCD({ id, title, query, isH3 }) {
|
|
51
|
-
const level = isH3 ? 3 : 2;
|
|
52
|
-
return html`<section
|
|
53
|
-
class="content-section"
|
|
54
|
-
aria-labelledby=${ifDefined(id ?? undefined)}
|
|
55
|
-
>
|
|
56
|
-
${HeadingAnchor.render(level, id ? String(id) : null, String(title))}
|
|
57
|
-
<mdn-compat-table-lazy
|
|
58
|
-
locale="en-US"
|
|
59
|
-
query=${query}
|
|
60
|
-
></mdn-compat-table-lazy>
|
|
61
|
-
</section>`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
46
|
/**
|
|
65
47
|
* @param {import("@fred").Context} context
|
|
66
48
|
* @param {import("@rari").SpecificationSection} section
|
|
@@ -72,6 +54,6 @@ function SpecificationsSection(context, { id, title, specifications, isH3 }) {
|
|
|
72
54
|
aria-labelledby=${ifDefined(id ?? undefined)}
|
|
73
55
|
>
|
|
74
56
|
${HeadingAnchor.render(level, id ? String(id) : null, String(title))}
|
|
75
|
-
${SpecificationsList(context, specifications)}
|
|
57
|
+
${SpecificationsList.render(context, specifications)}
|
|
76
58
|
</section>`;
|
|
77
59
|
}
|
|
@@ -257,7 +257,10 @@ export class CurriculumLanding extends ServerComponent {
|
|
|
257
257
|
const bannerLight = partnerBannerLight;
|
|
258
258
|
|
|
259
259
|
return html`
|
|
260
|
-
<section
|
|
260
|
+
<section
|
|
261
|
+
class="curriculum-partner-banner-container"
|
|
262
|
+
data-glean-view="curriculum: partner banner view"
|
|
263
|
+
>
|
|
261
264
|
<div class="partner-banner">
|
|
262
265
|
<section>
|
|
263
266
|
<h2>Learn the curriculum with Scrimba and become job ready</h2>
|
|
@@ -96,8 +96,19 @@ export class MDNDropdown extends LitElement {
|
|
|
96
96
|
`;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
/**
|
|
100
|
+
* @param {import("lit").PropertyValues<this>} changedProperties
|
|
101
|
+
*/
|
|
102
|
+
updated(changedProperties) {
|
|
100
103
|
this._setAriaAttributes();
|
|
104
|
+
if (
|
|
105
|
+
changedProperties.has("open") &&
|
|
106
|
+
changedProperties.get("open") !== undefined
|
|
107
|
+
) {
|
|
108
|
+
this.dispatchEvent(
|
|
109
|
+
new Event("toggle", { bubbles: true, composed: true }),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
firstUpdated() {
|
package/components/env/index.js
CHANGED
|
@@ -50,4 +50,4 @@ export const OBSERVATORY_API_URL = parseString(
|
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
// Transcend Consent Management - https://docs.transcend.io/docs/consent
|
|
53
|
-
export const
|
|
53
|
+
export const TRANSCEND_AIRGAP_URL = parseString("TRANSCEND_AIRGAP_URL", "");
|
|
@@ -10,16 +10,23 @@ export class FeaturedArticles extends ServerComponent {
|
|
|
10
10
|
render(articles) {
|
|
11
11
|
return html`<ul class="featured-articles">
|
|
12
12
|
${articles.map(
|
|
13
|
-
(article) =>
|
|
13
|
+
(article, index) =>
|
|
14
14
|
html`<li>
|
|
15
15
|
<article class="featured-articles__article">
|
|
16
16
|
${article.tag
|
|
17
|
-
? html`<a
|
|
17
|
+
? html`<a
|
|
18
|
+
class="featured-articles__tag"
|
|
19
|
+
href=${article.tag.uri}
|
|
20
|
+
data-glean-id=${`homepage: article_tag ${index + 1} -> ${article.tag.uri}`}
|
|
18
21
|
>${article.tag.title}</a
|
|
19
22
|
>`
|
|
20
23
|
: nothing}
|
|
21
24
|
<h3 class="featured-articles__title">
|
|
22
|
-
<a
|
|
25
|
+
<a
|
|
26
|
+
href=${article.mdn_url}
|
|
27
|
+
data-glean-id=${`homepage: article ${index + 1} -> ${article.mdn_url}`}
|
|
28
|
+
>${article.title}</a
|
|
29
|
+
>
|
|
23
30
|
</h3>
|
|
24
31
|
<p class="featured-articles__summary">${article.summary}</p>
|
|
25
32
|
</article>
|