From c9437ce4412874b66cf4429162c07524c67109e8 Mon Sep 17 00:00:00 2001 From: Leo McArdle Date: Fri, 6 Dec 2024 14:12:02 +0000 Subject: [PATCH] feat: new about page (#12069) https://mozilla-hub.atlassian.net/browse/MP-1362 --------- Co-authored-by: Florian Dieminger --- build/spas.ts | 10 +- client/src/about/_mixins.scss | 282 ++++++ client/src/about/custom-elements.js | 76 ++ client/src/about/index.scss | 835 +++++++++++++++++- client/src/about/index.tsx | 263 ++++-- client/src/about/testimonial/index.scss | 64 -- client/src/about/testimonial/index.tsx | 28 - client/src/about/testimonial/quote.svg | 3 - client/src/app.scss | 1 - client/src/app.tsx | 2 +- client/src/assets/about/accurate-sm.svg | 1 + client/src/assets/about/accurate.svg | 1 + client/src/assets/about/building-dark.svg | 1 + client/src/assets/about/building.svg | 1 + client/src/assets/about/collaborative-sm.svg | 1 + client/src/assets/about/collaborative.svg | 1 + client/src/assets/about/dot-dark.svg | 1 + client/src/assets/about/dot.svg | 1 + client/src/assets/about/education.svg | 1 + .../src/assets/about/global-impact-dark.svg | 1 + client/src/assets/about/global-impact.svg | 1 + client/src/assets/about/handshake.svg | 1 + client/src/assets/about/inclusive-sm.svg | 1 + client/src/assets/about/inclusive.svg | 1 + client/src/assets/about/line-dot.svg | 1 + client/src/assets/about/sparkle.svg | 1 + .../assets/about/text-box-check-outline.svg | 1 + client/src/assets/about/web-check.svg | 1 + client/src/assets/lines.svg | 1 + client/src/community/index.scss | 191 +--- client/src/community/index.tsx | 90 +- client/src/telemetry/constants.ts | 1 + libs/constants/index.js | 1 + 33 files changed, 1425 insertions(+), 441 deletions(-) create mode 100644 client/src/about/_mixins.scss create mode 100644 client/src/about/custom-elements.js delete mode 100644 client/src/about/testimonial/index.scss delete mode 100644 client/src/about/testimonial/index.tsx delete mode 100644 client/src/about/testimonial/quote.svg create mode 100644 client/src/assets/about/accurate-sm.svg create mode 100644 client/src/assets/about/accurate.svg create mode 100644 client/src/assets/about/building-dark.svg create mode 100644 client/src/assets/about/building.svg create mode 100644 client/src/assets/about/collaborative-sm.svg create mode 100644 client/src/assets/about/collaborative.svg create mode 100644 client/src/assets/about/dot-dark.svg create mode 100644 client/src/assets/about/dot.svg create mode 100644 client/src/assets/about/education.svg create mode 100644 client/src/assets/about/global-impact-dark.svg create mode 100644 client/src/assets/about/global-impact.svg create mode 100644 client/src/assets/about/handshake.svg create mode 100644 client/src/assets/about/inclusive-sm.svg create mode 100644 client/src/assets/about/inclusive.svg create mode 100644 client/src/assets/about/line-dot.svg create mode 100644 client/src/assets/about/sparkle.svg create mode 100644 client/src/assets/about/text-box-check-outline.svg create mode 100644 client/src/assets/about/web-check.svg create mode 100644 client/src/assets/lines.svg diff --git a/build/spas.ts b/build/spas.ts index 62e52645f654..6d500d2877b6 100644 --- a/build/spas.ts +++ b/build/spas.ts @@ -25,7 +25,12 @@ import { } from "../libs/env/index.js"; import { isValidLocale } from "../libs/locale-utils/index.js"; import { DocFrontmatter, DocParent, NewsItem } from "../libs/types/document.js"; -import { getSlugByBlogPostUrl, makeTOC } from "./utils.js"; +import { + getSlugByBlogPostUrl, + injectLoadingLazyAttributes, + makeTOC, + postProcessExternalLinks, +} from "./utils.js"; import { findByURL } from "../content/document.js"; import { buildDocument } from "./index.js"; import { findPostBySlug } from "./blog.js"; @@ -309,6 +314,8 @@ export async function buildSPAs(options: { }; const [$] = await kumascript.render(url, {}, d); wrapTables($); + postProcessExternalLinks($); + injectLoadingLazyAttributes($); const [sections] = await extractSections($); const toc = makeTOC({ body: sections }); @@ -355,6 +362,7 @@ export async function buildSPAs(options: { OBSERVATORY_TITLE ); await buildStaticPages(path.join(GENERIC_CONTENT_ROOT, "community")); + await buildStaticPages(path.join(GENERIC_CONTENT_ROOT, "about")); } // Build all the home pages in all locales. diff --git a/client/src/about/_mixins.scss b/client/src/about/_mixins.scss new file mode 100644 index 000000000000..eb3522309b7e --- /dev/null +++ b/client/src/about/_mixins.scss @@ -0,0 +1,282 @@ +@use "../ui/vars" as *; +@use "../ui/atoms/button/mixins" as button; + +@mixin theme-setup { + .light & { + @content (light); + } + + .dark & { + @content (dark); + } + + // OS Default. + :root:not(.light):not(.dark) & { + @media (prefers-color-scheme: light) { + @content (light); + } + + @media (prefers-color-scheme: dark) { + @content (dark); + } + } +} + +@mixin layout { + h2, + h3, + p { + margin: 0; + } + + h2, + h3 { + color: var(--layout-text-primary); + + a { + color: unset; + text-decoration: none; + } + } + + a { + text-decoration: underline; + + &:hover { + text-decoration: none; + } + } + + p + p { + margin-top: 1.5rem; + } + + section { + margin-left: auto; + margin-right: auto; + max-width: var(--max-width); + padding-left: var(--gutter); + padding-right: var(--gutter); + width: 100%; + } + + h2 { + font-size: 2rem; + font-weight: 600; + margin-bottom: 1rem; + + @media (max-width: $screen-md) { + font-size: 1.375rem; + } + } +} + +@mixin header { + background: linear-gradient( + to top, + var(--header-next-section-bg, transparent) 0%, + var(--header-next-section-bg, transparent) + calc(var(--header-stats-height) / 2), + var(--header-bg) calc(var(--header-stats-height) / 2), + var(--header-bg) 100% + ); + + @media (max-width: $screen-md) { + padding-top: 1rem; + } + + section { + padding-top: 5rem; + + @media (max-width: $screen-md) { + padding-top: 0; + } + } + + h1 { + color: var(--header-text-primary); + font-size: 2.5rem; + margin-bottom: 1rem; + + @media (max-width: $screen-md) { + font-size: 2rem; + } + } + + p { + color: var(--header-text-secondary); + margin-bottom: 1.5rem; + } + + + section { + margin-top: 4.56rem; + + @media (max-width: $screen-md) { + margin-top: 2rem; + } + } +} + +@mixin stats { + background: var(--stats-bg); + border-radius: 0.5rem; + box-shadow: var(--stats-box-shadow); + color: var(--stats-text-primary); + display: flex; + gap: 1rem; + justify-content: space-around; + margin-top: 5em; + padding: 1rem; + position: relative; + text-align: center; + z-index: 2; + + @media (max-width: $screen-md) { + flex-wrap: wrap; + margin-top: 2rem; + } + + li { + align-items: baseline; + column-gap: 1rem; + display: flex; + flex-wrap: wrap; + justify-content: center; + min-width: 7.75rem; + overflow-wrap: anywhere; + + @media (max-width: $screen-md) { + align-items: center; + flex: 1; + flex-direction: column; + justify-content: flex-start; + } + + strong { + align-items: center; + background: var(--stats-stat-bg); + border-radius: 50%; + color: var(--stats-stat-text); + display: inline-flex; + height: 3.75rem; + justify-content: center; + width: 3.75rem; + } + } +} + +@mixin section { + column-gap: min(5rem, 5vw); + display: grid; + grid-template-columns: 4fr 6fr; + + @media (max-width: $screen-md) { + display: block; + } + + > * { + min-width: 0; + } +} + +@mixin boxes { + display: grid; + gap: 2rem; + grid-auto-rows: 1fr; + grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); + + li { + align-items: center; + background: var(--boxes-bg); + border: 1px solid var(--boxes-border); + border-radius: 0.5rem; + box-shadow: var(--boxes-shadow); + display: flex; + flex-direction: column; + gap: 1.5rem; + justify-content: space-between; + padding: 1.5rem; + text-align: center; + + h3, + h4, + h5, + h6 { + align-self: stretch; + background: var(--boxes-header-bg); + border-radius: 0.5rem 0.5rem 0 0; + color: var(--boxes-header-color); + font-size: 1.25rem; + font-weight: 500; + margin: -1.5rem; + margin-bottom: 0; + padding: 1.5rem; + } + + p { + color: var(--boxes-main-color); + margin: 0; + } + } +} + +@mixin stairs { + --stairs-icon-size: 3.125rem; + --stairs-step-indent: 3.125rem; + --stairs-step-gap: 4rem; + + li { + --stairs-padding-left: calc(var(--stairs-step-indent) * var(--nth-child)); + align-items: center; + display: flex; + gap: 1rem; + padding-left: var(--stairs-padding-left); + position: relative; + + @media (max-width: $screen-md) { + --stairs-step-indent: 0; + --stairs-padding-left: var(--stairs-icon-size); + --stairs-step-gap: 1rem; + } + + &:not(:last-of-type) { + margin-bottom: var(--stairs-step-gap); + } + + &::before { + background: var(--stairs-color); + content: ""; + display: block; + flex-shrink: 0; + height: var(--stairs-icon-size); + margin-left: calc(-1 * var(--stairs-icon-size)); + mask-image: var(--stairs-icon); + mask-position: center; + mask-repeat: no-repeat; + mask-size: 80%; + width: var(--stairs-icon-size); + } + + &:not(:last-of-type)::after { + --height: calc(var(--stairs-step-gap) * 1.2); + background: linear-gradient(to bottom, var(--stairs-color), transparent); + bottom: calc(-1 * var(--height)); + content: ""; + display: block; + height: var(--height); + left: calc(var(--stairs-padding-left) - 50px); + mask: url("../assets/lines.svg"); + mask-position: center; + mask-repeat: no-repeat; + mask-size: contain; + position: absolute; + width: calc(var(--stairs-icon-size) + var(--stairs-step-indent)); + } + } + + @for $i from 1 through 4 { + li:nth-child(#{$i}) { + --nth-child: #{$i}; + } + } +} diff --git a/client/src/about/custom-elements.js b/client/src/about/custom-elements.js new file mode 100644 index 000000000000..35df7bcbb4d9 --- /dev/null +++ b/client/src/about/custom-elements.js @@ -0,0 +1,76 @@ +import { LitElement } from "lit"; + +export class MDNImageHistory extends LitElement { + createRenderRoot() { + return this; + } + + firstUpdated() { + this.renderRoot.querySelectorAll("img").forEach((img) => { + const regex = /@([0-9]+(?:\.[0-9]+)?)(?=x\.[a-z]+$)/; + const match = img.src.match(regex); + if (match?.[1]) { + const baseRes = parseFloat(match[1]); + const dpis = [1, 2]; + img.srcset = dpis + .map( + (dpi) => `${img.src.replace(regex, `@${baseRes * dpi}`)} ${dpi}x` + ) + .join(", "); + } + }); + } +} + +customElements.define("mdn-image-history", MDNImageHistory); + +export class TeamMember extends LitElement { + _setID() { + const hx = this.querySelector("h4, h5"); + const panel = hx?.closest(".tabpanel"); + if (hx && panel) { + const id = `${panel.id.replace("-panel", "")}_${hx.id}`; + if (this.id !== id) { + this.id = id; + } + } + } + + /** @param {FocusEvent} ev */ + _focusin({ currentTarget }) { + if (currentTarget instanceof HTMLElement) { + window.history.pushState({}, "", `#${currentTarget.id}`); + this.scrollIntoView({ block: "nearest", inline: "nearest" }); + } + } + + /** @param {MouseEvent} ev */ + _mousedown(ev) { + if (ev.target instanceof HTMLAnchorElement) { + ev.preventDefault(); + } + } + + createRenderRoot() { + return this; + } + + connectedCallback() { + super.connectedCallback(); + this.tabIndex = 0; + this._setID(); + this.addEventListener("mousedown", this._mousedown); + this.addEventListener("focusin", this._focusin); + if (window.location.hash === `#${this.id}`) { + setTimeout(() => this.focus(), 0); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.removeEventListener("mousedown", this._mousedown); + this.removeEventListener("focusin", this._focusin); + } +} + +customElements.define("team-member", TeamMember); diff --git a/client/src/about/index.scss b/client/src/about/index.scss index 3388f85691c9..4b67acbc5f23 100644 --- a/client/src/about/index.scss +++ b/client/src/about/index.scss @@ -1,55 +1,820 @@ @use "../ui/vars" as *; +@use "../ui/mixins" as *; +@use "../about/mixins" as about; -.about { - width: 100%; +main.about-container { + --about-stats-height: 5.75rem; + --about-section-gap: 5rem; + --max-width: 74rem; + --inner-width: calc(min(var(--max-width), 100vw) - 2 * var(--gutter)); - .about-container { - margin: 0 auto; - max-width: 52rem; - padding: 0 1rem 2rem; + background: var(--about-bg-primary); + color: var(--about-color); - h1 { - margin-top: 3rem; + @media (max-width: $screen-md) { + --about-section-gap: 3rem; + } + + @include about.layout; + + @include about.theme-setup using ($mode) { + @if $mode == light { + --header-next-section-bg: #f2f2f5; + --header-text-primary: #000; + --header-text-secondary: #343434; + --header-bg: #fff; + --stats-bg: #fff; + --stats-box-shadow: 4px -2px 15px 0 rgba(179, 179, 179, 0.2), + 4px -4px 15px 0 rgba(179, 179, 179, 0.15); + --stats-text-primary: #000; + --stats-stat-bg: #d7f5dc; + --stats-stat-text: #007936; + --boxes-bg: #fff; + --boxes-border: none; + --boxes-shadow: -4px 4px 8px 0 rgba(179, 179, 179, 0.15), + 4px 4px 8px 0 rgba(179, 179, 179, 0.18); + --boxes-header-bg-1: #d5e8fb; + --boxes-header-color-1: inherit; + --boxes-header-bg-2: #fbe3d5; + --boxes-header-color-2: inherit; + --boxes-header-bg-3: #fbf7d5; + --boxes-header-color-3: inherit; + --boxes-header-bg-4: #fbd5d5; + --boxes-header-color-4: inherit; + --boxes-header-bg-5: #dfd5fb; + --boxes-header-color-5: inherit; + --boxes-main-color: #000; + --stairs-color: #007936; + --about-bg-primary: #fff; + --about-bg-secondary: #f2f2f5; + --about-join-us-bg: #f2f2f5; + --about-join-us-border: none; + --about-join-us-color: #343434; + --about-join-us-image: url("../assets/about/building.svg"); + --about-tablist-border: #858585; + --about-tablist-color: rgba(0, 0, 0, 0.6); + --about-tablist-active-border: #007936; + --about-tablist-active-color: #000; + --about-color: #000; + --about-heading-color: #000; + --about-core-values-bg: #fff; + --about-core-values-bg-secondary: #d7f5dc; + --about-core-values-color: #000; + --about-core-values-shadow: 0 4px 15px 0 rgba(179, 179, 179, 0.2); + --about-team-title-color: #007936; + --about-team-bg: #fff; + --about-team-github-bg: rgba(255, 255, 255, 0.75); + --about-team-shadow: 0 4px 15px 0 rgba(179, 179, 179, 0.2); + --about-team-color: #000; + --about-journey-line-color: #007936; + --about-journey-image-border: #fff; + --about-journey-shadow: 4px -4px 15px 0 rgba(179, 179, 179, 0.25), + 4px 4px 15px 0 rgba(179, 179, 179, 0.25); + --about-journey-dot: url("../assets/about/dot.svg"); + --about-global-impact-image: url("../assets/about/global-impact.svg"); } - @media (min-width: $screen-md) { - h1 { - font-size: 3.5rem; + @if $mode == dark { + --header-next-section-bg: #1b1b1b; + --header-text-primary: #fff; + --header-text-secondary: #b3b3b3; + --header-bg: #101010; + --stats-bg: #000; + --stats-box-shadow: 4px -2px 15px 0 rgba(38, 38, 38, 0.2), + 4px -4px 15px 0 rgba(38, 38, 38, 0.15); + --stats-text-primary: #b3b3b3; + --stats-stat-bg: #394035; + --stats-stat-text: #8ff295; + --boxes-bg: #000; + --boxes-border: #4e4e4e; + --boxes-shadow: none; + --boxes-header-bg-1: #141e34; + --boxes-header-color-1: #9bb6f2; + --boxes-header-bg-2: #321d13; + --boxes-header-color-2: #e3642a; + --boxes-header-bg-3: #343114; + --boxes-header-color-3: #d4c53b; + --boxes-header-bg-4: #341419; + --boxes-header-color-4: #f19ca1; + --boxes-header-bg-5: #1d1434; + --boxes-header-color-5: #bf94ec; + --boxes-main-color: #b3b3b3; + --stairs-color: #8ff295; + --about-bg-primary: #1b1b1b; + --about-bg-secondary: #1b1b1b; + --about-join-us-bg: #000; + --about-join-us-border: 1px solid #4e4e4e; + --about-join-us-color: #b3b3b3; + --about-join-us-image: url("../assets/about/building-dark.svg"); + --about-tablist-border: #858585; + --about-tablist-color: #b3b3b3; + --about-tablist-active-border: #8ff295; + --about-tablist-active-color: #fff; + --about-color: #b3b3b3; + --about-heading-color: #fff; + --about-core-values-bg: #000; + --about-core-values-bg-secondary: #007936; + --about-core-values-color: #fff; + --about-core-values-shadow: 0 4px 15px 0 rgba(179, 179, 179, 0.2); + --about-team-title-color: #8ff295; + --about-team-bg: #000; + --about-team-github-bg: rgba(0, 0, 0, 0.7); + --about-team-shadow: 0 4px 15px 0 rgba(179, 179, 179, 0.2); + --about-team-color: #fff; + --about-journey-line-color: #8ff295; + --about-journey-image-border: #000; + --about-journey-shadow: 4px -4px 15px 0 rgba(179, 179, 179, 0.25), + 4px 4px 15px 0 rgba(179, 179, 179, 0.25); + --about-journey-dot: url("../assets/about/dot-dark.svg"); + --about-global-impact-image: url("../assets/about/global-impact-dark.svg"); + } + } + + h1, + h2, + h3, + h4, + h5, + h6 { + color: var(--about-heading-color); + letter-spacing: 0; + } + + strong { + letter-spacing: 0; + } + + > header { + --header-stats-height: var(--about-stats-height); + @include about.header; + + h1 + p { + font-size: 2rem; + font-weight: 500; + line-height: 120%; + max-width: 43rem; + + @media (max-width: $screen-md) { + font-size: 1.25rem; } } - header { - align-items: center; - display: flex; - flex-direction: column; - gap: 1rem; - padding: 2rem 0; + ul { + @include about.stats; + } + } + + > section { + --center-padding: max( + calc((100vw - var(--max-width)) / 2 + var(--gutter)), + var(--gutter) + ); + @include about.section; - .headline { - font-family: var(--font-heading); - font-size: 1.313rem; - font-style: normal; - font-variation-settings: normal; - font-weight: normal; - line-height: 175%; - margin: 0; + &[aria-labelledby="who_we_are"] { + background: var(--about-bg-secondary); + margin: 0; + max-width: none; + padding: 4rem var(--center-padding); + } + + .tabs { + grid-column: 1 / -1; + padding-top: 2rem; + position: relative; + + .tablist-wrapper { + background: var(--about-bg-secondary); + margin: 0 calc(-1 * var(--center-padding)); + margin-bottom: 2rem; + padding: 0 var(--center-padding); + position: sticky; + top: var(--top-nav-height); + z-index: 2; + } + + .tablist { + border-bottom: 1px solid var(--about-tablist-border); + display: flex; + gap: 3rem; + overflow-x: auto; + + @media (max-width: $screen-md) { + margin-left: calc(-1 * var(--gutter)); + margin-right: calc(-1 * var(--gutter)); + padding: 0 var(--gutter); + } + + a { + color: var(--about-tablist-color); + flex-shrink: 0; + text-decoration: none; + } + + .active { + border-bottom: 2px solid var(--about-tablist-active-border); + color: var(--about-tablist-active-color); + } + } + + > .tabpanel:not(.active) { + display: none !important; } } - p { - font-size: 1rem; - font-weight: 350; - line-height: 175%; + #what_we_offer-panel { + ul { + @include about.boxes; + + @media (min-width: $screen-md) { + gap: 5rem; + } + + @media (min-width: $screen-lg) { + gap: 5rem 8.5rem; + } + + li { + justify-content: flex-start; + } + + @for $i from 1 through 5 { + li:nth-of-type(#{$i}) { + --boxes-header-bg: var(--boxes-header-bg-#{$i}); + --boxes-header-color: var(--boxes-header-color-#{$i}); + } + } + } } - h2 { - font-size: 1.3rem; + #our_journey-panel { + --image-inner-height: calc(720px * 0.45); + --image-border-size: 4px; + --image-height: calc( + var(--image-inner-height) + var(--image-border-size) * 2 + ); + --image-width: calc( + var(--image-inner-height) * 16 / 9 + var(--image-border-size) * 2 + ); + --dot-height: 3rem; + --separator-width: 10rem; + --list-width: calc( + var(--inner-width) - var(--separator-width) - var(--image-width) + ); + + @media (max-width: $screen-xl) { + --separator-width: 6rem; + } + + > p { + margin-bottom: 1rem; + } + + > ul { + border-right: var(--about-journey-line-color) dashed 0.0625rem; + margin-top: 3rem; + width: var(--list-width); + + @media (max-width: $screen-lg) { + border-left: var(--about-journey-line-color) dashed 0.0625rem; + border-right: none; + margin-left: 1rem; + width: auto; + } + + li { + margin: 8rem 0; + min-height: var(--image-height); + padding-right: 2rem; + position: relative; + + &:first-of-type { + margin-top: 2rem; + } + + &:last-of-type { + margin-bottom: 2rem; + } + + @media (max-width: $screen-lg) { + padding-left: 2rem; + } + + &::after { + // dot on vertical dashed line + background-image: var(--about-journey-dot); + background-position: center center; + background-repeat: no-repeat; + content: ""; + display: block; + height: var(--dot-height); + position: absolute; + right: calc(var(--dot-height) / -2); + top: calc(var(--dot-height) / -2); + width: var(--dot-height); + + @media (max-width: $screen-lg) { + --dot-height: 2rem; + left: calc(var(--dot-height) / -2); + right: auto; + } + } + + &:has([id="2017"], [id="20232024"]) { + min-height: 0; + } + } + + h4 { + font-size: 1.375rem; + font-weight: 600; + line-height: 1.5; + margin: 0; + position: relative; + text-decoration-line: underline; + top: -0.75em; + + @media (max-width: $screen-lg) { + font-size: 1.25rem; + margin-bottom: -0.75em; + } + + &::before { + // horizontal line to dot + background: var(--about-journey-line-color); + content: ""; + display: none; + height: 10px; + left: calc(100% + 2rem); + mask-image: url("../assets/about/line-dot.svg"); + mask-position: center right; + mask-repeat: no-repeat; + mask-size: cover; + position: absolute; + top: calc(0.75em - 5px); + width: calc(var(--separator-width) - var(--dot-height) / 2); + + @media (max-width: $screen-lg) { + content: none; + } + } + + ~ p:last-of-type > img { + // image + background-color: var(--about-journey-image-border); + background-position: center center; + background-repeat: no-repeat; + background-size: contain; + border: var(--image-border-size) solid + var(--about-journey-image-border); + box-shadow: var(--about-journey-shadow); + height: var(--image-height); + left: calc(var(--inner-width) - var(--image-width)); + max-width: var(--image-width); + position: absolute; + top: calc(var(--dot-height) / -2); + width: var(--image-width); + + @media (max-width: $screen-lg) { + aspect-ratio: 16 / 9; + box-sizing: content-box; + height: auto; + left: auto; + margin: 2rem auto; + max-height: var(--image-inner-height); + max-width: 100%; + position: relative; + top: auto; + width: auto; + } + } + + &[id="2005"] { + &::before, + ~ p:last-of-type::after { + display: block; + } + } + + &[id="2010"] { + &::before, + ~ p:last-of-type::after { + display: block; + } + } + + &[id="2020"] { + &::before, + ~ p:last-of-type::after { + display: block; + } + } + + &[id="2022"] { + &::before, + ~ p:last-of-type::after { + display: block; + } + } + } + } + + h4 { + font-size: 1.75rem; + font-weight: 600; + } + + mdn-image-history ul { + display: flex; + gap: 1rem; + margin-bottom: -1rem; + overflow-x: auto; + padding-bottom: 1rem; // space for scrollbar + + li { + flex-shrink: 0; + } + + img { + aspect-ratio: 16 / 9; + border: var(--image-border-size) solid + var(--about-journey-image-border); + box-sizing: content-box; + height: calc(720px / 4); + } + } } - .heading-break { - display: none; - @media (min-width: $screen-md) { - display: block; + #our_core_values-panel { + li { + border-radius: 0.5rem; + color: var(--about-core-values-color); + + h4 { + color: var(--about-core-values-color); + font-size: 1rem; + font-weight: 600; + letter-spacing: -0.03125rem; + margin: 0; + margin-bottom: 0.5rem; + } + + &:not(:last-of-type) { + background: var(--about-core-values-bg-secondary); + margin: 1rem auto; + padding: 2rem; + + h4 { + --icon-size: 45px; + align-items: center; + background-position: left center; + background-repeat: no-repeat; + background-size: var(--icon-size) var(--icon-size); + display: flex; + min-height: var(--icon-size); + padding-left: calc(var(--icon-size) + 1rem); + + &#accurate_and_reliable { + background-image: url("../assets/about/accurate-sm.svg"); + } + + &#collaborative_and_community-driven { + background-image: url("../assets/about/collaborative-sm.svg"); + } + + &#inclusive_and_dynamic { + background-image: url("../assets/about/inclusive-sm.svg"); + } + } + } + + &:last-of-type { + background: var(--about-core-values-bg); + margin-top: 2rem; + padding: 2rem; + } + + @media (min-width: $screen-lg) { + display: grid; + grid-auto-flow: dense; + grid-template-columns: [left-start] 40% [left-end right-start] 1fr [right-end]; + padding: 2rem; + + h4 { + font-size: 1.75rem; + margin: 0; + max-width: 70%; + } + + p { + margin-left: var(--gutter); + } + + &:not(:last-of-type) { + background: var(--about-core-values-bg); + margin: 0 auto; + + &:not(:first-of-type) { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + &:not(:nth-last-of-type(2)) { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + + h4 { + --icon-size: 60px; + + align-self: center; + + &#accurate_and_reliable { + background-image: url("../assets/about/accurate.svg"); + } + + &#collaborative_and_community-driven { + background-image: url("../assets/about/collaborative.svg"); + } + + &#inclusive_and_dynamic { + background-image: url("../assets/about/inclusive.svg"); + } + } + + p { + background: var(--about-core-values-bg-secondary); + border-radius: 0.5rem; + padding: 2rem; + } + } + + &:nth-of-type(even):not(:last-of-type) { + h4 { + grid-column: right; + margin: 0 auto; + } + + p { + grid-column: left; + margin-left: 0; + margin-right: var(--gutter); + } + } + + &:last-of-type { + margin-top: 4rem; + } + } + } + } + + #our_team-panel, + #our_partners-panel > div { + --team-grid-gap: 2.5rem; + --team-card-padding: 1.5rem; + + display: grid; + gap: 0 var(--team-grid-gap); + grid-auto-flow: dense; + grid-template-columns: [full-start] 1fr 1fr 1fr [full-end]; + + @media (max-width: $screen-lg) { + --team-grid-gap: 2rem; + grid-template-columns: [full-start] 1fr 1fr [full-end]; + } + + > * { + margin: calc(var(--team-grid-gap) / 2) 0; + scroll-margin-top: calc( + var(--sticky-header-without-actions-height) + 1.5rem + 1rem + ); + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + > h4, + p { + grid-column: full; + } + + h4, + h5 { + font-size: 1.75rem; + font-weight: 600; + text-transform: none; + + @media (max-width: $screen-md) { + font-size: 1rem; + } + } + + team-member { + align-content: start; + background: var(--about-team-bg); + border: 1px solid var(--about-team-bg); + box-shadow: var(--about-team-shadow); + color: var(--about-team-color); + cursor: pointer; + display: grid; + gap: 0; + grid-row: span 5; + grid-template-areas: + "name" + "title" + "picture" + "bio" + "."; + grid-template-rows: subgrid; + padding: calc(var(--team-card-padding) - 1px); + + @media (max-width: $screen-sm) { + grid-column: full; + } + + &:hover { + border-color: var(--text-inactive); + } + + h4, + h5 { + grid-area: name; + margin: 0; + margin-bottom: 0.5rem; + } + + ul { + display: contents; + } + + li:first-of-type { + color: var(--about-team-title-color); + font-weight: 600; + grid-area: title; + margin-bottom: 0.75rem; + } + + li:nth-of-type(2) { + grid-area: picture; + } + + li:nth-of-type(3):not(:last-of-type) { + align-self: end; + grid-area: picture; + } + + li:last-of-type { + grid-area: bio; + line-height: 1.75; + @include line-clamp(3, 1.75, var(--about-team-bg)); + } + + img { + aspect-ratio: 1; + margin-bottom: 0.75rem; + width: 100%; + } + + a[href^="https://github.com"] + { + background: var(--about-team-github-bg); + border-top-right-radius: 0.5rem; + color: var(--about-team-color); + margin-top: -2.9rem; + padding: 0.25rem 0.5rem; + padding-right: 0.7rem; + position: absolute; + + &::before { + background: var(--about-team-color); + content: ""; + display: inline-block; + height: 1.2em; + margin-bottom: 0.2em; + margin-right: 0.2em; + mask-image: url("../assets/icons/github-mark-small.svg"); + mask-repeat: no-repeat; + vertical-align: middle; + width: 1.2em; + } + } + + &:focus-within { + align-content: start; + cursor: unset; + display: grid; + gap: 0 var(--team-card-padding); + grid-column: span 2; + grid-template-areas: + "name name" + "title title" + "picture bio" + ". bio" + ". bio"; + grid-template-columns: + calc( + (100% - var(--team-grid-gap) - 2 * var(--team-card-padding)) / 2 + ) + 1fr; + + li:last-of-type { + max-height: unset; + + &::after { + display: none; + } + } + + @media (max-width: $screen-sm) { + grid-template-areas: + "name" + "title" + "picture" + "bio"; + grid-template-columns: 1fr; + + ul li:nth-of-type(3):not(:last-of-type) { + align-self: end; + grid-area: picture; + + a { + position: absolute; + } + } + } + } + } + } + + #our_partners-panel > div { + margin-top: 2.5rem; + } + + &[aria-labelledby="global_impact"] { + display: block; + margin: var(--about-section-gap) auto; + + @media (min-width: $screen-lg) { + .section-content { + background: var(--about-global-impact-image); + background-position: right; + background-repeat: no-repeat; + background-size: 20rem 100%; + padding-right: 30rem; + } + } + + ul { + margin-top: 1rem; + @include about.stairs; + + li:nth-child(1) { + --stairs-icon: url("../assets/about/education.svg"); + } + + li:nth-child(2) { + --stairs-icon: url("../assets/about/text-box-check-outline.svg"); + } + + li:nth-child(3) { + --stairs-icon: url("../assets/about/web-check.svg"); + } + + li:nth-child(4) { + --stairs-icon: url("../assets/about/handshake.svg"); + } + } + } + + &[aria-labelledby="join_us_in_building_a_better_web"] { + background: var(--about-join-us-image) var(--about-join-us-bg); + background-position: 1.5rem; + background-repeat: no-repeat; + background-size: 20rem calc(100% - 3rem); + border: var(--about-join-us-border); + border-radius: 0.5rem; + color: var(--about-join-us-color); + display: flex; + flex-direction: column; + justify-content: center; + margin-bottom: var(--about-section-gap); + max-width: calc(100% - 2 * var(--center-padding)); + padding: 4rem 1rem; + padding-left: min(30rem, 50%); + + @media (max-width: $screen-md) { + background-color: transparent; + background-position: center bottom; + background-size: 100% 10rem; + border: none; + padding: 1rem 0; + padding-bottom: 12rem; } } } diff --git a/client/src/about/index.tsx b/client/src/about/index.tsx index 760c7bc0d3d5..92055d8c82f2 100644 --- a/client/src/about/index.tsx +++ b/client/src/about/index.tsx @@ -1,74 +1,201 @@ -import { useLocale } from "../hooks"; -import { GetInvolved } from "../ui/molecules/get_involved"; +import { HydrationData } from "../../../libs/types/hydration"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { ProseSection } from "../../../libs/types/document"; +import useSWR from "swr"; +import { HTTPError } from "../document"; +import { WRITER_MODE } from "../env"; +import { Prose } from "../document/ingredients/prose"; + import "./index.scss"; +import "./custom-elements"; +import { useGleanClick } from "../telemetry/glean-context"; +import { ABOUT } from "../telemetry/constants"; + +export interface AboutSection extends ProseSection { + H3s?: AboutSection[]; +} + +export interface AboutDoc { + title: string; + sections: AboutSection[]; +} + +export function About(appProps: HydrationData) { + const doc = useAboutDoc(appProps); -export function About() { - const locale = useLocale(); return ( -
-
-

Build it better

-

- MDN Web Docs is an open-source, collaborative project documenting Web - platform technologies, including{" "} - CSS,{" "} - HTML,{" "} - JavaScript, and{" "} - Web APIs. We also provide an - extensive set of{" "} - learning resources for beginning - developers and students. -

+
+ { + if (i === 0) { + return
; + } else if (section.H3s) { + return ; + } + return null; + }} + /> +
+ ); +} + +export function useAboutDoc( + appProps?: HydrationData +): AboutDoc | undefined { + const { data } = useSWR( + "index.json", + async () => { + const url = new URL( + `${window.location.pathname.replace(/\/$/, "")}/index.json`, + window.location.origin + ).toString(); + const response = await fetch(url); + + if (!response.ok) { + switch (response.status) { + case 404: + throw new HTTPError(response.status, url, "Page not found"); + } + + const text = await response.text(); + throw new HTTPError(response.status, url, text); + } + + return (await response.json())?.hyData; + }, + { + fallbackData: appProps?.hyData, + revalidateOnFocus: WRITER_MODE, + revalidateOnMount: true, + } + ); + const doc: AboutDoc | undefined = data || appProps?.hyData || undefined; + return doc; +} + +function RenderAboutBody({ + doc, + renderer = () => null, +}: { + doc?: AboutDoc; + renderer?: (section: AboutSection, i: number) => null | JSX.Element; +}) { + const sections = Array.from(doc?.sections || []).reduce( + (acc, curr) => { + if (curr.value.isH3) { + const prev = acc.at(-1); + if (prev) { + prev.H3s ? prev.H3s.push(curr) : (prev.H3s = [curr]); + } + } else { + acc.push(Object.assign({}, curr)); + } + return acc; + }, + [] + ); + return sections.map((section, i) => { + return ( + renderer(section, i) || ( + + ) + ); + }); +} + +export function Header({ section }: { section: AboutSection }) { + return section.value.content ? ( +
+
+
+ ) : null; +} + +function Tabs({ section }: { section: AboutSection }) { + const [activeTab, setActiveTab] = useState(0); + const tabs = useRef(null); + const gleanClick = useGleanClick(); + + const changeTab = useCallback( + (i: number) => { + const id = section.H3s?.[i].value.id; + if (id) { + setActiveTab(i); + if (tabs.current && tabs.current.getBoundingClientRect().top < 0) { + tabs.current.scrollIntoView({ block: "start", inline: "nearest" }); + } + gleanClick(`${ABOUT}: tab -> ${id}`); + } + }, + [section.H3s, gleanClick] + ); + + useEffect(() => { + const hash = document.location.hash.startsWith("#our_team") + ? "#our_team" + : document.location.hash.startsWith("#our_partners") || + document.location.hash === "#pab" || + document.location.hash === "#owd" + ? "#our_partners" + : document.location.hash; + const tab = section.H3s?.findIndex(({ value }) => `#${value.id}` === hash); + if (tab && tab > 0) { + setActiveTab(tab); + } + }, [section.H3s, changeTab]); -
- - - MDN's mission is to{" "} - provide a blueprint for a better internet and empower a new - generation of developers and content creators to build it. - - -
-

- We're always striving to connect developers more seamlessly with the - tools and information they need to easily build projects on the{" "} - open Web. Since our beginnings in 2005, Mozilla and - the community have amassed around 45,000 pages of free, open-source - content. -

- {/**/} -

- Independent and unbiased - across browsers and technologies -

-

- This guiding principle has made MDN Web Docs the go-to repository of - independent information for developers, regardless of brand, browser - or platform. We are an open community of devs, writers, and other - technologists building resources for a better Web, with over 17 - million monthly MDN users from all over the world. Anyone can - contribute, and each of the 45,000 individuals who have done so over - the past decades has strengthened and improved the resource. We also - receive content contributions from our partners, including Microsoft, - Google, Samsung, Igalia, W3C and others. Together we continue to drive - innovation on the Web and serve the common good. -

-

Accurate and vetted for quality

-

- Through our GitHub documentation repository, contributors can make - changes, submit pull requests, have their contributions reviewed and - then merged with existing content. Through{" "} - - this workflow - - , we welcome the vast knowledge and experience of our developer - community while maintaining a high level of quality, accurate content. -

-
- -
+ return ( + section.value.id && + section.value.content && ( +
+

{section.value.title}

+
+ {section.H3s && ( +
+
+
+ {section.H3s?.map( + ({ value }, i) => + value.id && + value.content && ( + changeTab(i)} + > + {value.title} + + ) + )} +
+
+ {section.H3s?.map( + ({ value }, i) => + value.id && + value.content && ( +
+ ) + )} +
+ )} +
+ ) ); } diff --git a/client/src/about/testimonial/index.scss b/client/src/about/testimonial/index.scss deleted file mode 100644 index 2a245a2bb3e9..000000000000 --- a/client/src/about/testimonial/index.scss +++ /dev/null @@ -1,64 +0,0 @@ -@use "../../ui/vars" as *; - -.testimonial { - display: flex; - flex-direction: column; - gap: 2rem; - padding: 2rem 0; - width: 100%; - - @media (min-width: $screen-md) { - flex-direction: row; - } - - iframe { - height: 50vw; - padding: 0; - - @media (min-width: $screen-md) { - height: initial; - width: 48%; - } - } - - .testimonial-copy { - margin: 0 auto; - max-width: 18rem; - text-align: center; - - @media (min-width: $screen-md) { - margin: initial; - text-align: initial; - } - - img { - margin: 0 auto; - - @media (min-width: $screen-md) { - margin: initial; - } - } - - p { - font-size: 18px; - line-height: 120%; - } - - .author-name, - .author-title { - display: block; - /* MDN UI / Body / M */ - font-size: 13px; - line-height: 1.2; - } - } - - .quotation-mark { - background-color: var(--icon-primary); - height: 10px; - margin-left: 4px; - mask-image: url("./quote.svg"); - mask-size: cover; - width: 10px; - } -} diff --git a/client/src/about/testimonial/index.tsx b/client/src/about/testimonial/index.tsx deleted file mode 100644 index 9d0695f0ee92..000000000000 --- a/client/src/about/testimonial/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import "./index.scss"; -import { ReactComponent as Quote } from "./quote.svg"; - -export function Testimonial() { - return ( -
- -
- -

- Condimentum donec quam odio viverra erat mi mae-cenas odio. Tempus - arcu tincidunt tortor placerat tempor pharetra. -

- - First Name - - Title goes here -
-
- ); -} diff --git a/client/src/about/testimonial/quote.svg b/client/src/about/testimonial/quote.svg deleted file mode 100644 index 19dcfe170995..000000000000 --- a/client/src/about/testimonial/quote.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/client/src/app.scss b/client/src/app.scss index eb5427c31dbf..6281566b0080 100644 --- a/client/src/app.scss +++ b/client/src/app.scss @@ -157,7 +157,6 @@ main { min-height: 80vh; } -.about-container, .main-page-content { a { &:link, diff --git a/client/src/app.tsx b/client/src/app.tsx index 001c73bba834..e452d6c89a3f 100644 --- a/client/src/app.tsx +++ b/client/src/app.tsx @@ -322,7 +322,7 @@ export function App(appProps: HydrationData) { path="/about/*" element={ - + } /> diff --git a/client/src/assets/about/accurate-sm.svg b/client/src/assets/about/accurate-sm.svg new file mode 100644 index 000000000000..1bfb970504aa --- /dev/null +++ b/client/src/assets/about/accurate-sm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/accurate.svg b/client/src/assets/about/accurate.svg new file mode 100644 index 000000000000..a8fc0113c5ea --- /dev/null +++ b/client/src/assets/about/accurate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/building-dark.svg b/client/src/assets/about/building-dark.svg new file mode 100644 index 000000000000..76e77083c4d1 --- /dev/null +++ b/client/src/assets/about/building-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/building.svg b/client/src/assets/about/building.svg new file mode 100644 index 000000000000..848d7e6395b6 --- /dev/null +++ b/client/src/assets/about/building.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/collaborative-sm.svg b/client/src/assets/about/collaborative-sm.svg new file mode 100644 index 000000000000..09cbf91b4ea6 --- /dev/null +++ b/client/src/assets/about/collaborative-sm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/collaborative.svg b/client/src/assets/about/collaborative.svg new file mode 100644 index 000000000000..56b62cce0fd3 --- /dev/null +++ b/client/src/assets/about/collaborative.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/dot-dark.svg b/client/src/assets/about/dot-dark.svg new file mode 100644 index 000000000000..15aed46ddf1a --- /dev/null +++ b/client/src/assets/about/dot-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/dot.svg b/client/src/assets/about/dot.svg new file mode 100644 index 000000000000..9af28548b671 --- /dev/null +++ b/client/src/assets/about/dot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/education.svg b/client/src/assets/about/education.svg new file mode 100644 index 000000000000..8d4393d8497a --- /dev/null +++ b/client/src/assets/about/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/global-impact-dark.svg b/client/src/assets/about/global-impact-dark.svg new file mode 100644 index 000000000000..f51d35856b88 --- /dev/null +++ b/client/src/assets/about/global-impact-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/global-impact.svg b/client/src/assets/about/global-impact.svg new file mode 100644 index 000000000000..96c6848942b1 --- /dev/null +++ b/client/src/assets/about/global-impact.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/handshake.svg b/client/src/assets/about/handshake.svg new file mode 100644 index 000000000000..680d0ee30401 --- /dev/null +++ b/client/src/assets/about/handshake.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/inclusive-sm.svg b/client/src/assets/about/inclusive-sm.svg new file mode 100644 index 000000000000..d85a3e471d74 --- /dev/null +++ b/client/src/assets/about/inclusive-sm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/inclusive.svg b/client/src/assets/about/inclusive.svg new file mode 100644 index 000000000000..db690b38296d --- /dev/null +++ b/client/src/assets/about/inclusive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/line-dot.svg b/client/src/assets/about/line-dot.svg new file mode 100644 index 000000000000..894138b85160 --- /dev/null +++ b/client/src/assets/about/line-dot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/sparkle.svg b/client/src/assets/about/sparkle.svg new file mode 100644 index 000000000000..87bdd6cb9b55 --- /dev/null +++ b/client/src/assets/about/sparkle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/text-box-check-outline.svg b/client/src/assets/about/text-box-check-outline.svg new file mode 100644 index 000000000000..af670b7d9c92 --- /dev/null +++ b/client/src/assets/about/text-box-check-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/about/web-check.svg b/client/src/assets/about/web-check.svg new file mode 100644 index 000000000000..2d5f0e3c2e0e --- /dev/null +++ b/client/src/assets/about/web-check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/lines.svg b/client/src/assets/lines.svg new file mode 100644 index 000000000000..20189e58c466 --- /dev/null +++ b/client/src/assets/lines.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/community/index.scss b/client/src/community/index.scss index e82e3e6b4645..e73d9749105f 100644 --- a/client/src/community/index.scss +++ b/client/src/community/index.scss @@ -1,5 +1,6 @@ @use "../ui/vars" as *; @use "../ui/atoms/button/mixins" as button; +@use "../about/mixins" as about; @mixin light-theme { --community-bg-primary: #fcfcfc; @@ -76,71 +77,22 @@ main.community-container { --community-stats-height: 5.75rem; --community-section-gap: 5rem; --max-width: 74rem; - --negative-space: calc( - max(0px, 100vw - var(--max-width)) * -0.5 - var(--gutter) - ); + --layout-text-primary: var(--community-text-primary); background: var(--community-bg-secondary); color: var(--community-text-secondary); - h2, - h3, - p { - margin: 0; - } - - h2, - h3 { - color: var(--community-text-primary); - - a { - color: unset; - text-decoration: none; - } - } - - a { - text-decoration: underline; - - &:hover { - text-decoration: none; - } - } - - p + p { - margin-top: 1.5rem; - } - - section { - margin-left: auto; - margin-right: auto; - max-width: var(--max-width); - padding-left: var(--gutter); - padding-right: var(--gutter); - width: 100%; - } - - h2 { - font-size: 2rem; - font-weight: 600; - margin-bottom: 1rem; - - @media (max-width: $screen-md) { - font-size: 1.375rem; - } - } + @include about.layout; > header { - background: linear-gradient( - to top, - transparent 0%, - transparent calc(var(--community-stats-height) / 2), - var(--community-bg-primary) calc(var(--community-stats-height) / 2), - var(--community-bg-primary) 100% - ); + --header-text-primary: var(--community-text-primary); + --header-text-secondary: var(--community-header-text); + --header-stats-height: var(--community-stats-height); + --header-bg: var(--community-bg-primary); + + @include about.header; @media (max-width: $screen-md) { - padding-top: 1rem; text-align: center; } @@ -150,7 +102,6 @@ main.community-container { right; background-repeat: no-repeat; background-size: 50%; - padding-top: var(--community-section-gap); @media (max-width: $screen-md) { background-position: top center; @@ -159,21 +110,6 @@ main.community-container { } } - h1 { - color: var(--community-text-primary); - font-size: 2.5rem; - margin-bottom: 1rem; - - @media (max-width: $screen-md) { - font-size: 2rem; - } - } - - p { - color: var(--community-header-text); - margin-bottom: 1.5rem; - } - ul:first-of-type { display: flex; flex-wrap: wrap; @@ -194,73 +130,24 @@ main.community-container { } ul:last-of-type { - background: var(--community-card-bg); - border-radius: 0.5rem; - box-shadow: var(--community-box-shadow); - color: var(--community-text-primary); - display: flex; - gap: 1rem; - justify-content: space-around; - margin-top: var(--community-section-gap); - padding: 1rem; - - @media (max-width: $screen-md) { - flex-wrap: wrap; - margin-top: 2rem; - } - - li { - align-items: baseline; - column-gap: 1rem; - display: flex; - flex-wrap: wrap; - justify-content: center; - min-width: 7.75rem; - overflow-wrap: anywhere; - - @media (max-width: $screen-md) { - align-items: center; - flex: 1; - flex-direction: column; - justify-content: flex-start; - } - - strong { - align-items: center; - background: var(--community-header-stats-bg); - border-radius: 50%; - color: var(--community-text-success); - display: inline-flex; - height: 3.75rem; - justify-content: center; - width: 3.75rem; - } - } - } - } + --stats-bg: var(--community-card-bg); + --stats-box-shadow: var(--community-box-shadow); + --stats-text-primary: var(--community-text-primary); + --stats-stat-bg: var(--community-header-stats-bg); + --stats-stat-text: var(--community-text-success); - > header + section { - margin-top: 4.56rem; - - @media (max-width: $screen-md) { - margin-top: 2rem; + @include about.stats; } } > section { --community-circle-height: 57rem; - column-gap: min(5rem, 5vw); - display: grid; - grid-template-columns: 4fr 6fr; + + @include about.section; @media (max-width: $screen-md) { /* stylelint-disable-next-line length-zero-no-unit */ --community-circle-height: 0rem; - display: block; - } - - > * { - min-width: 0; } &[aria-labelledby="meet_our_contributors"] { @@ -492,6 +379,7 @@ main.community-container { &[aria-labelledby="join_us_in_shaping_a_better_web"] { display: block; + margin-bottom: var(--community-section-gap); margin-top: var(--community-section-gap); p { @@ -499,43 +387,16 @@ main.community-container { } ul { - display: grid; - gap: 2rem; - grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); - margin-bottom: var(--community-section-gap); - } + --boxes-bg: var(--community-card-bg); + --boxes-border: var(--community-card-border); + --boxes-shadow: var(--community-box-shadow); + --boxes-header-bg: var(--community-card-header-bg); - li { - align-items: center; - background: var(--community-card-bg); - border: 1px solid var(--community-card-border); - border-radius: 0.5rem; - box-shadow: var(--community-box-shadow); - display: flex; - flex-direction: column; - gap: 1.5rem; - justify-content: space-between; - padding: 1.5rem; - text-align: center; - - h3 { - align-self: stretch; - background: var(--community-card-header-bg); - border-radius: 0.5rem 0.5rem 0 0; - font-size: 1.25rem; - font-weight: 500; - margin: -1.5rem; - margin-bottom: 0; - padding: 1.5rem; - } - - p { - margin: 0; - } + @include about.boxes; + } - a { - @include button.primary; - } + li a { + @include button.primary; } } diff --git a/client/src/community/index.tsx b/client/src/community/index.tsx index 26b9e6526f81..e2290d123a52 100644 --- a/client/src/community/index.tsx +++ b/client/src/community/index.tsx @@ -1,21 +1,14 @@ import "./index.scss"; import { HydrationData } from "../../../libs/types/hydration"; -import { useEffect, useMemo } from "react"; -import { Section } from "../../../libs/types/document"; +import { useEffect } from "react"; import useSWR, { SWRConfig } from "swr"; -import { HTTPError } from "../document"; -import { WRITER_MODE } from "../env"; import { Prose } from "../document/ingredients/prose"; import { SWRLocalStorageCache } from "../utils"; import { useIsServer } from "../hooks"; +import { AboutDoc, AboutSection, Header, useAboutDoc } from "../about"; -interface CommunityDoc { - title: string; - sections: Section[]; -} - -export function Community(appProps: HydrationData) { - const doc = useCommunityDoc(appProps); +export function Community(appProps: HydrationData) { + const doc = useAboutDoc(appProps); useEffect(() => { import("./contributor-list"); @@ -25,18 +18,12 @@ export function Community(appProps: HydrationData) { new SWRLocalStorageCache("community") }} > -
+
{ if (i === 0) { - return ( -
- ); + return
; } else if (section.value.id === "help_us_fix_open_issues") { return ; } @@ -48,46 +35,12 @@ export function Community(appProps: HydrationData) { ); } -function useCommunityDoc( - appProps?: HydrationData -): CommunityDoc | undefined { - const { data } = useSWR( - "index.json", - async () => { - const url = new URL( - `${window.location.pathname.replace(/\/$/, "")}/index.json`, - window.location.origin - ).toString(); - const response = await fetch(url); - - if (!response.ok) { - switch (response.status) { - case 404: - throw new HTTPError(response.status, url, "Page not found"); - } - - const text = await response.text(); - throw new HTTPError(response.status, url, text); - } - - return (await response.json())?.hyData; - }, - { - fallbackData: appProps?.hyData, - revalidateOnFocus: WRITER_MODE, - revalidateOnMount: true, - } - ); - const doc: CommunityDoc | undefined = data || appProps?.hyData || undefined; - return doc; -} - function RenderCommunityBody({ doc, renderer = () => null, }: { - doc?: CommunityDoc; - renderer?: (section: Section, i: number) => null | JSX.Element; + doc?: AboutDoc; + renderer?: (section: AboutSection, i: number) => null | JSX.Element; }) { return doc?.sections.map((section, i) => { return ( @@ -98,23 +51,7 @@ function RenderCommunityBody({ }); } -function Header({ section, h1 }: { section: any; h1?: string }) { - const html = useMemo( - () => ({ __html: section.value?.content }), - [section.value?.content] - ); - return ( -
-
-
- ); -} - -function Issues({ section }: { section: any }) { - const html = useMemo( - () => ({ __html: section.value?.content }), - [section.value?.content] - ); +function Issues({ section }: { section: AboutSection }) { const isServer = useIsServer(); const LABELS = ["good first issue", "accepting PR"]; const { data } = useSWR( @@ -134,10 +71,13 @@ function Issues({ section }: { section: any }) { revalidateOnFocus: false, } ); - return ( + return section.value.id && section.value.content ? (

{section.value.title}

-
+
@@ -184,5 +124,5 @@ function Issues({ section }: { section: any }) {
- ); + ) : null; } diff --git a/client/src/telemetry/constants.ts b/client/src/telemetry/constants.ts index 1f6735dd726a..c2942fe6f269 100644 --- a/client/src/telemetry/constants.ts +++ b/client/src/telemetry/constants.ts @@ -29,6 +29,7 @@ export const SETTINGS = "settings"; export const OBSERVATORY = "observatory"; export const CURRICULUM = "curriculum"; export const BCD_TABLE = "bcd"; +export const ABOUT = "about"; export const A11Y_MENU = "a11y_menu"; diff --git a/libs/constants/index.js b/libs/constants/index.js index 8b0da8d3aea8..7dda3d74fac1 100644 --- a/libs/constants/index.js +++ b/libs/constants/index.js @@ -164,6 +164,7 @@ export const CSP_DIRECTIVES = { // Shared assets. "https://mdn.github.io/shared-assets/", + "https://mdn.dev/", // GA4. "https://*.google-analytics.com",