diff --git a/astro/astro.config.mjs b/astro/astro.config.mjs index 3441315e48..6bf48350f7 100644 --- a/astro/astro.config.mjs +++ b/astro/astro.config.mjs @@ -2,14 +2,22 @@ import { defineConfig } from 'astro/config'; import mdx from '@astrojs/mdx'; import icon from 'astro-icon'; +import expressiveCode from 'astro-expressive-code'; // https://astro.build/config export default defineConfig({ site: 'https://expressjs.com', - integrations: [mdx(), icon()], - markdown: { - shikiConfig: { - theme: 'github-dark', - }, - }, + integrations: [ + expressiveCode({ + themes: ['github-dark'], + styleOverrides: { + uiFontSize: 'var(--font-size-sm)', + codeFontSize: 'var(--font-size-sm)', + borderRadius: 'var(--radius-base)', + borderWidth: 'var(--border-width-1)', + }, + }), + mdx(), + icon(), + ], }); diff --git a/astro/package-lock.json b/astro/package-lock.json index aa3d7adfca..1813b831f9 100644 --- a/astro/package-lock.json +++ b/astro/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@astrojs/mdx": "^4.3.13", "astro": "^5.18.0", + "astro-expressive-code": "^0.41.7", "astro-icon": "^1.1.5" }, "devDependencies": { @@ -321,6 +322,14 @@ "postcss": "^8.4" } }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "engines": { + "node": ">=14" + } + }, "node_modules/@emnapi/runtime": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", @@ -891,6 +900,83 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@expressive-code/core": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz", + "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==", + "dependencies": { + "@ctrl/tinycolor": "^4.0.4", + "hast-util-select": "^6.0.2", + "hast-util-to-html": "^9.0.1", + "hast-util-to-text": "^4.0.1", + "hastscript": "^9.0.0", + "postcss": "^8.4.38", + "postcss-nested": "^6.0.1", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1" + } + }, + "node_modules/@expressive-code/core/node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/@expressive-code/core/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@expressive-code/plugin-frames": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.7.tgz", + "integrity": "sha512-diKtxjQw/979cTglRFaMCY/sR6hWF0kSMg8jsKLXaZBSfGS0I/Hoe7Qds3vVEgeoW+GHHQzMcwvgx/MOIXhrTA==", + "dependencies": { + "@expressive-code/core": "^0.41.7" + } + }, + "node_modules/@expressive-code/plugin-shiki": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.7.tgz", + "integrity": "sha512-DL605bLrUOgqTdZ0Ot5MlTaWzppRkzzqzeGEu7ODnHF39IkEBbFdsC7pbl3LbUQ1DFtnfx6rD54k/cdofbW6KQ==", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "shiki": "^3.2.2" + } + }, + "node_modules/@expressive-code/plugin-text-markers": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.7.tgz", + "integrity": "sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==", + "dependencies": { + "@expressive-code/core": "^0.41.7" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -2833,6 +2919,17 @@ "url": "https://github.com/sponsors/ota-meshi" } }, + "node_modules/astro-expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.7.tgz", + "integrity": "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ==", + "dependencies": { + "rehype-expressive-code": "^0.41.7" + }, + "peerDependencies": { + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" + } + }, "node_modules/astro-icon": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/astro-icon/-/astro-icon-1.1.5.tgz", @@ -3388,6 +3485,15 @@ "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", "license": "MIT" }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -3869,6 +3975,21 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-selector-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz", + "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, "node_modules/css-tree": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", @@ -4144,6 +4265,18 @@ "node": ">=0.3.1" } }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -4939,6 +5072,17 @@ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, + "node_modules/expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.7.tgz", + "integrity": "sha512-2wZjC8OQ3TaVEMcBtYY4Va3lo6J+Ai9jf3d4dbhURMJcU4Pbqe6EcHe424MIZI0VHUA1bR6xdpoHYi3yxokWqA==", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "@expressive-code/plugin-frames": "^0.41.7", + "@expressive-code/plugin-shiki": "^0.41.7", + "@expressive-code/plugin-text-markers": "^0.41.7" + } + }, "node_modules/exsolve": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", @@ -5528,6 +5672,18 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-is-element": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", @@ -5579,6 +5735,32 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-select": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", + "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "nth-check": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-estree": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", @@ -5676,6 +5858,18 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-text": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", @@ -8756,6 +8950,14 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/rehype-expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.7.tgz", + "integrity": "sha512-25f8ZMSF1d9CMscX7Cft0TSQIqdwjce2gDOvQ+d/w0FovsMwrSt3ODP4P3Z7wO1jsIJ4eYyaDRnIR/27bd/EMQ==", + "dependencies": { + "expressive-code": "^0.41.7" + } + }, "node_modules/rehype-parse": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", @@ -10315,7 +10517,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, "node_modules/vfile": { diff --git a/astro/package.json b/astro/package.json index fec4a06e30..1841b05bd7 100644 --- a/astro/package.json +++ b/astro/package.json @@ -17,6 +17,7 @@ "dependencies": { "@astrojs/mdx": "^4.3.13", "astro": "^5.18.0", + "astro-expressive-code": "^0.41.7", "astro-icon": "^1.1.5" }, "devEngines": { diff --git a/astro/public/hero-poster-light.jpg b/astro/public/hero-poster-light.jpg new file mode 100644 index 0000000000..135b4be964 Binary files /dev/null and b/astro/public/hero-poster-light.jpg differ diff --git a/astro/public/hero-poster.jpg b/astro/public/hero-poster.jpg new file mode 100644 index 0000000000..b6709c7605 Binary files /dev/null and b/astro/public/hero-poster.jpg differ diff --git a/astro/public/logo-express-black.svg b/astro/public/logo-express-black.svg new file mode 100644 index 0000000000..d9dace678c --- /dev/null +++ b/astro/public/logo-express-black.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/astro/public/logo-express-white.svg b/astro/public/logo-express-white.svg new file mode 100644 index 0000000000..6b14ccf672 --- /dev/null +++ b/astro/public/logo-express-white.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/astro/public/videos/hero-background-light.mp4 b/astro/public/videos/hero-background-light.mp4 new file mode 100644 index 0000000000..e20340f1ea Binary files /dev/null and b/astro/public/videos/hero-background-light.mp4 differ diff --git a/astro/public/videos/hero-background.mp4 b/astro/public/videos/hero-background.mp4 new file mode 100644 index 0000000000..fcf4b00df0 Binary files /dev/null and b/astro/public/videos/hero-background.mp4 differ diff --git a/astro/src/components/patterns/Hero/Hero.astro b/astro/src/components/patterns/Hero/Hero.astro new file mode 100644 index 0000000000..016561dbed --- /dev/null +++ b/astro/src/components/patterns/Hero/Hero.astro @@ -0,0 +1,271 @@ +--- +import './Hero.css'; +import { Image } from 'astro:assets'; +import { Code } from 'astro-expressive-code/components'; +import { Icon } from 'astro-icon/components'; +import { H1, Button, Container, Flex, BodyMd, Grid, Col } from '@/components/primitives'; +import { getEntry } from 'astro:content'; +import { getLangFromUrl, useTranslations } from '@/i18n/utils'; + +const lang = getLangFromUrl(Astro.url); +const t = useTranslations(lang); + +const expressPackage = await getEntry('npm', 'express'); +const EXPRESS_VERSION = expressPackage?.data.version ?? '5.x'; +const INSTALL_COMMAND = 'npm install express --save'; +const EXAMPLE_CODE = `const express = require('express') +const app = express() +const port = 3000 + +app.get('/', (req, res) => { + res.send('Hello World!') +}) + +app.listen(port, () => { + console.log(\`Example app listening on port \${port}\`) +})`; + +interface Props { + videoSrc?: string; + videoSrcLight?: string; + posterSrc?: string; + posterSrcLight?: string; +} + +const { videoSrc, videoSrcLight, posterSrc, posterSrcLight } = Astro.props; +--- + + + { + videoSrc && ( + + {posterSrc && ( + + )} + {posterSrcLight && ( + + )} + + + + {videoSrcLight && ( + + + + )} + + + + + + + ) + } + + + + + + + + {EXPRESS_VERSION} + + + + + {t('hero.tagline')} + + + + + + {t('hero.getStarted')} + + + + + + + + + + + + + + + diff --git a/astro/src/components/patterns/Hero/Hero.css b/astro/src/components/patterns/Hero/Hero.css new file mode 100644 index 0000000000..bde4007a52 --- /dev/null +++ b/astro/src/components/patterns/Hero/Hero.css @@ -0,0 +1,332 @@ +@layer patterns { + .hero { + position: relative; + padding: var(--space-12) 0; + background-color: var(--color-bg-primary); + overflow: hidden; + + @media (--md-up) { + padding: var(--space-14) 0 var(--space-20) 0; + } + + @media (--lg-up) { + padding: var(--space-20) 0; + } + } + + .hero__video-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + overflow: hidden; + } + + .hero__video-poster, + .hero__video { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + min-width: 100%; + min-height: 100%; + width: auto; + height: auto; + object-fit: cover; + } + + .hero__video { + opacity: 0; + transition: opacity 0.5s ease-in; + + &[data-loaded='true'] { + opacity: 1; + } + } + + /* Default (light mode): hide dark poster, show light poster */ + .hero__video-poster--dark { + display: none; + } + + /* Dark theme via data-theme */ + [data-theme='dark'] .hero__video-poster--dark { + display: block; + } + + [data-theme='dark'] .hero__video-poster--light { + display: none; + } + + /* System dark mode */ + @media (prefers-color-scheme: dark) { + .hero__video-poster--dark { + display: block; + } + + .hero__video-poster--light { + display: none; + } + + [data-theme='light'] .hero__video-poster--dark { + display: none; + } + + [data-theme='light'] .hero__video-poster--light { + display: block; + } + } + + /* Default (light mode): hide dark video, show light video */ + .hero__video--dark { + display: none; + } + + /* Dark theme via data-theme */ + [data-theme='dark'] .hero__video--dark { + display: block; + } + + [data-theme='dark'] .hero__video--light { + display: none; + } + + /* System dark mode */ + @media (prefers-color-scheme: dark) { + .hero__video--dark { + display: block; + } + + .hero__video--light { + display: none; + } + + [data-theme='light'] .hero__video--dark { + display: none; + } + + [data-theme='light'] .hero__video--light { + display: block; + } + } + + .hero__video-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.05) 0%, + rgba(255, 255, 255, 0.15) 40%, + rgba(255, 255, 255, 0.3) 70%, + var(--color-bg-primary) 100% + ); + z-index: 1; + } + + .hero__video-control { + position: absolute; + bottom: var(--space-4); + right: var(--space-12); + z-index: 2; + width: var(--size-8); + height: var(--size-8); + display: flex; + align-items: center; + justify-content: center; + border: 1px solid var(--color-border-secondary); + border-radius: var(--radius-full); + color: var(--color-text-tertiary); + cursor: pointer; + transition: all 0.2s ease; + background-color: transparent; + + &:hover { + border-color: var(--color-border-primary); + color: var(--color-text-primary); + transform: scale(1.05); + } + + &:focus-visible { + outline: 2px solid var(--color-focus); + color: var(--color-text-primary); + outline-offset: 2px; + } + + &:active { + transform: scale(0.98); + } + + svg { + width: var(--size-3-5); + height: var(--size-3-5); + } + + &[data-state='playing'] .hero__video-control-play { + display: none; + } + + &[data-state='paused'] .hero__video-control-pause { + display: none; + } + + @media (--xs-only) { + display: none; + } + + @media (--md-up) { + bottom: var(--space-6); + right: var(--space-6); + } + } + + [data-theme='dark'] .hero__video-overlay { + background: linear-gradient( + 180deg, + rgba(0, 0, 0, 0.3) 0%, + rgba(0, 0, 0, 0.5) 40%, + rgba(0, 0, 0, 0.7) 70%, + var(--color-bg-primary) 100% + ); + } + + @media (prefers-color-scheme: dark) { + .hero__video-overlay { + background: linear-gradient( + 180deg, + rgba(0, 0, 0, 0.3) 0%, + rgba(0, 0, 0, 0.5) 40%, + rgba(0, 0, 0, 0.7) 70%, + var(--color-bg-primary) 100% + ); + } + + [data-theme='light'] .hero__video-overlay { + background: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.05) 0%, + rgba(255, 255, 255, 0.15) 40%, + rgba(255, 255, 255, 0.3) 70%, + var(--color-bg-primary) 100% + ); + } + } + + .hero__content { + position: relative; + z-index: 2; + display: flex; + flex-direction: column; + gap: var(--space-8); + + @media (--md-up) { + gap: var(--space-12); + } + } + + .hero__text-content { + @media (--lg-up) { + padding-right: var(--space-10); + } + } + + .hero__logo { + width: var(--size-36); + + svg { + width: 100%; + height: auto; + } + + @media (--md-up) { + width: var(--size-52); + } + } + + .hero__logo-icon--dark { + display: block; + } + + .hero__logo-icon--light { + display: none; + } + + [data-theme='dark'] .hero__logo-icon--dark { + display: none; + } + + [data-theme='dark'] .hero__logo-icon--light { + display: block; + } + + @media (prefers-color-scheme: dark) { + .hero__logo-icon--dark { + display: none; + } + + .hero__logo-icon--light { + display: block; + } + + [data-theme='light'] .hero__logo-icon--dark { + display: block; + } + + [data-theme='light'] .hero__logo-icon--light { + display: none; + } + } + + .hero__cta { + width: 100%; + display: flex; + margin: var(--space-8) 0; + flex-direction: column; + gap: var(--space-4); + + @media (--md-up) { + margin: var(--space-12) 0; + max-width: var(--size-96); + } + } +} + +.hero__code { + .expressive-code { + .frame pre { + backdrop-filter: blur(21px); + } + + figure { + box-shadow: none; + } + } + + [data-theme='dark'] & .expressive-code .frame pre { + background-color: rgba(0, 0, 0, 0.62); + } + + [data-theme='light'] & .expressive-code .frame pre { + background-color: color-mix(in srgb, var(--gray-950) 85%, transparent); + } + + @media (prefers-color-scheme: dark) { + .expressive-code .frame pre { + background-color: rgba(0, 0, 0, 0.62); + } + + [data-theme='light'] & .expressive-code .frame pre { + background-color: color-mix(in srgb, var(--gray-950) 85%, transparent); + } + } +} + +.hero__example-code { + .expressive-code .frame pre { + padding: var(--space-3); + } +} diff --git a/astro/src/components/patterns/Hero/index.ts b/astro/src/components/patterns/Hero/index.ts new file mode 100644 index 0000000000..d9d0f6d215 --- /dev/null +++ b/astro/src/components/patterns/Hero/index.ts @@ -0,0 +1,7 @@ +/** + * Hero Component Index + * + * Export Hero component + */ + +export { default } from './Hero.astro'; diff --git a/astro/src/components/patterns/index.ts b/astro/src/components/patterns/index.ts index e847728e25..0aab97857a 100644 --- a/astro/src/components/patterns/index.ts +++ b/astro/src/components/patterns/index.ts @@ -6,6 +6,7 @@ export { default as Breadcrumbs } from './Breadcrumbs/Breadcrumbs.astro'; export { default as Header } from './Header/Header.astro'; +export { default as Hero } from './Hero/Hero.astro'; export { default as Sidebar } from './Sidebar/Sidebar.astro'; export { default as ThemeSwitcher } from './ThemeSwitcher/ThemeSwitcher.astro'; export { default as VersionSwitcher } from './VersionSwitcher/VersionSwitcher.astro'; diff --git a/astro/src/components/primitives/Container/Container.css b/astro/src/components/primitives/Container/Container.css index 5df62ad59a..e61f83c405 100644 --- a/astro/src/components/primitives/Container/Container.css +++ b/astro/src/components/primitives/Container/Container.css @@ -4,12 +4,12 @@ @layer primitives { .container { - width: 90%; + width: 85%; max-width: 1440px; margin-inline: auto; @media (--lg-up) { - width: 95%; + width: 90%; } } } diff --git a/astro/src/components/primitives/Typography/H1.astro b/astro/src/components/primitives/Typography/H1.astro index c8475a637e..c1877845fb 100644 --- a/astro/src/components/primitives/Typography/H1.astro +++ b/astro/src/components/primitives/Typography/H1.astro @@ -17,6 +17,7 @@ interface Props extends HTMLAttributes<'h1'> { as?: HTMLTag; color?: TypographyColor; weight?: TypographyWeight; + vMargin?: boolean; } const { as = 'h1', ...rest } = Astro.props; diff --git a/astro/src/content.config.ts b/astro/src/content.config.ts index ba78fa7676..c03fc21dfe 100644 --- a/astro/src/content.config.ts +++ b/astro/src/content.config.ts @@ -17,7 +17,17 @@ const resourcesCollection = defineCollection({ }), }); +const npmCollection = defineCollection({ + loader: async () => { + const res = await fetch('https://registry.npmjs.org/express/latest'); + const { version } = await res.json(); + return [{ id: 'express', version }]; + }, + schema: z.object({ version: z.string() }), +}); + export const collections = { docs: docsCollection, resources: resourcesCollection, + npm: npmCollection, }; diff --git a/astro/src/i18n/ui/de.json b/astro/src/i18n/ui/de.json index 91eceeb47f..a2ad0b9cdc 100644 --- a/astro/src/i18n/ui/de.json +++ b/astro/src/i18n/ui/de.json @@ -71,5 +71,9 @@ "404.description": "Die gesuchte Seite konnte nicht gefunden werden.", "404.heading": "Seite nicht gefunden", "404.message": "Die gesuchte Seite existiert nicht oder wurde verschoben.", - "404.goHome": "Zur Startseite" + "404.goHome": "Zur Startseite", + "hero.tagline": "Schnelles, unvoreingenommenes, minimalistisches Web-Framework für Node.js", + "hero.getStarted": "Erste Schritte", + "hero.videoPause": "Hintergrundvideo anhalten", + "hero.videoPlay": "Hintergrundvideo abspielen" } diff --git a/astro/src/i18n/ui/en.json b/astro/src/i18n/ui/en.json index 79799945d4..013d5015d1 100644 --- a/astro/src/i18n/ui/en.json +++ b/astro/src/i18n/ui/en.json @@ -71,5 +71,9 @@ "404.description": "The page you are looking for could not be found.", "404.heading": "Page Not Found", "404.message": "The page you are looking for does not exist or has been moved.", - "404.goHome": "Go to Home" + "404.goHome": "Go to Home", + "hero.tagline": "Fast, unopinionated, minimalist web framework for Node.js", + "hero.getStarted": "Get Started", + "hero.videoPause": "Pause background video", + "hero.videoPlay": "Play background video" } diff --git a/astro/src/i18n/ui/es.json b/astro/src/i18n/ui/es.json index c55c5644b3..d4923ad3dd 100644 --- a/astro/src/i18n/ui/es.json +++ b/astro/src/i18n/ui/es.json @@ -71,5 +71,9 @@ "404.description": "La página que buscas no se pudo encontrar.", "404.heading": "Página no encontrada", "404.message": "La página que buscas no existe o ha sido movida.", - "404.goHome": "Ir a inicio" + "404.goHome": "Ir a inicio", + "hero.tagline": "Framework web rápido, sin opiniones y minimalista para Node.js", + "hero.getStarted": "Comenzar", + "hero.videoPause": "Pausar video de fondo", + "hero.videoPlay": "Reproducir video de fondo" } diff --git a/astro/src/i18n/ui/fr.json b/astro/src/i18n/ui/fr.json index f0cbb56e57..428e87af0e 100644 --- a/astro/src/i18n/ui/fr.json +++ b/astro/src/i18n/ui/fr.json @@ -71,5 +71,9 @@ "404.description": "La page que vous recherchez est introuvable.", "404.heading": "Page non trouvée", "404.message": "La page que vous recherchez n'existe pas ou a été déplacée.", - "404.goHome": "Aller à l'accueil" + "404.goHome": "Aller à l'accueil", + "hero.tagline": "Framework web rapide, sans opinion et minimaliste pour Node.js", + "hero.getStarted": "Commencer", + "hero.videoPause": "Mettre en pause la vidéo de fond", + "hero.videoPlay": "Lire la vidéo de fond" } diff --git a/astro/src/i18n/ui/it.json b/astro/src/i18n/ui/it.json index 765fe1e107..9a85677fe6 100644 --- a/astro/src/i18n/ui/it.json +++ b/astro/src/i18n/ui/it.json @@ -71,5 +71,9 @@ "404.description": "La pagina che stai cercando non è stata trovata.", "404.heading": "Pagina non trovata", "404.message": "La pagina che stai cercando non esiste o è stata spostata.", - "404.goHome": "Vai alla home" + "404.goHome": "Vai alla home", + "hero.tagline": "Framework web veloce, non prescrittivo e minimalista per Node.js", + "hero.getStarted": "Inizia", + "hero.videoPause": "Metti in pausa il video in background", + "hero.videoPlay": "Riproduci il video in background" } diff --git a/astro/src/i18n/ui/ja.json b/astro/src/i18n/ui/ja.json index dcd5771120..ba78dcff99 100644 --- a/astro/src/i18n/ui/ja.json +++ b/astro/src/i18n/ui/ja.json @@ -71,5 +71,9 @@ "404.description": "お探しのページが見つかりませんでした。", "404.heading": "ページが見つかりません", "404.message": "お探しのページは存在しないか、移動されました。", - "404.goHome": "ホームに戻る" + "404.goHome": "ホームに戻る", + "hero.tagline": "Node.js向けの高速で中立的なミニマリストWebフレームワーク", + "hero.getStarted": "始める", + "hero.videoPause": "背景動画を一時停止", + "hero.videoPlay": "背景動画を再生" } diff --git a/astro/src/i18n/ui/ko.json b/astro/src/i18n/ui/ko.json index 7006ef4e4a..23561f5f63 100644 --- a/astro/src/i18n/ui/ko.json +++ b/astro/src/i18n/ui/ko.json @@ -71,5 +71,9 @@ "404.description": "찾으시는 페이지를 찾을 수 없습니다.", "404.heading": "페이지를 찾을 수 없습니다", "404.message": "찾으시는 페이지가 존재하지 않거나 이동되었습니다.", - "404.goHome": "홈으로 이동" + "404.goHome": "홈으로 이동", + "hero.tagline": "Node.js를 위한 빠르고 독단적이지 않은 미니멀리스트 웹 프레임워크", + "hero.getStarted": "시작하기", + "hero.videoPause": "배경 비디오 일시정지", + "hero.videoPlay": "배경 비디오 재생" } diff --git a/astro/src/i18n/ui/pt-br.json b/astro/src/i18n/ui/pt-br.json index 68a043d648..17cc03007b 100644 --- a/astro/src/i18n/ui/pt-br.json +++ b/astro/src/i18n/ui/pt-br.json @@ -71,5 +71,9 @@ "404.description": "A página que você está procurando não foi encontrada.", "404.heading": "Página não encontrada", "404.message": "A página que você está procurando não existe ou foi movida.", - "404.goHome": "Ir para a página inicial" + "404.goHome": "Ir para a página inicial", + "hero.tagline": "Framework web rápido, sem opinião e minimalista para Node.js", + "hero.getStarted": "Começar", + "hero.videoPause": "Pausar vídeo de fundo", + "hero.videoPlay": "Reproduzir vídeo de fundo" } diff --git a/astro/src/i18n/ui/zh-cn.json b/astro/src/i18n/ui/zh-cn.json index 0559aa7ab2..05a9699a36 100644 --- a/astro/src/i18n/ui/zh-cn.json +++ b/astro/src/i18n/ui/zh-cn.json @@ -71,5 +71,9 @@ "404.description": "您正在查找的页面无法找到。", "404.heading": "页面未找到", "404.message": "您正在查找的页面不存在或已被移动。", - "404.goHome": "返回主页" + "404.goHome": "返回主页", + "hero.tagline": "快速、无偏见、极简主义的 Node.js Web 框架", + "hero.getStarted": "开始使用", + "hero.videoPause": "暂停背景视频", + "hero.videoPlay": "播放背景视频" } diff --git a/astro/src/i18n/ui/zh-tw.json b/astro/src/i18n/ui/zh-tw.json index 8f5067b2e7..10c42eaa88 100644 --- a/astro/src/i18n/ui/zh-tw.json +++ b/astro/src/i18n/ui/zh-tw.json @@ -71,5 +71,9 @@ "404.description": "您正在尋找的頁面無法找到。", "404.heading": "頁面未找到", "404.message": "您正在尋找的頁面不存在或已被移動。", - "404.goHome": "返回首頁" + "404.goHome": "返回首頁", + "hero.tagline": "快速、無偏見、極簡主義的 Node.js Web 框架", + "hero.getStarted": "開始使用", + "hero.videoPause": "暫停背景影片", + "hero.videoPlay": "播放背景影片" } diff --git a/astro/src/icons/logo-express-black.svg b/astro/src/icons/logo-express-black.svg new file mode 100644 index 0000000000..d9dace678c --- /dev/null +++ b/astro/src/icons/logo-express-black.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/astro/src/icons/logo-express-white.svg b/astro/src/icons/logo-express-white.svg new file mode 100644 index 0000000000..6b14ccf672 --- /dev/null +++ b/astro/src/icons/logo-express-white.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/astro/src/pages/[lang]/index.astro b/astro/src/pages/[lang]/index.astro index 5c36cf363c..75ed95b53a 100644 --- a/astro/src/pages/[lang]/index.astro +++ b/astro/src/pages/[lang]/index.astro @@ -1,7 +1,6 @@ --- -import { Body, Container, H1 } from '@components/primitives'; +import { Hero } from '@components/patterns'; import Layout from '@layouts/Layout.astro'; -import { getLangFromUrl, useTranslations } from '@i18n/utils'; import { languages } from '@i18n/locales'; export function getStaticPaths() { @@ -9,21 +8,13 @@ export function getStaticPaths() { params: { lang }, })); } - -const lang = getLangFromUrl(Astro.url); -const t = useTranslations(lang); --- - - {t('home.welcome')} - {t('home.workInProgress')} - - - {t('home.reviewDesignSystemPrefix')} - {t('home.reviewDesignSystemLink')} - {t('home.reviewDesignSystemSuffix')} - - - + diff --git a/astro/src/styles/base/_expressive-code.css b/astro/src/styles/base/_expressive-code.css new file mode 100644 index 0000000000..dbb335d318 --- /dev/null +++ b/astro/src/styles/base/_expressive-code.css @@ -0,0 +1,24 @@ +/** + * Expressive Code Global Styles + * + * Applied to all expressive-code blocks across the app + * Using higher specificity selectors to override expressive-code defaults + */ + +/* Increase specificity by doubling the class selector */ +.expressive-code.expressive-code { + .copy { + top: var(--space-3); + right: var(--space-3); + height: var(--size-5); + } + + figcaption:not(:empty) ~ .copy { + top: var(--space-10); + } +} + +.expressive-code.expressive-code .feedback { + line-height: var(--line-height-snug); + font-family: var(--font-family-body); +} diff --git a/astro/src/styles/main.css b/astro/src/styles/main.css index d06f8a5ced..ad16559b4c 100644 --- a/astro/src/styles/main.css +++ b/astro/src/styles/main.css @@ -21,3 +21,6 @@ @import './base/_fonts.css' layer(base); @import './base/_global.css' layer(base); @import './utilities/_utilities.css' layer(base); + +/* Import expressive-code customizations OUTSIDE layers for highest priority */ +@import './base/_expressive-code.css';