diff --git a/client/src/ui/atoms/signup-link/index.tsx b/client/src/ui/atoms/signup-link/index.tsx
index b01b87349bc1..51a6a3bcf372 100644
--- a/client/src/ui/atoms/signup-link/index.tsx
+++ b/client/src/ui/atoms/signup-link/index.tsx
@@ -10,7 +10,7 @@ export const SignUpLink = ({ toPlans = false, gleanContext = "" }) => {
const loginUrl = useLoginUrl();
const href = toPlans ? plansUrl : loginUrl;
- const label = toPlans ? "Upgrade Now" : "Sign up";
+ const label = toPlans ? "Upgrade Now" : "Sign up for free";
const rel = toPlans ? undefined : "nofollow";
return (
diff --git a/client/src/ui/molecules/guides-menu/index.scss b/client/src/ui/molecules/guides-menu/index.scss
new file mode 100644
index 000000000000..42a234d2f98a
--- /dev/null
+++ b/client/src/ui/molecules/guides-menu/index.scss
@@ -0,0 +1,23 @@
+@use "sass:color";
+@use "../../vars" as *;
+
+.guides {
+ .submenu .submenu-item-heading {
+ font-size: var(--type-smaller-font-size);
+ font-weight: initial;
+ }
+
+ .desktop-only {
+ display: none;
+ }
+
+ @media (min-width: $screen-lg) {
+ .desktop-only {
+ display: inherit;
+ }
+
+ .mobile-only {
+ display: none;
+ }
+ }
+}
diff --git a/client/src/ui/molecules/guides-menu/index.tsx b/client/src/ui/molecules/guides-menu/index.tsx
new file mode 100644
index 000000000000..402f11600180
--- /dev/null
+++ b/client/src/ui/molecules/guides-menu/index.tsx
@@ -0,0 +1,66 @@
+import { useLocale } from "../../../hooks";
+import { Menu } from "../menu";
+
+import "./index.scss";
+
+export const GuidesMenu = ({ visibleSubMenuId, toggleMenu }) => {
+ const locale = useLocale();
+
+ const menu = {
+ id: "guides",
+ label: "Guides",
+ to: `/${locale}/docs/Learn`,
+ items: [
+ {
+ description: "Learn web development",
+ hasIcon: true,
+ extraClasses: "apis-link-container mobile-only",
+ iconClasses: "submenu-icon learn",
+ label: "Overview / MDN Learning Area",
+ url: `/${locale}/docs/Learn`,
+ },
+ {
+ description: "Learn web development",
+ extraClasses: "apis-link-container desktop-only",
+ hasIcon: true,
+ iconClasses: "submenu-icon learn",
+ label: "MDN Learning Area",
+ url: `/${locale}/docs/Learn`,
+ },
+ {
+ description: "Learn to structure web content with HTML",
+ extraClasses: "html-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon html",
+ label: "HTML",
+ url: `/${locale}/docs/Learn/HTML`,
+ },
+ {
+ description: "Learn to style content using CSS",
+ extraClasses: "css-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon css",
+ label: "CSS",
+ url: `/${locale}/docs/Learn/CSS`,
+ },
+ {
+ description: "Learn to run scripts in the browser",
+ extraClasses: "javascript-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon javascript",
+ label: "JavaScript",
+ url: `/${locale}/docs/Learn/JavaScript`,
+ },
+ {
+ description: "Learn to make the web accessible to all",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "Accessibility",
+ url: `/${locale}/docs/Web/Accessibility`,
+ },
+ ],
+ };
+ const isOpen = visibleSubMenuId === menu.id;
+
+ return
;
+};
diff --git a/client/src/ui/molecules/main-menu/index.scss b/client/src/ui/molecules/main-menu/index.scss
index 459e5e18ec30..29913f4dbe63 100644
--- a/client/src/ui/molecules/main-menu/index.scss
+++ b/client/src/ui/molecules/main-menu/index.scss
@@ -40,22 +40,8 @@ ul.main-menu {
}
}
- .top-level-entry .long {
- display: none;
- }
-
@media (min-width: ($screen-xl)) {
gap: 1rem;
-
- .top-level-entry {
- .long {
- display: unset;
- }
-
- .short {
- display: none;
- }
- }
}
}
@@ -92,55 +78,3 @@ ul.main-menu {
}
}
}
-
-@media screen and (min-width: $screen-lg) {
- #more-button {
- display: flex;
-
- &::after {
- display: none;
- }
- }
-}
-
-.submenu-icon {
- &.html {
- background-color: var(--html-accent-color);
- }
-
- &.css {
- background-color: var(--css-accent-color);
- }
-
- &.javascript {
- background-color: var(--js-accent-color);
- }
-
- &.http {
- background-color: var(--http-accent-color);
- }
-
- &.apis {
- background-color: var(--apis-accent-color);
- }
-
- &.learn {
- background-color: var(--learn-accent-color);
- }
-
- &.plus {
- background-color: var(--plus-accent-color);
- }
-
- &.curriculum {
- background-color: var(--curriculum-color);
- }
-
- &.blog {
- background-color: var(--apis-accent-color);
- }
-
- &.observatory {
- background-color: var(--observatory-accent);
- }
-}
diff --git a/client/src/ui/molecules/main-menu/index.tsx b/client/src/ui/molecules/main-menu/index.tsx
index 25a4ca9c9254..a61dc3e1e28d 100644
--- a/client/src/ui/molecules/main-menu/index.tsx
+++ b/client/src/ui/molecules/main-menu/index.tsx
@@ -1,19 +1,15 @@
import { useEffect, useRef, useState } from "react";
-import { Menu } from "../menu";
+import { GuidesMenu } from "../guides-menu";
+import { ReferenceMenu } from "../reference-menu";
+import { PlusMenu } from "../plus-menu";
import "./index.scss";
-import "./tools.scss";
-
import { PLUS_IS_ENABLED } from "../../../env";
import { useGleanClick } from "../../../telemetry/glean-context";
import { MENU } from "../../../telemetry/constants";
import { useLocation } from "react-router";
-import { useIsServer, useLocale } from "../../../hooks";
-import { usePlusUrl } from "../../../plus/utils";
-import { MenuEntry } from "../submenu";
-import { useUserData } from "../../../user-context";
-import { OBSERVATORY_TITLE } from "../../../../../libs/constants";
+import { ToolsMenu } from "../tools-menu";
export default function MainMenu({ isOpenOnMobile }) {
const previousActiveElement = useRef(null);
@@ -68,269 +64,34 @@ export default function MainMenu({ isOpenOnMobile }) {
}
}
- const locale = useLocale();
-
- // Plus menu.
- const plusUrl = usePlusUrl();
- const isServer = useIsServer();
- const userData = useUserData();
- const isAuthenticated = userData && userData.isAuthenticated;
- const { pathname } = useLocation();
-
- const menus = [
- {
- id: "html",
- label: "HTML",
- to: `/${locale}/docs/Web/HTML`,
- isActive:
- pathname.startsWith(`/${locale}/docs/Learn/HTML`) ||
- pathname.startsWith(`/${locale}/docs/Web/HTML`),
- items: [
- {
- description: "Learn to structure web content with HTML",
- hasIcon: true,
- iconClasses: "submenu-icon html",
- label: "Learn HTML",
- url: `/${locale}/docs/Learn/HTML`,
- },
- {
- description: "Look up elements, attributes, and more",
- hasIcon: true,
- iconClasses: "submenu-icon html",
- label: "HTML references",
- url: `/${locale}/docs/Web/HTML`,
- },
- ],
- },
- {
- id: "css",
- label: "CSS",
- to: `/${locale}/docs/Web/CSS`,
- isActive:
- pathname.startsWith(`/${locale}/docs/Learn/CSS`) ||
- pathname.startsWith(`/${locale}/docs/Web/CSS`),
- items: [
- {
- description: "Learn to style content using CSS",
- hasIcon: true,
- iconClasses: "submenu-icon css",
- label: "Learn CSS",
- url: `/${locale}/docs/Learn/CSS`,
- },
- {
- description: "Look up properties, selectors, and more",
- hasIcon: true,
- iconClasses: "submenu-icon css",
- label: "CSS references",
- url: `/${locale}/docs/Web/CSS`,
- },
- ],
- },
- {
- id: "js",
- label: (
- <>
- JS
- JavaScript
- >
- ),
- to: `/${locale}/docs/Web/JavaScript`,
- isActive:
- pathname.startsWith(`/${locale}/docs/Learn/JavaScript`) ||
- pathname.startsWith(`/${locale}/docs/Web/JavaScript`),
- items: [
- {
- description: "Learn to run scripts in the browser",
- hasIcon: true,
- iconClasses: "submenu-icon javascript",
- label: "Learn JavaScript",
- url: `/${locale}/docs/Learn/JavaScript`,
- },
- {
- description: "Look up objects, expressions, and more",
- hasIcon: true,
- iconClasses: "submenu-icon javascript",
- label: "JavaScript references",
- url: `/${locale}/docs/Web/JavaScript`,
- },
- ],
- },
- {
- id: "apis",
- label: (
- <>
- APIs
- Web APIs
- >
- ),
- to: `/${locale}/docs/Web/API`,
- isActive: pathname.startsWith(`/${locale}/docs/Web/API`),
- items: [
- {
- description: "Look up all the APIs and interfaces",
- hasIcon: true,
- iconClasses: "submenu-icon apis",
- label: "Web API references",
- url: `/${locale}/docs/Web/API`,
- },
- ],
- },
- {
- id: "http",
- label: "HTTP",
- to: `/${locale}/docs/Web/HTTP`,
- isActive: pathname.startsWith(`/${locale}/docs/Web/HTTP`),
- items: [
- {
- description: "Look up status codes, headers, and more",
- hasIcon: true,
- iconClasses: "submenu-icon http",
- label: "HTTP references",
- url: `/${locale}/docs/Web/HTTP`,
- },
- ],
- },
- {
- id: "more",
- label: "More",
- items: [
- {
- description: "Learn to make the web accessible to all",
- hasIcon: true,
- iconClasses: "submenu-icon",
- label: "Accessibility",
- url: `/${locale}/docs/Web/Accessibility`,
- },
- {
- description: "Develop extensions for web browsers",
- hasIcon: true,
- iconClasses: "submenu-icon",
- label: "Web Extensions",
- url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`,
- },
- ],
- },
- {
- id: "learn",
- label: "Learn",
- to: `/${locale}/docs/Learn`,
- isActive:
- pathname.startsWith("/en-US/curriculum/") ||
- pathname.startsWith(`/${locale}/docs/Learn`),
- items: [
- {
- description: "Essential skills for front-end developers",
- hasIcon: true,
- iconClasses: "submenu-icon curriculum",
- label: "MDN Curriculum",
- url: "/en-US/curriculum/",
- },
- {
- description: "Learn web development",
- hasIcon: true,
- iconClasses: "submenu-icon learn",
- label: "MDN Learning Area",
- url: `/${locale}/docs/Learn`,
- },
- ],
- },
- {
- id: "blog",
- label: "Blog",
- to: "/en-US/blog/",
- isActive: pathname.startsWith("/en-US/blog/"),
- items: [
- {
- description: "Learn about web features, and MDN",
- hasIcon: true,
- iconClasses: "submenu-icon blog",
- label: "MDN Blog",
- url: "/en-US/blog/",
- },
- ],
- },
- {
- id: "tools",
- label: "Tools",
- items: [
- {
- description: "Write, test and share your code",
- hasIcon: true,
- iconClasses: "submenu-icon",
- label: "Playground",
- url: `/${locale}/play`,
- },
- {
- description: "Scan a website for free",
- hasIcon: true,
- iconClasses: "submenu-icon observatory",
- label: OBSERVATORY_TITLE,
- url: `/en-US/observatory`,
- },
- ...(PLUS_IS_ENABLED
- ? [
- {
- description: "A customized MDN experience",
- hasIcon: true,
- iconClasses: "submenu-icon plus",
- label: "MDN Plus",
- url: plusUrl,
- },
- {
- description: "Get real-time assistance and support",
- hasIcon: true,
- iconClasses: "submenu-icon plus",
- label: "AI Help",
- url: `/en-US/plus/ai-help`,
- },
- ...(!isServer && isAuthenticated
- ? [
- {
- description: "Your saved articles from across MDN",
- hasIcon: true,
- iconClasses: "submenu-icon plus",
- label: "Collections",
- url: `/${locale}/plus/collections`,
- },
- ]
- : []),
- {
- description: "All browser compatibility updates at a glance",
- hasIcon: true,
- iconClasses: "submenu-icon plus",
- label: "Updates",
- url: `/${locale}/plus/updates`,
- },
- ]
- : []),
- ],
- },
- ].filter(Boolean) as (MenuEntry | React.ReactElement)[];
-
return (
);
}
-function isMenuEntry(menu: any): menu is MenuEntry {
- return typeof menu.id === "string";
-}
-
function TopLevelMenuLink({
to,
children,
diff --git a/client/src/ui/molecules/plus-menu/index.scss b/client/src/ui/molecules/plus-menu/index.scss
new file mode 100644
index 000000000000..bdc55163ef3c
--- /dev/null
+++ b/client/src/ui/molecules/plus-menu/index.scss
@@ -0,0 +1,23 @@
+@use "sass:color";
+@use "../../vars" as *;
+
+.mdn-plus {
+ .submenu-icon {
+ background-color: var(--plus-accent-color);
+ }
+
+ .note {
+ background-color: var(--background-information);
+
+ .submenu-item-description {
+ display: block;
+ margin: 0.125rem;
+ }
+ }
+
+ @media (min-width: $screen-lg) {
+ .mobile-only {
+ display: none;
+ }
+ }
+}
diff --git a/client/src/ui/molecules/plus-menu/index.tsx b/client/src/ui/molecules/plus-menu/index.tsx
new file mode 100644
index 000000000000..5aa5138688c7
--- /dev/null
+++ b/client/src/ui/molecules/plus-menu/index.tsx
@@ -0,0 +1,94 @@
+import "./index.scss";
+import { usePlusUrl } from "../../../plus/utils";
+import { Menu } from "../menu";
+import { useIsServer, useLocale, useViewedState } from "../../../hooks";
+import { useUserData } from "../../../user-context";
+import { MenuEntry } from "../submenu";
+import { FeatureId } from "../../../constants";
+import { useLocation } from "react-router";
+
+export const PlusMenu = ({ visibleSubMenuId, toggleMenu }) => {
+ const plusUrl = usePlusUrl();
+ const locale = useLocale();
+ const isServer = useIsServer();
+ const userData = useUserData();
+ const isAuthenticated = userData && userData.isAuthenticated;
+
+ const { isViewed } = useViewedState();
+
+ // Avoid that "Plus" and "AI Help" are both active.
+ const { pathname } = useLocation();
+ const aiHelpUrl = `/${locale}/plus/ai-help`;
+ const isActive =
+ pathname.startsWith(plusUrl.split("#", 2)[0]) &&
+ !pathname.startsWith(aiHelpUrl);
+
+ const plusMenu: MenuEntry = {
+ label: "Plus",
+ id: "mdn-plus",
+ to: plusUrl,
+ items: [
+ {
+ description: "A customized MDN experience",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "Overview",
+ url: plusUrl,
+ },
+ {
+ description: "Get real-time assistance and support",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "AI Help",
+ url: aiHelpUrl,
+ },
+ ...(!isServer && isAuthenticated
+ ? [
+ {
+ description: "Your saved articles from across MDN",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "Collections",
+ url: `/${locale}/plus/collections`,
+ },
+ ]
+ : []),
+ {
+ description: "All browser compatibility updates at a glance",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "Updates",
+ dot:
+ Date.now() < 1675209600000 && // new Date("2023-02-01 00:00:00Z").getTime()
+ !isViewed(FeatureId.PLUS_UPDATES_V2)
+ ? "New feature"
+ : undefined,
+ url: `/${locale}/plus/updates`,
+ },
+ {
+ description: "Learn how to use MDN Plus",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "Documentation",
+ url: `/en-US/plus/docs/features/overview`,
+ },
+ {
+ description: "Frequently asked questions about MDN Plus",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "FAQ",
+ url: `/en-US/plus/docs/faq`,
+ },
+ ],
+ };
+ const isOpen = visibleSubMenuId === plusMenu.id;
+
+ return (
+
+ );
+};
diff --git a/client/src/ui/molecules/reference-menu/index.scss b/client/src/ui/molecules/reference-menu/index.scss
new file mode 100644
index 000000000000..1420a142333f
--- /dev/null
+++ b/client/src/ui/molecules/reference-menu/index.scss
@@ -0,0 +1,110 @@
+@use "sass:color";
+@use "../../vars" as *;
+
+.references {
+ .desktop-only {
+ display: none;
+ }
+
+ @media (min-width: $screen-lg) {
+ .desktop-only {
+ display: inherit;
+ }
+
+ .mobile-only {
+ display: none;
+ }
+ }
+}
+
+.html-link-container {
+ a:hover,
+ a:focus {
+ .submenu-icon {
+ &.html {
+ background: var(--html-accent-color) !important;
+ }
+ }
+ }
+}
+
+.css-link-container {
+ a:hover,
+ a:focus {
+ .submenu-icon {
+ &.css {
+ background-color: var(--css-accent-color) !important;
+ }
+ }
+ }
+}
+
+.javascript-link-container {
+ a:hover,
+ a:focus {
+ .submenu-icon {
+ &.javascript {
+ background-color: var(--js-accent-color) !important;
+ }
+ }
+ }
+}
+
+.http-link-container {
+ a:hover,
+ a:focus {
+ .submenu-icon {
+ &.http {
+ background-color: var(--http-accent-color) !important;
+ }
+ }
+ }
+}
+
+.apis-link-container {
+ a:hover,
+ a:focus {
+ .submenu-icon {
+ &.apis {
+ background-color: var(--apis-accent-color) !important;
+ }
+ }
+ }
+}
+
+.learn-link-container {
+ a:hover,
+ a:focus {
+ .submenu-icon {
+ &.learn {
+ background-color: var(--learn-accent-color) !important;
+ }
+ }
+ }
+}
+
+.submenu-icon {
+ &.html {
+ background-color: var(--html-accent-engage);
+ }
+
+ &.css {
+ background-color: var(--css-accent-engage);
+ }
+
+ &.javascript {
+ background-color: var(--js-accent-engage);
+ }
+
+ &.http {
+ background-color: var(--http-accent-engage);
+ }
+
+ &.apis {
+ background-color: var(--apis-accent-engage);
+ }
+
+ &.learn {
+ background-color: var(--learn-accent-engage);
+ }
+}
diff --git a/client/src/ui/molecules/reference-menu/index.tsx b/client/src/ui/molecules/reference-menu/index.tsx
new file mode 100644
index 000000000000..35c550dfa9e3
--- /dev/null
+++ b/client/src/ui/molecules/reference-menu/index.tsx
@@ -0,0 +1,84 @@
+import { useLocale } from "../../../hooks";
+import { Menu } from "../menu";
+
+import "./index.scss";
+
+export const ReferenceMenu = ({ visibleSubMenuId, toggleMenu }) => {
+ const locale = useLocale();
+
+ const menu = {
+ id: "references",
+ label: "References",
+ to: `/${locale}/docs/Web`,
+ items: [
+ {
+ description: "Web technology reference for developers",
+ hasIcon: true,
+ extraClasses: "apis-link-container mobile-only",
+ iconClasses: "submenu-icon",
+ label: "Overview / Web Technology",
+ url: `/${locale}/docs/Web`,
+ },
+ {
+ description: "Structure of content on the web",
+ extraClasses: "html-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon html",
+ label: "HTML",
+ url: `/${locale}/docs/Web/HTML`,
+ },
+ {
+ description: "Code used to describe document style",
+ extraClasses: "css-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon css",
+ label: "CSS",
+ url: `/${locale}/docs/Web/CSS`,
+ },
+ {
+ description: "General-purpose scripting language",
+ extraClasses: "javascript-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon javascript",
+ label: "JavaScript",
+ url: `/${locale}/docs/Web/JavaScript`,
+ },
+ {
+ description: "Protocol for transmitting web resources",
+ extraClasses: "http-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon http",
+ label: "HTTP",
+ url: `/${locale}/docs/Web/HTTP`,
+ },
+ {
+ description: "Interfaces for building web applications",
+ extraClasses: "apis-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon apis",
+ label: "Web APIs",
+ url: `/${locale}/docs/Web/API`,
+ },
+ {
+ description: "Developing extensions for web browsers",
+ extraClasses: "apis-link-container",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "Web Extensions",
+ url: `/${locale}/docs/Mozilla/Add-ons/WebExtensions`,
+ },
+ {
+ description: "Web technology reference for developers",
+ extraClasses: "apis-link-container desktop-only",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "Web Technology",
+ url: `/${locale}/docs/Web`,
+ },
+ ],
+ };
+
+ const isOpen = visibleSubMenuId === menu.id;
+
+ return ;
+};
diff --git a/client/src/ui/molecules/submenu/index.tsx b/client/src/ui/molecules/submenu/index.tsx
index caf480f0f7c8..1931e98db100 100644
--- a/client/src/ui/molecules/submenu/index.tsx
+++ b/client/src/ui/molecules/submenu/index.tsx
@@ -17,7 +17,6 @@ export type SubmenuItem = {
export type MenuEntry = {
id: string;
- isActive?: boolean;
items: SubmenuItem[];
label: string | ReactNode;
to?: string;
diff --git a/client/src/ui/molecules/theme-switcher/index.tsx b/client/src/ui/molecules/theme-switcher/index.tsx
index add18e2e3627..91e143ccb6f0 100644
--- a/client/src/ui/molecules/theme-switcher/index.tsx
+++ b/client/src/ui/molecules/theme-switcher/index.tsx
@@ -82,7 +82,7 @@ export const ThemeSwitcher = () => {
setIsOpen(!isOpen);
}}
>
- Theme
+ Theme
diff --git a/client/src/ui/molecules/main-menu/tools.scss b/client/src/ui/molecules/tools-menu/index.scss
similarity index 100%
rename from client/src/ui/molecules/main-menu/tools.scss
rename to client/src/ui/molecules/tools-menu/index.scss
diff --git a/client/src/ui/molecules/tools-menu/index.tsx b/client/src/ui/molecules/tools-menu/index.tsx
new file mode 100644
index 000000000000..ce1a83b316e2
--- /dev/null
+++ b/client/src/ui/molecules/tools-menu/index.tsx
@@ -0,0 +1,40 @@
+import { OBSERVATORY_TITLE } from "../../../../../libs/constants";
+import { useLocale } from "../../../hooks";
+import { Menu } from "../menu";
+
+import "./index.scss";
+
+export const ToolsMenu = ({ visibleSubMenuId, toggleMenu }) => {
+ const locale = useLocale();
+
+ const menu = {
+ id: "tools",
+ label: "Tools",
+ items: [
+ {
+ description: "Write, test and share your code",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "Playground",
+ url: `/${locale}/play`,
+ },
+ {
+ description: "Scan a website for free",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: OBSERVATORY_TITLE,
+ url: `/en-US/observatory`,
+ },
+ {
+ description: "Get real-time assistance and support",
+ hasIcon: true,
+ iconClasses: "submenu-icon",
+ label: "AI Help",
+ url: `/en-US/plus/ai-help`,
+ },
+ ],
+ };
+ const isOpen = visibleSubMenuId === menu.id;
+
+ return ;
+};