@mdn/fred 1.6.0 → 1.6.1

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/components/menu/constants.js +8 -20
  3. package/components/menu/server.js +5 -2
  4. package/components/reference-layout/server.css +1 -1
  5. package/components/reference-layout/server.js +3 -2
  6. package/components/survey/README.md +5 -0
  7. package/components/survey/element.css +10 -0
  8. package/components/survey/element.js +45 -35
  9. package/components/survey/surveys.js +25 -21
  10. package/components/survey/types.d.ts +2 -0
  11. package/out/service-worker.js +1 -1
  12. package/out/static/client/2936.d694758cdb9cca05.js +2 -0
  13. package/out/static/client/2936.d694758cdb9cca05.js.map +1 -0
  14. package/out/static/client/3279.af292ae0820e0f6d.js +33 -0
  15. package/out/static/client/3279.af292ae0820e0f6d.js.map +1 -0
  16. package/out/static/client/9804.307d2bd47ea925e6.js +2 -0
  17. package/out/static/client/9804.307d2bd47ea925e6.js.map +1 -0
  18. package/out/static/client/{index.1fdf341dd8b9ed94.js → index.db196dc46f1ac46c.js} +4 -4
  19. package/out/static/client/index.db196dc46f1ac46c.js.map +1 -0
  20. package/out/static/client/{runtime.45841d40e5b5e1ef.js → runtime.27e033484190f93b.js} +2 -2
  21. package/out/static/client/{runtime.45841d40e5b5e1ef.js.map → runtime.27e033484190f93b.js.map} +1 -1
  22. package/out/static/client/stats.json +22 -22
  23. package/out/static/client/{styles-global.4031cdde644ed6ce.css → styles-global.c9f2a49e47bf5c55.css} +2 -2
  24. package/out/static/client/{styles-global.4031cdde644ed6ce.css.map → styles-global.c9f2a49e47bf5c55.css.map} +1 -1
  25. package/out/static/client/{styles-global.d954bf3cd4308e10.js → styles-global.ee8053c9a16002d4.js} +1 -1
  26. package/out/static/client/styles-reference-layout.be92c8fb52c5622b.css +2 -0
  27. package/out/static/client/styles-reference-layout.be92c8fb52c5622b.css.map +1 -0
  28. package/out/static/legacy/{1539.7e1697ed14914723.js → 1539.85dfd82e5678bcd1.js} +4 -4
  29. package/out/static/legacy/{1539.7e1697ed14914723.js.map → 1539.85dfd82e5678bcd1.js.map} +1 -1
  30. package/out/static/legacy/asset-manifest.json +7 -7
  31. package/out/static/legacy/{index.f8971c6fcd83fc71.js → index.66ef13fe869f8d43.js} +3 -3
  32. package/out/static/legacy/{index.f8971c6fcd83fc71.js.map → index.66ef13fe869f8d43.js.map} +1 -1
  33. package/out/static/legacy/{index.ce4e2073f6fcdcde.html → index.ada5bb78e9e86c98.html} +1 -1
  34. package/out/static/legacy/stats.json +13 -13
  35. package/out/static/legacy/{yari.87de8673af6079c0.js → yari.4e0236da42985a90.js} +3 -3
  36. package/out/static/legacy/{yari.87de8673af6079c0.js.map → yari.4e0236da42985a90.js.map} +1 -1
  37. package/out/static/ssr/index.js +31 -23
  38. package/out/static/ssr/index.js.map +1 -1
  39. package/out/static/ssr/stats.json +4 -4
  40. package/package.json +2 -2
  41. package/out/static/client/2936.45c52ae3ada1ba52.js +0 -2
  42. package/out/static/client/2936.45c52ae3ada1ba52.js.map +0 -1
  43. package/out/static/client/3279.c247237095588115.js +0 -27
  44. package/out/static/client/3279.c247237095588115.js.LICENSE.txt +0 -5
  45. package/out/static/client/3279.c247237095588115.js.map +0 -1
  46. package/out/static/client/9804.f51f5d1ea2fd9564.js +0 -2
  47. package/out/static/client/9804.f51f5d1ea2fd9564.js.map +0 -1
  48. package/out/static/client/index.1fdf341dd8b9ed94.js.map +0 -1
  49. package/out/static/client/styles-reference-layout.d6cf97d60124f649.css +0 -2
  50. package/out/static/client/styles-reference-layout.d6cf97d60124f649.css.map +0 -1
  51. /package/out/static/client/{index.1fdf341dd8b9ed94.js.LICENSE.txt → index.db196dc46f1ac46c.js.LICENSE.txt} +0 -0
  52. /package/out/static/legacy/{1539.7e1697ed14914723.js.LICENSE.txt → 1539.85dfd82e5678bcd1.js.LICENSE.txt} +0 -0
  53. /package/out/static/legacy/{index.f8971c6fcd83fc71.js.LICENSE.txt → index.66ef13fe869f8d43.js.LICENSE.txt} +0 -0
  54. /package/out/static/legacy/{yari.87de8673af6079c0.js.LICENSE.txt → yari.4e0236da42985a90.js.LICENSE.txt} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.1](https://github.com/mdn/fred/compare/v1.6.0...v1.6.1) (2025-10-03)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **menu:** update Color format converter link (Color picker) ([#816](https://github.com/mdn/fred/issues/816)) ([7422d4b](https://github.com/mdn/fred/commit/7422d4b024967a5cd0765a8a546e27a381541c73))
9
+
10
+
11
+ ### Miscellaneous
12
+
13
+ * **deps:** bump @codemirror/lang-html from 6.4.10 to 6.4.11 in the prod group ([#854](https://github.com/mdn/fred/issues/854)) ([3e8572c](https://github.com/mdn/fred/commit/3e8572cad623795fd12facc043c421a9850fe9b4))
14
+ * **menu:** update missing docs ([#860](https://github.com/mdn/fred/issues/860)) ([212bfbc](https://github.com/mdn/fred/commit/212bfbca3b522f7c624674bc80641974d674c251))
15
+ * **survey:** add developer survey 2025 ([#853](https://github.com/mdn/fred/issues/853)) ([71acaf1](https://github.com/mdn/fred/commit/71acaf165f8b1136a1c92990b70a6e55b990fd34))
16
+
3
17
  ## [1.6.0](https://github.com/mdn/fred/compare/v1.5.0...v1.6.0) (2025-10-01)
4
18
 
5
19
 
@@ -7,14 +7,13 @@
7
7
  */
8
8
  export const MISSING_DOCS = {
9
9
  es: [
10
- "Web/API/File_System_API",
11
10
  "Web/API/HTML_DOM_API",
12
11
  "Web/API/History_API/Working_with_the_History_API",
13
12
  "Web/API/Web_Animations_API/Using_the_Web_Animations_API",
13
+ "Web/CSS/CSS_Values_and_Units",
14
14
  "Web/CSS/CSS_backgrounds_and_borders/Box-shadow_generator",
15
15
  "Web/CSS/CSS_colors/Color_mixer",
16
16
  "Web/CSS/CSS_shapes/Shape_generator",
17
- "Web/CSS/CSS_Values_and_Units",
18
17
  "Web/CSS/Guides",
19
18
  "Web/CSS/Layout_cookbook/Card",
20
19
  "Web/CSS/Layout_cookbook/Center_an_element",
@@ -30,22 +29,8 @@ export const MISSING_DOCS = {
30
29
  "Web/WebDriver",
31
30
  ],
32
31
  fr: [
33
- "Learn_web_development/Getting_started",
34
- "Web/API/File_System_API",
35
- "Web/API/HTML_DOM_API",
36
- "Web/API/Web_Animations_API/Using_the_Web_Animations_API",
37
32
  "Web/CSS/CSS_box_model/Introduction_to_the_CSS_box_model",
38
- "Web/CSS/CSS_colors/Color_mixer",
39
- "Web/CSS/CSS_colors/Color_picker",
40
33
  "Web/CSS/CSS_shapes/Shape_generator",
41
- "Web/CSS/Guides",
42
- "Web/CSS/Properties",
43
- "Web/HTML/Guides",
44
- "Web/JavaScript/Guide/Using_classes",
45
- "Web/Media",
46
- "Web/Privacy",
47
- "Web/URI",
48
- "Web/WebDriver",
49
34
  ],
50
35
  ja: ["Web/CSS/CSS_colors/Color_mixer", "Web/CSS/CSS_shapes/Shape_generator"],
51
36
  ko: [
@@ -93,14 +78,15 @@ export const MISSING_DOCS = {
93
78
  "Web/Privacy",
94
79
  "Web/URI",
95
80
  "Web/WebDriver",
81
+ "Web/XML",
96
82
  ],
97
83
  ru: [
98
84
  "Learn_web_development/Getting_started",
99
85
  "Web/API/HTML_DOM_API",
100
86
  "Web/CSS/CSS_Values_and_Units",
101
- "Web/CSS/Guides",
102
87
  "Web/CSS/CSS_colors/Color_mixer",
103
88
  "Web/CSS/CSS_shapes/Shape_generator",
89
+ "Web/CSS/Guides",
104
90
  "Web/CSS/Layout_cookbook/Card",
105
91
  "Web/CSS/Layout_cookbook/Center_an_element",
106
92
  "Web/CSS/Layout_cookbook/Column_layouts",
@@ -115,9 +101,9 @@ export const MISSING_DOCS = {
115
101
  "Web/WebDriver",
116
102
  ],
117
103
  "zh-CN": [
118
- "Web/CSS/Properties",
119
104
  "Web/CSS/CSS_colors/Color_mixer",
120
105
  "Web/CSS/CSS_shapes/Shape_generator",
106
+ "Web/CSS/Properties",
121
107
  ],
122
108
  "zh-TW": [
123
109
  "Learn_web_development/Getting_started",
@@ -129,14 +115,14 @@ export const MISSING_DOCS = {
129
115
  "Web/API/Service_Worker_API",
130
116
  "Web/API/Web_Animations_API/Using_the_Web_Animations_API",
131
117
  "Web/API/Web_Speech_API/Using_the_Web_Speech_API",
132
- "Web/CSS/CSS_colors/Color_mixer",
133
- "Web/CSS/CSS_shapes/Shape_generator",
134
118
  "Web/CSS/CSS_Values_and_Units",
135
119
  "Web/CSS/CSS_backgrounds_and_borders/Border-image_generator",
136
120
  "Web/CSS/CSS_backgrounds_and_borders/Border-radius_generator",
137
121
  "Web/CSS/CSS_backgrounds_and_borders/Box-shadow_generator",
138
122
  "Web/CSS/CSS_box_model/Introduction_to_the_CSS_box_model",
139
123
  "Web/CSS/CSS_colors",
124
+ "Web/CSS/CSS_colors/Color_mixer",
125
+ "Web/CSS/CSS_shapes/Shape_generator",
140
126
  "Web/CSS/CSS_syntax/At-rule",
141
127
  "Web/CSS/Guides",
142
128
  "Web/CSS/Layout_cookbook",
@@ -146,6 +132,7 @@ export const MISSING_DOCS = {
146
132
  "Web/CSS/Properties",
147
133
  "Web/CSS/Reference",
148
134
  "Web/HTML/Guides",
135
+ "Web/HTML/Guides/Cheatsheet",
149
136
  "Web/HTML/Guides/Date_and_time_formats",
150
137
  "Web/HTML/Guides/Responsive_images",
151
138
  "Web/HTML/How_to/Add_JavaScript_to_your_web_page",
@@ -159,5 +146,6 @@ export const MISSING_DOCS = {
159
146
  "Web/Privacy",
160
147
  "Web/URI",
161
148
  "Web/WebDriver",
149
+ "Web/XML",
162
150
  ],
163
151
  };
@@ -618,10 +618,13 @@ export class Menu extends ServerComponent {
618
618
  )}
619
619
  </li>
620
620
  <li>
621
- ${link("Web/CSS/CSS_colors/Color_mixer", "Color mixer")}
621
+ ${link(
622
+ "Web/CSS/CSS_colors/Color_format_converter",
623
+ "Color format converter",
624
+ )}
622
625
  </li>
623
626
  <li>
624
- ${link("Web/CSS/CSS_colors/Color_picker", "Color picker")}
627
+ ${link("Web/CSS/CSS_colors/Color_mixer", "Color mixer")}
625
628
  </li>
626
629
  <li>
627
630
  ${link(
@@ -6,7 +6,7 @@
6
6
  grid-template-areas:
7
7
  "sidebar . header . toc"
8
8
  "sidebar . body . toc";
9
- grid-template-rows: min-content auto;
9
+ grid-template-rows: min-content 1fr;
10
10
  grid-template-columns: var(--layout-2-sidebars);
11
11
 
12
12
  justify-content: space-between;
@@ -27,14 +27,15 @@ export class ReferenceLayout extends ServerComponent {
27
27
  ${WRITER_MODE ? WriterToolbar.render(context) : nothing}
28
28
  ${TranslationBanner.render(context)}
29
29
  <h1>${doc.title}</h1>
30
- ${BaselineIndicator.render(context)} ${description}
30
+ ${BaselineIndicator.render(context)}
31
+ <mdn-survey></mdn-survey>
32
+ ${description}
31
33
  </div>
32
34
  <aside class="reference-layout__toc">
33
35
  ${ReferenceToc.render(context)}
34
36
  <mdn-placement-sidebar></mdn-placement-sidebar>
35
37
  </aside>
36
38
  <div class="reference-layout__body">
37
- <mdn-survey></mdn-survey>
38
39
  ${sections} ${ArticleFooter.render(context)}
39
40
  </div>
40
41
  </main>
@@ -0,0 +1,5 @@
1
+ # Surveys
2
+
3
+ You can force show a survey by appending `?force_survey` to the URL of any doc page.
4
+
5
+ If multiple surveys are configured, this will only show the first in the array: you can force a particular survey by appending `?force_survey=SURVEY_KEY` to the URL.
@@ -1,3 +1,5 @@
1
+ @import url("../external-link/global.css");
2
+
1
3
  :host {
2
4
  display: block;
3
5
  }
@@ -53,6 +55,14 @@ summary {
53
55
  cursor: pointer;
54
56
  }
55
57
 
58
+ a {
59
+ color: var(--color-text-purple);
60
+
61
+ &:hover {
62
+ text-decoration: none;
63
+ }
64
+ }
65
+
56
66
  iframe {
57
67
  width: 100%;
58
68
  height: 500px;
@@ -4,6 +4,7 @@ import { createRef, ref } from "lit/directives/ref.js";
4
4
 
5
5
  import "../button/element.js";
6
6
  import { L10nMixin } from "../../l10n/mixin.js";
7
+ import { gleanClick } from "../../utils/glean.js";
7
8
  import closeIcon from "../icon/cancel.svg?lit";
8
9
 
9
10
  import styles from "./element.css?lit";
@@ -48,18 +49,13 @@ export class MDNSurvey extends L10nMixin(LitElement) {
48
49
  }
49
50
 
50
51
  #checkForSurvey() {
51
- if (globalThis.window === undefined) return;
52
-
53
- const FORCE_SURVEY_PREFIX = "#FORCE_SURVEY=";
54
- this._force = globalThis.location.hash.startsWith(FORCE_SURVEY_PREFIX);
55
-
56
52
  this._survey = this.#findSurvey();
57
53
 
58
54
  if (this._survey) {
59
55
  this._surveyState = getSurveyState(this._survey.bucket);
60
56
  this._source =
61
57
  typeof this._survey.src === "function"
62
- ? this._survey.src(globalThis.location.pathname)
58
+ ? this._survey.src(location.pathname)
63
59
  : this._survey.src;
64
60
 
65
61
  this.#markAsSeen();
@@ -70,20 +66,16 @@ export class MDNSurvey extends L10nMixin(LitElement) {
70
66
  * @returns {Survey.Survey | undefined}
71
67
  */
72
68
  #findSurvey() {
69
+ const forcedSurvey = new URLSearchParams(location.search).get(
70
+ "force_survey",
71
+ );
72
+ this._force = forcedSurvey !== null;
73
73
  return SURVEYS.find((survey) => {
74
74
  if (this._force) {
75
- const FORCE_SURVEY_PREFIX = "#FORCE_SURVEY=";
76
- return (
77
- survey.key ===
78
- globalThis.location.hash.slice(FORCE_SURVEY_PREFIX.length)
79
- );
80
- }
81
-
82
- if (globalThis.window === undefined) {
83
- return false;
75
+ return forcedSurvey ? survey.key === forcedSurvey : true;
84
76
  }
85
77
 
86
- if (!survey.show(globalThis.location.pathname)) {
78
+ if (!survey.show(location.pathname)) {
87
79
  return false;
88
80
  }
89
81
 
@@ -121,22 +113,32 @@ export class MDNSurvey extends L10nMixin(LitElement) {
121
113
  this.requestUpdate();
122
114
  }
123
115
 
116
+ #onLinkClick() {
117
+ this.#markOpened();
118
+ }
119
+
124
120
  #onToggle() {
125
121
  if (!this._survey || !this._surveyState || this._isOpen) return;
126
122
 
127
123
  const details = this._detailsRef.value;
128
124
  if (details && details.open) {
129
- this._surveyState = {
130
- ...this._surveyState,
131
- opened_at: Date.now(),
132
- };
133
- writeSurveyState(this._survey.bucket, this._surveyState);
134
- this.#measure("opened");
125
+ this.#markOpened();
135
126
  this._isOpen = true;
136
127
  this.requestUpdate();
137
128
  }
138
129
  }
139
130
 
131
+ #markOpened() {
132
+ if (!this._survey || !this._surveyState) return;
133
+
134
+ this._surveyState = {
135
+ ...this._surveyState,
136
+ opened_at: Date.now(),
137
+ };
138
+ writeSurveyState(this._survey.bucket, this._surveyState);
139
+ this.#measure("opened");
140
+ }
141
+
140
142
  #onSubmitted() {
141
143
  if (!this._survey || !this._surveyState) return;
142
144
 
@@ -155,8 +157,7 @@ export class MDNSurvey extends L10nMixin(LitElement) {
155
157
  #measure(action) {
156
158
  if (!this._survey) return;
157
159
 
158
- // TODO: GLEAN
159
- console.log(`Survey: ${action} ${this._survey.bucket}`);
160
+ gleanClick(`survey: ${action} ${this._survey.bucket}`);
160
161
  }
161
162
 
162
163
  #setupMessageListener() {
@@ -208,17 +209,26 @@ export class MDNSurvey extends L10nMixin(LitElement) {
208
209
  @click=${this.#dismiss}
209
210
  ></mdn-button>
210
211
  </header>
211
- <details ${ref(this._detailsRef)} @toggle=${this.#onToggle}>
212
- <summary>${this._survey.question}</summary>
213
- ${this._isOpen && this._source
214
- ? html`
215
- <iframe
216
- title=${ifDefined(this._survey.question)}
217
- src=${this._source}
218
- ></iframe>
219
- `
220
- : nothing}
221
- </details>
212
+ ${this._survey.link
213
+ ? html`<a
214
+ class="external"
215
+ href=${this._source}
216
+ target="_blank"
217
+ title=${this.l10n`Take survey (Opens in a new tab)`}
218
+ @click=${this.#onLinkClick}
219
+ >${this._survey.question}</a
220
+ >`
221
+ : html`<details ${ref(this._detailsRef)} @toggle=${this.#onToggle}>
222
+ <summary>${this._survey.question}</summary>
223
+ ${this._isOpen && this._source
224
+ ? html`
225
+ <iframe
226
+ title=${ifDefined(this._survey.question)}
227
+ src=${this._source}
228
+ ></iframe>
229
+ `
230
+ : nothing}
231
+ </details>`}
222
232
  ${this._survey.footnote
223
233
  ? html` <footer>(${this._survey.footnote})</footer> `
224
234
  : nothing}
@@ -29,6 +29,7 @@ export const SurveyBucket = Object.freeze({
29
29
  HOUSE_SURVEY_2025: "HOUSE_SURVEY_2025",
30
30
  JS_PROPOSALS_2025: "JS_PROPOSALS_2025",
31
31
  FIRST_FRED_2025: "FIRST_FRED_2025",
32
+ DEVELOPER_SURVEY_2025: "DEVELOPER_SURVEY_2025",
32
33
  });
33
34
 
34
35
  /**
@@ -43,25 +44,28 @@ export const SurveyBucket = Object.freeze({
43
44
  * @type {Survey.Survey[]}
44
45
  */
45
46
  export const SURVEYS = [
46
- // {
47
- // key: "something unique in the current surveys",
48
- // bucket: SurveyBucket.FIRST_FRED_2025,
49
- // show: (mdn_url) => {
50
- // return /^\/[a-z]{2}(-[A-Z]{2})?\/docs\/Web\/CSS/.test(mdn_url);
51
- // },
52
- // src: (mdn_url) => {
53
- // const url = new URL(
54
- // "https://survey.alchemer.com/s3/8385674/MDN-short-survey-Fred",
55
- // );
56
- // url.searchParams.set("referrer", mdn_url);
57
- // return url.toString();
58
- // },
59
- // teaser: html`Fred is <strong>MDN</strong>'s shiny new frontend!`,
60
- // question: "How satisfied are you with this new MDN frontend?",
61
- // footnote: "fred = fr(ont)e(n)d",
62
- // rateFrom: 0,
63
- // rateTill: 1,
64
- // start: 0,
65
- // end: Infinity,
66
- // },
47
+ {
48
+ key: SurveyBucket.DEVELOPER_SURVEY_2025,
49
+ bucket: SurveyBucket.DEVELOPER_SURVEY_2025,
50
+ show: (mdn_url) => {
51
+ return /^\/[a-z]{2}(-[A-Z]{2})?\/docs\/(Glossary|Learn|Web)/.test(
52
+ mdn_url,
53
+ );
54
+ },
55
+ src: (mdn_url) => {
56
+ const url = new URL(
57
+ "https://survey.alchemer.com/s3/8409929/MDN-Developer-Survey",
58
+ );
59
+ url.searchParams.set("referrer", mdn_url);
60
+ return url.toString();
61
+ },
62
+ teaser:
63
+ "We’re running a survey to understand how you use MDN in your web development work.",
64
+ question: "We’d love for you to take 10 minutes to share your feedback.",
65
+ link: true,
66
+ rateFrom: 0,
67
+ rateTill: 0.3,
68
+ start: Date.parse("2025-10-06"),
69
+ end: Date.parse("2025-10-20"),
70
+ },
67
71
  ];
@@ -23,6 +23,8 @@ export interface Survey {
23
23
  question: TemplateResult | string;
24
24
  /** Optional footer HTML displayed below the survey */
25
25
  footnote?: TemplateResult | string;
26
+ /** Link to the survey instead of embedding it */
27
+ link?: boolean;
26
28
  }
27
29
 
28
30
  export interface SurveyState {