diff --git a/website/components/io-home-hero/index.tsx b/website/components/io-home-hero/index.tsx new file mode 100644 index 000000000000..7a1b1f3436da --- /dev/null +++ b/website/components/io-home-hero/index.tsx @@ -0,0 +1,124 @@ +import * as React from 'react' +import Button from '@hashicorp/react-button' +import classNames from 'classnames' +import s from './style.module.css' + +interface IoHomeHeroProps { + brand: 'vault' | 'consul' + heading: string + description: string + ctas: Array<{ + title: string + url: string + }> + cards: Array +} + +export default function IoHomeHero({ + brand, + heading, + description, + ctas, + cards, +}: IoHomeHeroProps) { + const [loaded, setLoaded] = React.useState(false) + + React.useEffect(() => { + setTimeout(() => { + setLoaded(true) + }, 250) + }, []) + + return ( +
+ +
+
+

{heading}

+

{description}

+ {ctas && ( +
+ {ctas.map((cta, index) => { + return ( +
+ )} +
+ {cards && ( +
+ {cards.map((card, index) => { + return ( + + ) + })} +
+ )} +
+
+ ) +} + +interface IoHomeHeroCardProps { + index?: number + heading: string + description: string + cta: { + title: string + url: string + brand?: 'neutral' | 'vault' | 'consul' + } + subText: string +} + +function IoHomeHeroCard({ + index, + heading, + description, + cta, + subText, +}: IoHomeHeroCardProps) { + return ( +
+

{heading}

+

{description}

+
+ ) +} diff --git a/website/components/io-home-hero/style.module.css b/website/components/io-home-hero/style.module.css new file mode 100644 index 000000000000..959923f05e80 --- /dev/null +++ b/website/components/io-home-hero/style.module.css @@ -0,0 +1,148 @@ +.hero { + position: relative; + padding-top: 64px; + padding-bottom: 64px; + background: linear-gradient(180deg, #f9f9fa 0%, #fff 28.22%, #fff 100%); + + @media (--medium-up) { + padding-top: 128px; + padding-bottom: 128px; + } +} + +.pattern { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + max-width: 1600px; + width: 100%; + margin: auto; + + @media (--medium-up) { + background-image: url('/img/hero-pattern.svg'); + background-repeat: no-repeat; + background-position: top right; + } +} + +.container { + --columns: 1; + + composes: g-grid-container from global; + display: grid; + grid-template-columns: repeat(var(--columns), minmax(0, 1fr)); + gap: 48px 32px; + + @media (--medium-up) { + --columns: 12; + } +} + +.content { + grid-column: 1 / -1; + + @media (--medium-up) { + grid-column: 1 / 6; + } + + @media (--large) { + grid-column: 1 / 5; + } +} + +.heading { + margin: 0; + composes: g-type-display-1 from global; +} + +.description { + margin: 8px 0 0; + composes: g-type-body-small from global; + color: var(--gray-3); +} + +.ctas { + margin-top: 24px; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 24px; +} + +.cards { + --columns: 1; + + grid-column: 1 / -1; + align-self: start; + display: grid; + grid-template-columns: repeat(var(--columns), minmax(0, 1fr)); + gap: 32px; + + @media (min-width: 600px) { + --columns: 2; + } + + @media (--medium-up) { + --columns: 1; + + grid-column: 7 / -1; + } + + @media (--large) { + --columns: 2; + + grid-column: 6 / -1; + } +} + +.card { + --token-radius: 6px; + --token-elevation-mid: 0 2px 3px rgba(101, 106, 118, 0.1), + 0 8px 16px -10px rgba(101, 106, 118, 0.2); + + opacity: 0; + padding: 40px 32px; + display: flex; + align-items: flex-start; + flex-direction: column; + flex-grow: 1; + background-color: var(--white); + border-radius: var(--token-radius); + box-shadow: 0 0 0 1px rgba(38, 53, 61, 0.1), var(--token-elevation-mid); + + @nest .loaded & { + animation-name: slideIn; + animation-duration: 0.5s; + animation-delay: calc(var(--index) * 0.1s); + animation-fill-mode: forwards; + } +} + +.cardHeading { + margin: 0; + composes: g-type-display-4 from global; +} + +.cardDescription { + margin: 8px 0 16px; + composes: g-type-display-6 from global; +} + +.cardSubText { + margin: 32px 0 0; + composes: g-type-body-small from global; + color: var(--gray-3); +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(50px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/website/components/io-home-pre-footer/index.tsx b/website/components/io-home-pre-footer/index.tsx new file mode 100644 index 000000000000..8b0d2ee0b223 --- /dev/null +++ b/website/components/io-home-pre-footer/index.tsx @@ -0,0 +1,77 @@ +import * as React from 'react' +import { IconArrowRight16 } from '@hashicorp/flight-icons/svg-react/arrow-right-16' +import s from './style.module.css' + +interface IoHomePreFooterProps { + brand: string + heading: string + description: string + ctas: [IoHomePreFooterCard, IoHomePreFooterCard, IoHomePreFooterCard] +} + +export default function IoHomePreFooter({ + brand, + heading, + description, + ctas, +}: IoHomePreFooterProps) { + return ( +
+
+
+

{heading}

+

{description}

+
+
+ {ctas.map((cta, index) => { + return ( + + ) + })} +
+
+
+ ) +} + +interface IoHomePreFooterCard { + brand?: string + link: string + heading: string + description: string + label: string +} + +function IoHomePreFooterCard({ + brand, + link, + heading, + description, + label, +}: IoHomePreFooterCard) { + return ( + +

{heading}

+

{description}

+ + {label} + +
+ ) +} diff --git a/website/components/io-home-pre-footer/style.module.css b/website/components/io-home-pre-footer/style.module.css new file mode 100644 index 000000000000..876009d416fe --- /dev/null +++ b/website/components/io-home-pre-footer/style.module.css @@ -0,0 +1,113 @@ +.preFooter { + padding: 96px 0 64px; +} + +.container { + --columns: 1; + + composes: g-grid-container from global; + display: grid; + grid-template-columns: repeat(var(--columns), minmax(0, 1fr)); + gap: 32px; + + @media (--medium-up) { + --columns: 12; + } +} + +.content { + grid-column: 1 / -1; + + @media (--medium-up) { + grid-column: 1 / 6; + } + + @media (--large) { + grid-column: 1 / 4; + } +} + +.heading { + margin: 0; + composes: g-type-display-1 from global; +} + +.description { + margin: 24px 0 0; + composes: g-type-body from global; + color: var(--gray-3); +} + +.cards { + grid-column: 1 / -1; + + --columns: 1; + + display: grid; + grid-template-columns: repeat(var(--columns), minmax(0, 1fr)); + gap: 32px; + + @media (--medium-up) { + --columns: 3; + + grid-column: 1 / -1; + } + + @media (--large) { + grid-column: 5 / -1; + } +} + +.card { + display: flex; + flex-direction: column; + flex-grow: 1; + padding: 32px 24px; + background-color: var(--primary); + color: var(--black); + border-radius: 6px; + box-shadow: 0 2px 3px rgba(101, 106, 118, 0.1), + 0 8px 16px -10px rgba(101, 106, 118, 0.2); + transition: ease-in-out 0.2s; + transition-property: box-shadow; + + &:hover { + box-shadow: 0 2px 3px rgba(101, 106, 118, 0.15), + 0 16px 16px -10px rgba(101, 106, 118, 0.2); + } + + &:nth-of-type(2) { + background-color: var(--secondary); + } + + &:nth-of-type(3) { + background-color: var(--gray-6); + } +} + +.cardHeading { + margin: 0; + composes: g-type-display-4 from global; +} + +.cardDescription { + margin: 8px 0 0; + padding-bottom: 48px; + composes: g-type-display-6 from global; +} + +.cardCta { + margin-top: auto; + display: inline-flex; + align-items: center; + composes: g-type-buttons-and-standalone-links from global; + + & svg { + margin-left: 12px; + transition: transform 0.2s; + } + + @nest .card:hover & svg { + transform: translate(2px); + } +} diff --git a/website/next.config.js b/website/next.config.js index 860db5c0789d..dc7aa6967d48 100644 --- a/website/next.config.js +++ b/website/next.config.js @@ -3,6 +3,7 @@ const redirects = require('./redirects.next') module.exports = withHashicorp({ nextOptimizedImages: true, + transpileModules: ['@hashicorp/flight-icons'], })({ svgo: { plugins: [{ removeViewBox: false }] }, rewrites: () => [ diff --git a/website/package-lock.json b/website/package-lock.json index dd57b658ecfd..fb9a38f7b738 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { + "@hashicorp/flight-icons": "^1.3.0", "@hashicorp/mktg-global-styles": "^4.0.0", "@hashicorp/mktg-logos": "^1.2.0", "@hashicorp/platform-analytics": "^0.2.0", @@ -964,6 +965,11 @@ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", "integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==" }, + "node_modules/@hashicorp/flight-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@hashicorp/flight-icons/-/flight-icons-1.3.0.tgz", + "integrity": "sha512-m4GkuYocXv54cBc+7sCkD+xorSxGPYovACpk8/A8I9rCFwdkX3WSZM8osT2/ws4IkCfeNYoxB1dz6a2SRhRfDg==" + }, "node_modules/@hashicorp/js-utils": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@hashicorp/js-utils/-/js-utils-1.0.10.tgz", @@ -23087,6 +23093,11 @@ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", "integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==" }, + "@hashicorp/flight-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@hashicorp/flight-icons/-/flight-icons-1.3.0.tgz", + "integrity": "sha512-m4GkuYocXv54cBc+7sCkD+xorSxGPYovACpk8/A8I9rCFwdkX3WSZM8osT2/ws4IkCfeNYoxB1dz6a2SRhRfDg==" + }, "@hashicorp/js-utils": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@hashicorp/js-utils/-/js-utils-1.0.10.tgz", diff --git a/website/package.json b/website/package.json index ebba06a308cb..64d3e00f7425 100644 --- a/website/package.json +++ b/website/package.json @@ -4,6 +4,7 @@ "version": "1.0.0", "author": "HashiCorp", "dependencies": { + "@hashicorp/flight-icons": "^1.3.0", "@hashicorp/mktg-global-styles": "^4.0.0", "@hashicorp/mktg-logos": "^1.2.0", "@hashicorp/platform-analytics": "^0.2.0", diff --git a/website/pages/home/index.tsx b/website/pages/home/index.tsx index 688ed15e0d8a..37b13453bf46 100644 --- a/website/pages/home/index.tsx +++ b/website/pages/home/index.tsx @@ -1,10 +1,76 @@ -// Imports below are used in getStaticProps only -import RAW_CONTENT from './content.json' +import IoHomeHero from 'components/io-home-hero' +import IoHomePreFooter from 'components/io-home-pre-footer' export async function getStaticProps() { return { props: {} } } export default function Homepage({ content }) { - return

Homepage

+ return ( + <> + + + + + ) } diff --git a/website/public/img/hero-pattern.svg b/website/public/img/hero-pattern.svg new file mode 100644 index 000000000000..416fa336fde3 --- /dev/null +++ b/website/public/img/hero-pattern.svg @@ -0,0 +1 @@ + \ No newline at end of file