website: remove source code (#15068)

* removes site source code

* remove algolia index and docker image workflows

* remove unneeded dependencies
This commit is contained in:
Bryce Kalow 2022-04-19 13:41:15 -05:00 committed by GitHub
parent e69f89c279
commit 2c4a619a8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
105 changed files with 3746 additions and 22041 deletions

51
.circleci/config.yml generated
View File

@ -148,22 +148,6 @@ jobs:
- GO_VERSION: 1.17.9
- GOFUMPT_VERSION: 0.2.1
- GOTESTSUM_VERSION: 0.5.2
algolia-index:
docker:
- image: node:14
steps:
- checkout
- run:
command: |
if [ "$CIRCLE_REPOSITORY_URL" != "git@github.com:hashicorp/vault.git" ]; then
echo "Not Vault OSS Repo, not indexing Algolia"
exit 0
fi
cd website/
npm install -g npm@latest
npm install
node scripts/index_search_content.js
name: Push content to Algolia Index
test-go-remote-docker:
docker:
- image: docker.mirror.hashicorp.services/cimg/go:1.17.9
@ -588,29 +572,6 @@ jobs:
environment:
- CIRCLECI_CLI_VERSION: 0.1.5546
- GO_TAGS: ''
website-docker-image:
docker:
- image: circleci/buildpack-deps
shell: /usr/bin/env bash -euo pipefail -c
steps:
- checkout
- setup_remote_docker
- run:
command: |
IMAGE_TAG="$(git rev-list -n1 HEAD -- website/Dockerfile website/package-lock.json)"
echo "Using $IMAGE_TAG"
if [ "$CIRCLE_REPOSITORY_URL" != "git@github.com:hashicorp/vault.git" ]; then
echo "Not Vault OSS Repo, not building website docker image"
elif curl https://hub.docker.com/v2/repositories/hashicorp/vault-website/tags/$IMAGE_TAG -fsL > /dev/null; then
echo "Dependencies have not changed, not building a new website docker image."
else
cd website/
docker build -t hashicorp/vault-website:$IMAGE_TAG .
docker tag hashicorp/vault-website:$IMAGE_TAG hashicorp/vault-website:latest
docker login -u $WEBSITE_DOCKER_USER -p $WEBSITE_DOCKER_PASS
docker push hashicorp/vault-website
fi
name: Build Docker Image if Necessary
test-go:
docker:
- image: docker.mirror.hashicorp.services/cimg/go:1.17.9
@ -1137,18 +1098,6 @@ workflows:
- test-go-race-remote-docker:
requires:
- pre-flight-checks
- website-docker-image:
filters:
branches:
only:
- main
context: vault-docs
- algolia-index:
filters:
branches:
only:
- stable-website
context: vault-docs
- semgrep:
requires:
- pre-flight-checks

View File

@ -1,15 +0,0 @@
docker:
- image: node:14
steps:
- checkout
- run:
name: Push content to Algolia Index
command: |
if [ "$CIRCLE_REPOSITORY_URL" != "git@github.com:hashicorp/vault.git" ]; then
echo "Not Vault OSS Repo, not indexing Algolia"
exit 0
fi
cd website/
npm install -g npm@latest
npm install
node scripts/index_search_content.js

View File

@ -1,22 +0,0 @@
docker:
- image: circleci/buildpack-deps
shell: /usr/bin/env bash -euo pipefail -c
steps:
- checkout
- setup_remote_docker
- run:
name: Build Docker Image if Necessary
command: |
IMAGE_TAG="$(git rev-list -n1 HEAD -- website/Dockerfile website/package-lock.json)"
echo "Using $IMAGE_TAG"
if [ "$CIRCLE_REPOSITORY_URL" != "git@github.com:hashicorp/vault.git" ]; then
echo "Not Vault OSS Repo, not building website docker image"
elif curl https://hub.docker.com/v2/repositories/hashicorp/vault-website/tags/$IMAGE_TAG -fsL > /dev/null; then
echo "Dependencies have not changed, not building a new website docker image."
else
cd website/
docker build -t hashicorp/vault-website:$IMAGE_TAG .
docker tag hashicorp/vault-website:$IMAGE_TAG hashicorp/vault-website:latest
docker login -u $WEBSITE_DOCKER_USER -p $WEBSITE_DOCKER_PASS
docker push hashicorp/vault-website
fi

View File

@ -38,18 +38,6 @@ jobs:
- test-go-race-remote-docker:
requires:
- pre-flight-checks
- website-docker-image:
context: vault-docs
filters:
branches:
only:
- main
- algolia-index:
context: vault-docs
filters:
branches:
only:
- stable-website
- semgrep:
requires:
- pre-flight-checks

View File

@ -1,6 +1,6 @@
# Vault Website
This subdirectory contains the entire source for the [Vault Website](https://vaultproject.io/). This is a [NextJS](https://nextjs.org/) project, which builds a static site from these source files.
This subdirectory contains the content for the [Vault Website](https://vaultproject.io/).
<!--
This readme file contains several blocks of generated text, to make it easier to share common information
@ -40,11 +40,10 @@ The website can be run locally through node.js or [Docker](https://www.docker.co
> **Note:** If you are using a text editor that uses a "safe write" save style such as **vim** or **goland**, this can cause issues with the live reload in development. If you turn off safe write, this should solve the problem. In vim, this can be done by running `:set backupcopy=yes`. In goland, search the settings for "safe write" and turn that setting off.
| :warning: WARNING :warning: |
|:----------------------------|
| :warning: WARNING :warning: |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| If you've previously run the website successfully using either the Docker or Node.js approach but are now facing some issue then try `docker rmi $(docker images -aq hashicorp-vault-website-local) && make build-image && make website-local` or `rm -rf node_modules`; failing that consider cloning this repository again and re-attempting the steps anew in a clean clone. |
### With Docker
Running the site locally is simple. Provided you have Docker installed, clone this repo, run `make`, and then visit `http://localhost:3000`.
@ -82,7 +81,6 @@ This file can be standard Markdown and also supports [YAML frontmatter](https://
title: 'My Title'
description: "A thorough, yet succinct description of the page's contents"
---
```
The significant keys in the YAML frontmatter are:

View File

@ -1,13 +0,0 @@
fragment beforeAfterDiagramFields on SbcBeforeAfterDiagramRecord {
theme
beforeHeadline
beforeContent
beforeImage {
...imageFields
}
afterHeadline
afterContent
afterImage {
...imageFields
}
}

View File

@ -1,87 +0,0 @@
import Image from '@hashicorp/react-image'
import InlineSvg from '@hashicorp/react-inline-svg'
import alertIcon from 'public/img/icons/alert.svg?include'
import checkIcon from 'public/img/icons/check.svg?include'
import fragment from './fragment.graphql'
import s from './style.module.css'
function BeforeAfterDiagram(props) {
const {
theme,
beforeHeadline,
beforeContent,
beforeImage,
afterHeadline,
afterContent,
afterImage,
} = props
return (
<div className={s.beforeAfterDiagram} data-theme={theme}>
<div className={s.beforeSide}>
<div className={s.image}>
<div>
<Image {...beforeImage} />
</div>
</div>
<div className={s.contentContainer}>
<span className={s.iconLineContainer}>
<InlineSvg className={s.beforeIcon} src={alertIcon} />
<span className={s.lineSegment} />
</span>
<div>
{beforeHeadline && (
<h2
className={s.contentHeadline}
dangerouslySetInnerHTML={{
__html: beforeHeadline,
}}
/>
)}
{beforeContent && (
<div
className={s.beforeContent}
dangerouslySetInnerHTML={{
__html: beforeContent,
}}
/>
)}
</div>
</div>
</div>
<div className={s.afterSide}>
<div className={s.image}>
<div>
<Image {...afterImage} />
</div>
</div>
<div className={s.contentContainer}>
<span className={s.iconLineContainer}>
<InlineSvg className={s.afterIcon} src={checkIcon} />
</span>
<div>
{afterHeadline && (
<h2
className={s.contentHeadline}
dangerouslySetInnerHTML={{
__html: afterHeadline,
}}
/>
)}
{afterContent && (
<div
className={s.afterContent}
data-theme={theme}
dangerouslySetInnerHTML={{
__html: afterContent,
}}
/>
)}
</div>
</div>
</div>
</div>
)
}
BeforeAfterDiagram.fragmentSpec = { fragment, dependencies: [Image] }
export default BeforeAfterDiagram

View File

@ -1,31 +0,0 @@
import md from '@hashicorp/platform-markdown-utils/markdown-to-html'
import mdInline from '@hashicorp/platform-markdown-utils/markdown-to-inline-html'
export default async function processBeforeAfterDiagramProps(props) {
const { beforeHeadline, beforeContent, afterHeadline, afterContent } = props
// Transform headline markdown to HTML, for inline bold / italic support
props.beforeHeadline = await mdInline(beforeHeadline)
props.afterHeadline = await mdInline(afterHeadline)
// Transform content markdown to HTML, using custom type classes
const contentOptions = {
contentPlugins: {
pluginOptions: {
typography: {
map: {
h1: 'g-type-label',
h2: 'g-type-label',
h3: 'g-type-label',
h4: 'g-type-label',
h5: 'g-type-label',
h6: 'g-type-label',
p: 'g-type-body-small',
li: 'g-type-body-small',
},
},
},
},
}
props.beforeContent = await md(beforeContent, contentOptions)
props.afterContent = await md(afterContent, contentOptions)
return props
}

View File

@ -1,348 +0,0 @@
/* Container */
.beforeAfterDiagram {
/* CSS custom properties to control theming */
--product-color: var(--black);
--gray-6-transparent: rgba(210, 212, 219, 0);
--after-bullet-background: url('/img/icons/check-square-vault.svg');
--after-bullet-height: 18px;
display: flex;
flex-wrap: wrap;
margin: 0 -16px;
position: relative;
@media (max-width: 1023px) {
margin-left: -12px;
margin-right: -12px;
}
@media (max-width: 767px) {
flex-direction: column;
margin-left: 40px;
margin-right: 0;
}
}
/* Before and after columns */
.side {
display: flex;
flex-direction: column;
margin: 0 16px;
position: relative;
width: calc(50% - 32px);
@media (max-width: 1023px) {
margin: 0 12px;
width: calc(50% - 24px);
}
@media (max-width: 767px) {
margin: 0;
width: 100%;
}
}
.beforeSide {
composes: side;
@media (max-width: 767px) {
margin-bottom: 62px;
}
}
.afterSide {
composes: side;
}
/* Diagram images */
.image {
align-items: flex-end;
display: flex;
height: 320px;
justify-content: center;
margin-bottom: 96px;
@media (max-width: 767px) {
margin-bottom: 40px;
}
@media (max-width: 640px) {
height: 284px;
}
@media (max-width: 540px) {
height: 238px;
}
@media (max-width: 480px) {
height: 211px;
}
@media (max-width: 375px) {
height: 163px;
}
& div {
height: 100%;
text-align: center;
width: 100%;
}
& picture {
height: 100%;
}
& img,
& svg {
height: 100%;
max-width: 100%;
object-fit: contain;
}
@media (--medium-up) {
& div {
height: unset;
}
& picture {
height: unset;
}
& img,
& svg {
height: unset;
}
}
}
/* icon / line container above content */
.iconLineContainer {
padding: 0;
position: absolute;
right: 0;
top: -75px;
width: 100%;
@media (max-width: 767px) {
height: 100%;
left: -28px;
right: auto;
top: 28px;
width: auto;
}
}
/* Line segment above content (before side only) */
.lineSegment {
background: black;
display: block;
height: 2px;
left: calc(50% + 30px);
position: absolute;
top: 12px;
width: calc(100% - 24px);
@media (max-width: 767px) {
height: calc(100% + 375px);
left: auto;
top: 38px;
width: 2px;
}
@media (max-width: 640px) {
height: calc(100% + 339px);
}
@media (max-width: 540px) {
height: calc(100% + 293px);
}
@media (max-width: 480px) {
height: calc(100% + 266px);
}
@media (max-width: 375px) {
height: calc(100% + 218px);
}
&::before {
border-radius: 100%;
border-style: solid;
border-width: 5.5px 0 5.5px 8px;
border-width: 2px;
content: '';
height: 8px;
left: -8px;
position: absolute;
top: -3px;
width: 8px;
@media (max-width: 767px) {
left: -3px;
top: -8px;
}
}
&::after {
border-color: transparent transparent transparent var(--product-color);
border-style: solid;
border-width: 6px 0 6px 8px;
content: '';
height: 0;
position: absolute;
right: -8px;
top: -5px;
width: 0;
@media (max-width: 767px) {
bottom: -8px;
right: -4px;
top: auto;
transform: rotate(90deg);
}
}
}
/* Icon above each content container */
.contentIcon {
& svg {
left: 50%;
margin: 0 0 0 -11px;
position: absolute;
}
}
.beforeIcon {
composes: contentIcon;
}
.afterIcon {
composes: contentIcon;
& svg path:first-child {
fill: var(--product-color);
stroke: var(--product-color);
}
}
/* Content container */
.contentContainer {
border: 1px solid var(--gray-5);
flex-grow: 1;
padding: 24px 32px 20px;
position: relative;
@media (max-width: 1023px) {
padding-left: 24px;
padding-right: 24px;
}
@media (max-width: 767px) {
padding-left: 20px;
padding-right: 20px;
}
&::before,
&::after {
border: solid transparent;
bottom: 100%;
content: '';
height: 0;
left: 50%;
pointer-events: none;
position: absolute;
width: 0;
}
&::before {
border-color: rgba(229, 230, 235, 0);
border-bottom-color: var(--gray-5);
border-width: 18px;
margin-left: -18px;
}
&::after {
border-color: rgba(255, 255, 255, 0);
border-bottom-color: var(--white);
border-width: 17px;
margin-left: -17px;
}
& > div {
height: 100%;
& > div {
@media (min-width: 768px) {
margin: 0 auto;
max-width: 480px;
}
}
}
}
/* Content headline */
.contentHeadline {
border-bottom: 1px solid var(--gray-5);
color: var(--black);
composes: g-type-display-3 from global;
margin: 0 0 24px;
padding-bottom: 24px;
text-align: center;
}
/* Content styles (for rendered markdown) */
.content {
& :global(.__permalink-h) {
display: none;
}
& :global(.g-type-label) {
margin: 24px 0 26px 0;
}
& ul,
& ol {
list-style: none;
padding-left: 32px;
position: relative;
}
& li {
margin: 8px 0;
&::before {
background-repeat: no-repeat;
content: '';
left: 0;
position: absolute;
}
}
}
.beforeContent {
composes: content;
& li::before {
background: url('/img/icons/alert-check.svg');
background-repeat: no-repeat;
height: var(--after-bullet-height);
margin-top: 3px;
width: 20px;
}
}
.afterContent {
composes: content;
& li::before {
background: var(--after-bullet-background);
height: var(--after-bullet-height);
margin-top: 4px;
width: 18px;
}
}

View File

@ -1,3 +0,0 @@
# Columns Component
An _experimental_ component currently used only on `/api-docs/plugins-directory` -- currently only works correctly when applied to `ul` elements.

View File

@ -1,5 +0,0 @@
import s from './style.module.css'
export default function Columns({ count = 1, children }) {
return <div className={`${s.root} ${s['count-' + count]}`}>{children}</div>
}

View File

@ -1,27 +0,0 @@
.root {
break-inside: avoid-column;
page-break-inside: avoid;
&.count-1 ul {
columns: 1;
}
&.count-2 ul {
columns: 2;
}
&.count-3 ul {
columns: 3;
}
&.count-4 ul {
columns: 4;
}
&[class] ul {
@media (max-width: 500px) {
columns: 1;
}
}
& > ul li {
margin-top: 0;
}
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="136" height="48" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M134.509 35.895c-.978.414-2.857.714-4.023.714-3.346 0-5.037-1.579-5.037-4.849V21.574h-2.744v-3.646h2.744v-4.549l4.586-.64v5.189h4.698l-.3 3.646h-4.398v9.585c0 .978.451 1.617 1.654 1.617.677 0 1.503-.15 2.293-.375l.527 3.495zm-18.269.338V10.446l4.586-.64v26.427h-4.586zm-14.773-18.306v12.78c0 .978.414 1.466 1.466 1.466 1.128 0 3.12-.676 4.774-1.54V17.926h4.586v18.306h-3.496l-.451-1.541c-2.293 1.128-5.187 1.917-7.33 1.917-3.044 0-4.135-2.143-4.135-5.413v-13.27h4.586zm-12.555 11.39H85.53c-1.503 0-1.917.414-1.917 1.805 0 1.278.414 1.84 1.841 1.84 1.354 0 2.595-.45 3.46-.939v-2.706zm4.586 6.916H89.74l-.338-1.24c-1.654 1.09-3.609 1.616-5.45 1.616-3.347 0-4.775-2.293-4.775-5.45 0-3.721 1.617-5.15 5.338-5.15h4.398V24.09c0-2.03-.564-2.744-3.496-2.744-1.654 0-3.458.226-5.074.564l-.564-3.496c1.728-.526 4.247-.865 6.277-.865 5.752 0 7.443 2.03 7.443 6.617v12.066zm-20.11-25.036h4.924l-7.48 25.036h-6.993l-7.48-25.036h4.924l6.052 20.864 6.053-20.864zm-45.96 9.567h2.782v-2.783h-2.782v2.783zm-.024-4.174h2.782v-2.783h-2.782v2.783zm-4.15-4.174h2.782V9.633h-2.783v2.783zm0 4.174h2.782v-2.783h-2.783v2.783zm0 4.174h2.782V17.98h-2.783v2.783zm0 4.174h2.782v-2.783h-2.783v2.782zM19.08 12.415h2.783V9.633H19.08v2.783zm0 4.174h2.783v-2.783H19.08v2.783zm0 4.174h2.783V17.98H19.08v2.783zm8.324-8.348h2.782V9.633h-2.782v2.783zM.645 0l23.913 48L48.645 0h-48z" fill="#000"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,76 +0,0 @@
import Link from 'next/link'
import Button from '@hashicorp/react-button'
import { VERSION, CHANGELOG_URL } from 'data/version'
import s from '../../pages/downloads/style.module.css'
const changelogUrl = CHANGELOG_URL.length
? CHANGELOG_URL
: `https://github.com/hashicorp/vault/blob/v${VERSION}/CHANGELOG.md`
export default function DownloadsProps(preMerchandisingSlot) {
return {
changelog: changelogUrl,
getStartedDescription:
'Follow step-by-step tutorials on the essentials of Vault.',
getStartedLinks: [
{
label: 'Getting Started with the CLI',
href: 'http://learn.hashicorp.com/collections/vault/getting-started',
},
{
label: 'Getting Started with Vault UI',
href: 'http://learn.hashicorp.com/collections/vault/getting-started-ui',
},
{
label: 'Vault on HCP',
href: 'http://learn.hashicorp.com/collections/vault/getting-started-ui',
},
{
label: 'View all Vault tutorials',
href: 'https://learn.hashicorp.com/vault',
},
],
logo: (
<img
className={s.logo}
alt="Vault"
src={require('./img/vault-logo.svg')}
/>
),
tutorialLink: {
href: 'https://learn.hashicorp.com/vault',
label: 'View Tutorials at HashiCorp Learn',
},
merchandisingSlot: (
<>
{preMerchandisingSlot && preMerchandisingSlot}
<div className={s.merchandisingSlot}>
<div className={s.centerWrapper}>
<p>
Want all of the power and security of Vault, without the
complexity and overhead of managing it yourself?
</p>
<Button
title="Sign up for HCP Vault"
linkType="inbound"
url="https://portal.cloud.hashicorp.com/sign-up?utm_source=vault_io&utm_content=download_cta"
theme={{
variant: 'tertiary',
brand: 'vault',
}}
/>
</div>
</div>
<p className={s.releaseNote}>
Release notes are available in our{' '}
<Link href={`/docs/release-notes`}>
<a>documentation</a>
</Link>
.
</p>
</>
),
}
}

View File

@ -1,30 +0,0 @@
import Link from 'next/link'
export default function Footer({ openConsentManager }) {
return (
<footer className="g-footer">
<div className="g-grid-container">
<div className="left">
<Link href="/docs">
<a>Docs</a>
</Link>
<Link href="/api">
<a>API</a>
</Link>
<a href="https://learn.hashicorp.com/vault">Learn</a>
<Link href="/community">
<a>Community</a>
</Link>
<a href="https://hashicorp.com/privacy">Privacy</a>
<Link href="/security">
<a>Security</a>
</Link>
<Link href="/files/press-kit.zip">
<a>Press Kit</a>
</Link>
<a onClick={openConsentManager}>Consent Manager</a>
</div>
</div>
</footer>
)
}

View File

@ -1,31 +0,0 @@
.g-footer {
padding: 25px 0 17px 0;
flex-shrink: 0;
display: flex;
& .g-grid-container {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
& a {
color: var(--gray-3);
transition: color 0.25s ease;
cursor: pointer;
display: inline-block;
&:hover {
color: black;
}
}
& .left > a {
margin-right: 20px;
margin-bottom: 8px;
&:last-child {
margin-right: 0;
}
}
}

View File

@ -1,79 +0,0 @@
.hcpCalloutSection {
composes: g-grid-container from global;
padding-top: 88px;
padding-bottom: 88px;
& .header {
display: flex;
justify-content: center;
margin-bottom: 88px;
@media (max-width: 1120px) {
margin-bottom: 48px;
}
& h2 {
margin: 0;
text-align: center;
max-width: 450px;
}
}
& .content {
display: flex;
flex-direction: row;
justify-content: space-between;
@media (max-width: 1120px) {
flex-direction: column-reverse;
& .info {
margin-top: 32px;
}
}
& .info {
max-width: 488px;
margin-right: 32px;
& h1 {
margin-top: 0;
margin-bottom: 8px;
}
& .description {
color: var(--gray-2);
margin-top: 28px;
margin-bottom: 0;
@media (max-width: 900px) {
margin-top: 18px;
}
}
& .links {
margin-top: 32px;
display: inline-flex;
flex-direction: column;
& > * {
&:not(:last-child) {
margin-bottom: 24px;
}
}
}
}
& > img {
align-self: center;
margin-right: -48px;
@media (max-width: 670px) {
max-width: 100%;
}
}
}
}
.chin {
composes: g-type-label from global;
}
.description {
composes: g-type-long-body from global;
}

View File

@ -1,46 +0,0 @@
import styles from './HCPCalloutSection.module.css'
import Button from '@hashicorp/react-button'
export default function HcpCalloutSection({
id,
header,
title,
description,
chin,
image,
links,
}) {
return (
<div className={styles.hcpCalloutSection} id={id}>
{header ? (
<div className={styles.header}>
<h2 className="g-type-display-2">{header}</h2>
</div>
) : null}
<div className={styles.content}>
<div className={styles.info}>
<h1 className="g-type-display-1">{title}</h1>
<span className={styles.chin}>{chin}</span>
<p className={styles.description}>{description}</p>
<div className={styles.links}>
{links.map((link, index) => {
const variant = index === 0 ? 'primary' : 'tertiary'
return (
<div key={link.text}>
<Button
title={link.text}
label="Learn more — HCP Vault"
linkType={link.type}
url={link.url}
theme={{ variant, brand: 'neutral', background: 'light' }}
/>
</div>
)
})}
</div>
</div>
<img alt={title} src={image} />
</div>
</div>
)
}

View File

@ -1,18 +0,0 @@
.homepageHero {
& .thirdLinkWrapper {
position: relative;
& a {
position: absolute;
top: -200px;
@media (max-width: 1177px) {
top: -150px;
}
@media (max-width: 1120px) {
display: none;
}
}
}
}

View File

@ -1,69 +0,0 @@
import Hero from '@hashicorp/react-hero'
import Button from '@hashicorp/react-button'
import styles from './HomepageHero.module.css'
import classNames from 'classnames'
/* A simple Facade wrapper around the Hero component */
export default function HomepageHero({
title,
description,
buttons,
uiVideo,
cliVideo,
}) {
return (
<div className={styles.homepageHero}>
<Hero
data={{
backgroundTheme: 'light',
buttons: buttons.slice(0, 2),
centered: false,
description: description,
product: 'vault',
title: title,
videos: [
{
name: 'UI',
playbackRate: 2,
src: [
{
srcType: 'mp4',
url: uiVideo,
},
],
},
{
name: 'CLI',
playbackRate: 2,
src: [
{
srcType: 'mp4',
url: cliVideo,
},
],
},
],
}}
/>
{/* A hack to inject a third link styled in tertiary style
this is very much a non-ideal way to handle this. */}
<div className={classNames('g-grid-container', styles.thirdLinkWrapper)}>
{buttons[2] && (
<div className="third-link">
<Button
// eslint-disable-next-line react/no-array-index-key
linkType={buttons[2].type}
theme={{
variant: 'tertiary-neutral',
brand: 'vault',
background: 'light',
}}
title={buttons[2].title}
url={buttons[2].url}
/>
</div>
)}
</div>
</div>
)
}

View File

@ -1,3 +0,0 @@
# Inline Tag Component
An _experimental_ component, used only on the plugin directory page, to represent "tags" which are meant to be placed inline.

View File

@ -1,5 +0,0 @@
import s from './style.module.css'
export default function InlineTag({ title, color }) {
return <span className={`${s.root} ${s[color]}`}>{title}</span>
}

View File

@ -1,34 +0,0 @@
.root {
background: #333;
color: white;
padding: 5px 9px;
border-radius: 999px;
text-transform: uppercase;
font-weight: bold;
font-size: 0.8em;
margin-left: 5px;
&.red {
background: #c82525;
}
&.orange {
background: #ea721b;
}
&.yellow {
background: #fbb228;
}
&.green {
background: #63c863;
}
&.blue {
background: #358ce8;
}
&.purple {
background: #913799;
}
}

View File

@ -1,82 +0,0 @@
import * as React from 'react'
import classNames from 'classnames'
import Button from '@hashicorp/react-button'
import IoCard, { IoCardProps } from 'components/io-card'
import s from './style.module.css'
interface IoCardContaianerProps {
theme?: 'light' | 'dark'
heading?: string
description?: string
label?: string
cta?: {
url: string
text: string
}
cardsPerRow: 3 | 4
cards: Array<IoCardProps>
}
export default function IoCardContaianer({
theme = 'light',
heading,
description,
label,
cta,
cardsPerRow = 3,
cards,
}: IoCardContaianerProps): React.ReactElement {
return (
<div className={classNames(s.cardContainer, s[theme])}>
{heading || description ? (
<header className={s.header}>
{heading ? <h2 className={s.heading}>{heading}</h2> : null}
{description ? <p className={s.description}>{description}</p> : null}
</header>
) : null}
{cards.length ? (
<>
{label || cta ? (
<header className={s.subHeader}>
{label ? <h3 className={s.label}>{label}</h3> : null}
{cta ? (
<Button
title={cta.text}
url={cta.url}
linkType="inbound"
theme={{
brand: 'neutral',
variant: 'tertiary',
background: theme,
}}
/>
) : null}
</header>
) : null}
<ul
className={classNames(
s.cardList,
cardsPerRow === 3 && s.threeUp,
cardsPerRow === 4 && s.fourUp
)}
style={
{
'--length': cards.length,
} as React.CSSProperties
}
>
{cards.map((card, index) => {
return (
// Index is stable
// eslint-disable-next-line react/no-array-index-key
<li key={index}>
<IoCard variant={theme} {...card} />
</li>
)
})}
</ul>
</>
) : null}
</div>
)
}

View File

@ -1,114 +0,0 @@
.cardContainer {
position: relative;
& + .cardContainer {
margin-top: 64px;
@media (--medium-up) {
margin-top: 132px;
}
}
}
.header {
margin: 0 auto 64px;
text-align: center;
max-width: 600px;
}
.heading {
margin: 0;
composes: g-type-display-2 from global;
@nest .dark & {
color: var(--white);
}
}
.description {
margin: 8px 0 0;
composes: g-type-body-large from global;
@nest .dark & {
color: var(--gray-5);
}
}
.subHeader {
margin: 0 0 32px;
display: flex;
align-items: center;
justify-content: space-between;
@nest .dark & {
color: var(--gray-5);
}
}
.label {
margin: 0;
composes: g-type-display-4 from global;
}
.cardList {
list-style: none;
--minCol: 250px;
--columns: var(--length);
position: relative;
gap: 32px;
padding: 0;
@media (--small) {
display: flex;
overflow-x: auto;
-ms-overflow-style: none;
scrollbar-width: none;
margin: 0;
padding: 6px 24px;
left: 50%;
margin-left: -50vw;
width: 100vw;
/* This is to ensure there is overflow padding right on mobile. */
&::after {
content: '';
display: block;
width: 1px;
flex-shrink: 0;
}
}
@media (--medium-up) {
display: grid;
grid-template-columns: repeat(var(--columns), minmax(var(--minCol), 1fr));
}
&.threeUp {
@media (--medium-up) {
--columns: 3;
--minCol: 0;
}
}
&.fourUp {
@media (--medium-up) {
--columns: 3;
--minCol: 0;
}
@media (--large) {
--columns: 4;
}
}
& > li {
display: flex;
@media (--small) {
flex-shrink: 0;
width: 250px;
}
}
}

View File

@ -1,124 +0,0 @@
import * as React from 'react'
import Link from 'next/link'
import InlineSvg from '@hashicorp/react-inline-svg'
import classNames from 'classnames'
import { IconArrowRight24 } from '@hashicorp/flight-icons/svg-react/arrow-right-24'
import { IconExternalLink24 } from '@hashicorp/flight-icons/svg-react/external-link-24'
import { productLogos } from './product-logos'
import s from './style.module.css'
export interface IoCardProps {
variant?: 'light' | 'gray' | 'dark'
products?: Array<{
name: keyof typeof productLogos
}>
link: {
url: string
type: 'inbound' | 'outbound'
}
inset?: 'none' | 'sm' | 'md'
eyebrow?: string
heading?: string
description?: string
children?: React.ReactNode
}
function IoCard({
variant = 'light',
products,
link,
inset = 'md',
eyebrow,
heading,
description,
children,
}: IoCardProps): React.ReactElement {
const LinkWrapper = ({ className, children }) =>
link.type === 'inbound' ? (
<Link href={link.url}>
<a className={className}>{children}</a>
</Link>
) : (
<a
className={className}
href={link.url}
target="_blank"
rel="noopener noreferrer"
>
{children}
</a>
)
return (
<article className={classNames(s.card)}>
<LinkWrapper className={classNames(s[variant], s[inset])}>
{children ? (
children
) : (
<>
{eyebrow ? <Eyebrow>{eyebrow}</Eyebrow> : null}
{heading ? <Heading>{heading}</Heading> : null}
{description ? <Description>{description}</Description> : null}
</>
)}
<footer className={s.footer}>
{products && (
<ul className={s.products}>
{products.map(({ name }, index) => {
const key = name.toLowerCase()
const version = variant === 'dark' ? 'neutral' : 'color'
return (
// eslint-disable-next-line react/no-array-index-key
<li key={index}>
<InlineSvg
className={s.logo}
src={productLogos[key][version]}
/>
</li>
)
})}
</ul>
)}
<span className={s.linkType}>
{link.type === 'inbound' ? (
<IconArrowRight24 />
) : (
<IconExternalLink24 />
)}
</span>
</footer>
</LinkWrapper>
</article>
)
}
interface EyebrowProps {
children: string
}
function Eyebrow({ children }: EyebrowProps) {
return <p className={s.eyebrow}>{children}</p>
}
interface HeadingProps {
as?: 'h2' | 'h3' | 'h4'
children: React.ReactNode
}
function Heading({ as: Component = 'h2', children }: HeadingProps) {
return <Component className={s.heading}>{children}</Component>
}
interface DescriptionProps {
children: string
}
function Description({ children }: DescriptionProps) {
return <p className={s.description}>{children}</p>
}
IoCard.Eyebrow = Eyebrow
IoCard.Heading = Heading
IoCard.Description = Description
export default IoCard

View File

@ -1,34 +0,0 @@
export const productLogos = {
boundary: {
color: require('@hashicorp/mktg-logos/product/boundary/logomark/color.svg?include'),
neutral: require('@hashicorp/mktg-logos/product/boundary/logomark/white.svg?include'),
},
consul: {
color: require('@hashicorp/mktg-logos/product/consul/logomark/color.svg?include'),
neutral: require('@hashicorp/mktg-logos/product/consul/logomark/white.svg?include'),
},
nomad: {
color: require('@hashicorp/mktg-logos/product/nomad/logomark/color.svg?include'),
neutral: require('@hashicorp/mktg-logos/product/nomad/logomark/white.svg?include'),
},
packer: {
color: require('@hashicorp/mktg-logos/product/packer/logomark/color.svg?include'),
neutral: require('@hashicorp/mktg-logos/product/packer/logomark/white.svg?include'),
},
terraform: {
color: require('@hashicorp/mktg-logos/product/terraform/logomark/color.svg?include'),
neutral: require('@hashicorp/mktg-logos/product/terraform/logomark/white.svg?include'),
},
vagrant: {
color: require('@hashicorp/mktg-logos/product/vagrant/logomark/color.svg?include'),
neutral: require('@hashicorp/mktg-logos/product/vagrant/logomark/white.svg?include'),
},
vault: {
color: require('@hashicorp/mktg-logos/product/vault/logomark/color.svg?include'),
neutral: require('@hashicorp/mktg-logos/product/vault/logomark/white.svg?include'),
},
waypoint: {
color: require('@hashicorp/mktg-logos/product/waypoint/logomark/color.svg?include'),
neutral: require('@hashicorp/mktg-logos/product/waypoint/logomark/white.svg?include'),
},
}

View File

@ -1,148 +0,0 @@
.card {
/* Radii */
--token-radius: 6px;
/* Spacing */
--token-spacing-03: 8px;
--token-spacing-04: 16px;
--token-spacing-05: 24px;
--token-spacing-06: 32px;
/* Elevations */
--token-elevation-mid: 0 2px 3px rgba(101, 106, 118, 0.1),
0 8px 16px -10px rgba(101, 106, 118, 0.2);
--token-elevation-high: 0 2px 3px rgba(101, 106, 118, 0.15),
0 16px 16px -10px rgba(101, 106, 118, 0.2);
/* Transition */
--token-transition: ease-in-out 0.2s;
display: flex;
flex-direction: column;
flex-grow: 1;
min-height: 300px;
& a {
display: flex;
flex-direction: column;
flex-grow: 1;
border-radius: var(--token-radius);
box-shadow: 0 0 0 1px rgba(38, 53, 61, 0.1), var(--token-elevation-mid);
transition: var(--token-transition);
transition-property: background-color, box-shadow;
&:hover {
box-shadow: 0 0 0 2px rgba(38, 53, 61, 0.15), var(--token-elevation-high);
cursor: pointer;
}
/* Variants */
&.dark {
background-color: var(--gray-1);
&:hover {
background-color: var(--gray-2);
}
}
&.gray {
background-color: #f9f9fa;
}
&.light {
background-color: var(--white);
}
/* Spacing */
&.none {
padding: 0;
}
&.sm {
padding: var(--token-spacing-05);
}
&.md {
padding: var(--token-spacing-06);
}
}
}
.eyebrow {
margin: 0;
composes: g-type-label-small from global;
color: var(--gray-3);
@nest .dark & {
color: var(--gray-5);
}
}
.heading {
margin: 0;
composes: g-type-display-5 from global;
color: var(--black);
@nest * + & {
margin-top: var(--token-spacing-05);
}
@nest .dark & {
color: var(--white);
}
}
.description {
margin: 0;
composes: g-type-body-small from global;
color: var(--gray-3);
@nest * + & {
margin-top: var(--token-spacing-03);
}
@nest .dark & {
color: var(--gray-5);
}
}
.footer {
margin-top: auto;
display: flex;
justify-content: space-between;
align-items: flex-end;
padding-top: 32px;
}
.products {
display: flex;
gap: 8px;
margin: 0;
padding: 0;
& > li {
width: 32px;
height: 32px;
display: grid;
place-items: center;
}
& .logo {
display: flex;
& svg {
width: 32px;
height: 32px;
}
}
}
.linkType {
margin-left: auto;
display: flex;
color: var(--black);
@nest .dark & {
color: var(--white);
}
}

View File

@ -1,47 +0,0 @@
import * as React from 'react'
import { DialogOverlay, DialogContent, DialogOverlayProps } from '@reach/dialog'
import { AnimatePresence, motion } from 'framer-motion'
import s from './style.module.css'
export interface IoDialogProps extends DialogOverlayProps {
label: string
}
export default function IoDialog({
isOpen,
onDismiss,
children,
label,
}: IoDialogProps): React.ReactElement {
const AnimatedDialogOverlay = motion(DialogOverlay)
return (
<AnimatePresence>
{isOpen && (
<AnimatedDialogOverlay
className={s.dialogOverlay}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onDismiss={onDismiss}
>
<div className={s.dialogWrapper}>
<motion.div
initial={{ y: 50 }}
animate={{ y: 0 }}
exit={{ y: 50 }}
transition={{ min: 0, max: 100, bounceDamping: 8 }}
style={{ width: '100%', maxWidth: 800 }}
>
<DialogContent className={s.dialogContent} aria-label={label}>
<button onClick={onDismiss} className={s.dialogClose}>
Close
</button>
{children}
</DialogContent>
</motion.div>
</div>
</AnimatedDialogOverlay>
)}
</AnimatePresence>
)
}

View File

@ -1,62 +0,0 @@
.dialogOverlay {
background-color: rgba(0, 0, 0, 0.75);
height: 100%;
left: 0;
overflow-y: auto;
position: fixed;
top: 0;
width: 100%;
z-index: 666666667 /* higher than global nav */;
}
.dialogWrapper {
display: grid;
min-height: 100vh;
padding: 24px;
place-items: center;
}
.dialogContent {
background-color: var(--gray-1);
color: var(--white);
max-width: 800px;
outline: none;
overflow-y: auto;
padding: 24px;
position: relative;
width: 100%;
@media (min-width: 768px) {
padding: 48px;
}
}
.dialogClose {
appearance: none;
background-color: transparent;
border: 0;
composes: g-type-display-5 from global;
cursor: pointer;
margin: 0;
padding: 0;
position: absolute;
color: var(--white);
right: 24px;
top: 24px;
z-index: 1;
@media (min-width: 768px) {
right: 48px;
top: 48px;
}
@nest html[dir='rtl'] & {
left: 24px;
right: auto;
@media (min-width: 768px) {
left: 48px;
right: auto;
}
}
}

View File

@ -1,39 +0,0 @@
import ReactCallToAction from '@hashicorp/react-call-to-action'
import { Products } from '@hashicorp/platform-product-meta'
import s from './style.module.css'
interface IoHomeCallToActionProps {
brand: Products
heading: string
content: string
links: Array<{
text: string
url: string
}>
}
export default function IoHomeCallToAction({
brand,
heading,
content,
links,
}: IoHomeCallToActionProps) {
return (
<div className={s.callToAction}>
<ReactCallToAction
variant="compact"
heading={heading}
content={content}
product={brand}
theme="dark"
links={links.map(({ text, url }, index) => {
return {
text,
url,
type: index === 1 ? 'inbound' : null,
}
})}
/>
</div>
)
}

View File

@ -1,12 +0,0 @@
.callToAction {
margin: 60px auto;
background-image: linear-gradient(52.3deg, #2c2d2f 39.83%, #626264 96.92%);
@media (--medium-up) {
margin: 120px auto;
}
& > * {
background-color: transparent;
}
}

View File

@ -1,80 +0,0 @@
import * as React from 'react'
import Image from 'next/image'
import { isInternalLink } from 'lib/utils'
import { IconExternalLink16 } from '@hashicorp/flight-icons/svg-react/external-link-16'
import { IconArrowRight16 } from '@hashicorp/flight-icons/svg-react/arrow-right-16'
import s from './style.module.css'
interface IoHomeCaseStudiesProps {
heading: string
description: string
primary: Array<{
thumbnail: {
url: string
alt: string
}
link: string
heading: string
}>
secondary: Array<{
link: string
heading: string
}>
}
export default function IoHomeCaseStudies({
heading,
description,
primary,
secondary,
}: IoHomeCaseStudiesProps): React.ReactElement {
return (
<section className={s.root}>
<div className={s.container}>
<header className={s.header}>
<h2 className={s.heading}>{heading}</h2>
<p className={s.description}>{description}</p>
</header>
<div className={s.caseStudies}>
<ul className={s.primary}>
{primary.map((item, index) => {
return (
<li key={index} className={s.primaryItem}>
<a className={s.card} href={item.link}>
<h3 className={s.cardHeading}>{item.heading}</h3>
<Image
className={s.cardThumbnail}
src={item.thumbnail.url}
layout="fill"
objectFit="cover"
alt={item.thumbnail.alt}
/>
</a>
</li>
)
})}
</ul>
<ul className={s.secondary}>
{secondary.map((item, index) => {
return (
<li key={index} className={s.secondaryItem}>
<a className={s.link} href={item.link}>
<span className={s.linkInner}>
<h3 className={s.linkHeading}>{item.heading}</h3>
{isInternalLink(item.link) ? (
<IconArrowRight16 />
) : (
<IconExternalLink16 />
)}
</span>
</a>
</li>
)
})}
</ul>
</div>
</div>
</section>
)
}

View File

@ -1,171 +0,0 @@
.root {
position: relative;
margin: 0 auto;
margin: 60px auto;
max-width: 1600px;
@media (--medium-up) {
margin: 120px auto;
}
}
.container {
composes: g-grid-container from global;
}
.header {
margin-bottom: 32px;
@media (--medium-up) {
max-width: calc(100% * 5 / 12);
}
}
.heading {
margin: 0;
composes: g-type-display-3 from global;
}
.description {
margin: 8px 0 0;
composes: g-type-body from global;
color: var(--gray-3);
}
.caseStudies {
--columns: 1;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 32px;
@media (--medium-up) {
--columns: 12;
}
}
.primary {
--columns: 1;
grid-column: 1 / -1;
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 32px;
@media (--medium-up) {
--columns: 2;
}
@media (--large) {
grid-column: 1 / 9;
}
}
.primaryItem {
display: flex;
}
.card {
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: flex-end;
padding: 32px;
box-shadow: 0 8px 16px -10px rgba(101, 106, 118, 0.2);
background-color: #000;
border-radius: 6px;
color: var(--white);
transition: ease-in-out 0.2s;
transition-property: box-shadow;
min-height: 300px;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
border-radius: 6px;
background-image: linear-gradient(
to bottom,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0.45)
);
transition: opacity ease-in-out 0.2s;
}
&:hover {
box-shadow: 0 2px 3px rgba(101, 106, 118, 0.15),
0 16px 16px -10px rgba(101, 106, 118, 0.2);
&::before {
opacity: 0.75;
}
}
}
.cardThumbnail {
transition: transform 0.4s;
@nest .card:hover & {
transform: scale(1.04);
}
}
.cardHeading {
margin: 0;
composes: g-type-display-4 from global;
z-index: 10;
}
.secondary {
grid-column: 1 / -1;
list-style: none;
margin: 0;
padding: 0;
@media (--large) {
margin-top: -32px;
grid-column: 9 / -1;
}
}
.secondaryItem {
border-bottom: 1px solid var(--gray-5);
}
.link {
display: flex;
width: 100%;
color: var(--black);
}
.linkInner {
display: flex;
width: 100%;
justify-content: space-between;
padding-top: 32px;
padding-bottom: 32px;
transition: transform ease-in-out 0.2s;
@nest .link:hover & {
transform: translateX(4px);
}
& svg {
margin-top: 6px;
flex-shrink: 0;
}
}
.linkHeading {
margin: 0 32px 0 0;
composes: g-type-display-6 from global;
}

View File

@ -1,69 +0,0 @@
import * as React from 'react'
import Image from 'next/image'
import Link from 'next/link'
import { isInternalLink } from 'lib/utils'
import { IconArrowRight16 } from '@hashicorp/flight-icons/svg-react/arrow-right-16'
import s from './style.module.css'
export interface IoHomeFeatureProps {
link?: string
image: {
url: string
alt: string
}
heading: string
description: string
}
export default function IoHomeFeature({
link,
image,
heading,
description,
}: IoHomeFeatureProps): React.ReactElement {
return (
<IoHomeFeatureWrap href={link}>
<div className={s.featureMedia}>
<Image
src={image.url}
width={400}
height={200}
layout="responsive"
alt={image.alt}
/>
</div>
<div className={s.featureContent}>
<h3 className={s.featureHeading}>{heading}</h3>
<p className={s.featureDescription}>{description}</p>
{link ? (
<span className={s.featureCta} aria-hidden={true}>
Learn more{' '}
<span>
<IconArrowRight16 />
</span>
</span>
) : null}
</div>
</IoHomeFeatureWrap>
)
}
function IoHomeFeatureWrap({ href, children }) {
if (!href) {
return <div className={s.feature}>{children}</div>
}
if (isInternalLink(href)) {
return (
<Link href={href}>
<a className={s.feature}>{children}</a>
</Link>
)
}
return (
<a className={s.feature} href={href}>
{children}
</a>
)
}

View File

@ -1,79 +0,0 @@
.feature {
display: flex;
align-items: center;
flex-direction: column;
padding: 32px;
gap: 24px 64px;
border-radius: 6px;
background-color: #f9f9fa;
color: var(--black);
box-shadow: 0 2px 3px rgba(101, 106, 118, 0.1),
0 8px 16px -10px rgba(101, 106, 118, 0.2);
@media (--medium-up) {
flex-direction: row;
}
}
.featureLink {
transition: box-shadow ease-in-out 0.2s;
&:hover {
box-shadow: 0 2px 3px rgba(101, 106, 118, 0.15),
0 16px 16px -10px rgba(101, 106, 118, 0.2);
}
}
.featureMedia {
flex-shrink: 0;
display: flex;
width: 100%;
border-radius: 6px;
overflow: hidden;
border: 1px solid var(--gray-5);
@media (--medium-up) {
width: 300px;
}
@media (--large) {
width: 400px;
}
& > * {
width: 100%;
}
}
.featureContent {
max-width: 520px;
}
.featureHeading {
margin: 0;
composes: g-type-display-4 from global;
}
.featureDescription {
margin: 8px 0 24px;
composes: g-type-body-small from global;
color: var(--gray-3);
}
.featureCta {
display: inline-flex;
align-items: center;
& > span {
display: flex;
margin-left: 12px;
& > svg {
transition: transform 0.2s;
}
}
@nest .feature:hover & span svg {
transform: translateX(2px);
}
}

View File

@ -1,135 +0,0 @@
import * as React from 'react'
import { Products } from '@hashicorp/platform-product-meta'
import Button from '@hashicorp/react-button'
import classNames from 'classnames'
import s from './style.module.css'
interface IoHomeHeroProps {
pattern: string
brand: Products | 'neutral'
heading: string
description: string
ctas: Array<{
title: string
link: string
}>
cards: Array<IoHomeHeroCardProps>
}
export default function IoHomeHero({
pattern,
brand,
heading,
description,
ctas,
cards,
}: IoHomeHeroProps) {
const [loaded, setLoaded] = React.useState(false)
React.useEffect(() => {
setTimeout(() => {
setLoaded(true)
}, 250)
}, [])
return (
<header
className={classNames(s.hero, loaded && s.loaded)}
style={
{
'--pattern': `url(${pattern})`,
} as React.CSSProperties
}
>
<span className={s.pattern} />
<div className={s.container}>
<div className={s.content}>
<h1 className={s.heading}>{heading}</h1>
<p className={s.description}>{description}</p>
{ctas && (
<div className={s.ctas}>
{ctas.map((cta, index) => {
return (
<Button
key={index}
title={cta.title}
url={cta.link}
linkType="inbound"
theme={{
brand: 'neutral',
variant: 'tertiary',
background: 'light',
}}
/>
)
})}
</div>
)}
</div>
{cards && (
<div className={s.cards}>
{cards.map((card, index) => {
return (
<IoHomeHeroCard
key={index}
index={index}
heading={card.heading}
description={card.description}
cta={{
brand: index === 0 ? 'neutral' : brand,
title: card.cta.title,
link: card.cta.link,
}}
subText={card.subText}
/>
)
})}
</div>
)}
</div>
</header>
)
}
interface IoHomeHeroCardProps {
index?: number
heading: string
description: string
cta: {
title: string
link: string
brand?: 'neutral' | Products
}
subText: string
}
function IoHomeHeroCard({
index,
heading,
description,
cta,
subText,
}: IoHomeHeroCardProps): React.ReactElement {
return (
<article
className={s.card}
style={
{
'--index': index,
} as React.CSSProperties
}
>
<h2 className={s.cardHeading}>{heading}</h2>
<p className={s.cardDescription}>{description}</p>
<Button
title={cta.title}
url={cta.link}
theme={{
variant: 'primary',
brand: cta.brand,
}}
/>
<p className={s.cardSubText}>{subText}</p>
</article>
)
}

View File

@ -1,148 +0,0 @@
.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: var(--pattern);
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;
}
& > * {
max-width: 415px;
}
}
.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);
}
}

View File

@ -1,86 +0,0 @@
import * as React from 'react'
import Image from 'next/image'
import Button from '@hashicorp/react-button'
import { Products } from '@hashicorp/platform-product-meta'
import { IoCardProps } from 'components/io-card'
import IoCardContainer from 'components/io-card-container'
import s from './style.module.css'
interface IoHomeInPracticeProps {
brand: Products
pattern: string
heading: string
description: string
cards: Array<IoCardProps>
cta: {
heading: string
description: string
link: string
image: {
url: string
alt: string
width: number
height: number
}
}
}
export default function IoHomeInPractice({
brand,
pattern,
heading,
description,
cards,
cta,
}: IoHomeInPracticeProps) {
return (
<section
className={s.inPractice}
style={
{
'--pattern': `url(${pattern})`,
} as React.CSSProperties
}
>
<div className={s.container}>
<IoCardContainer
theme="dark"
heading={heading}
description={description}
cardsPerRow={3}
cards={cards}
/>
{cta.heading ? (
<div className={s.inPracticeCta}>
<div className={s.inPracticeCtaContent}>
<h3 className={s.inPracticeCtaHeading}>{cta.heading}</h3>
{cta.description ? (
<p className={s.inPracticeCtaDescription}>{cta.description}</p>
) : null}
{cta.link ? (
<Button
title="Learn more"
url={cta.link}
theme={{
brand: brand,
}}
/>
) : null}
</div>
{cta.image?.url ? (
<div className={s.inPracticeCtaMedia}>
<Image
src={cta.image.url}
width={cta.image.width}
height={cta.image.height}
alt={cta.image.alt}
/>
</div>
) : null}
</div>
) : null}
</div>
</section>
)
}

View File

@ -1,98 +0,0 @@
.inPractice {
position: relative;
margin: 60px auto;
padding: 64px 0;
max-width: 1600px;
@media (--medium-up) {
padding: 80px 0;
margin: 120px auto;
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--black);
background-image: url('/img/practice-pattern.svg');
background-repeat: no-repeat;
background-size: 50%;
background-position: top 200px left;
@media (--large) {
border-radius: 6px;
left: 24px;
right: 24px;
background-size: 35%;
background-position: top 64px left;
}
}
}
.container {
composes: g-grid-container from global;
}
.inPracticeCta {
--columns: 1;
position: relative;
margin-top: 64px;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 64px 32px;
@media (--medium-up) {
--columns: 12;
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
bottom: -64px;
background-image: radial-gradient(
42.33% 42.33% at 50% 100%,
#363638 0%,
#000 100%
);
@media (--medium-up) {
bottom: -80px;
}
}
}
.inPracticeCtaContent {
position: relative;
grid-column: 1 / -1;
@media (--medium-up) {
grid-column: 1 / 5;
}
}
.inPracticeCtaMedia {
grid-column: 1 / -1;
@media (--medium-up) {
grid-column: 6 / -1;
}
}
.inPracticeCtaHeading {
margin: 0;
color: var(--white);
composes: g-type-display-3 from global;
}
.inPracticeCtaDescription {
margin: 8px 0 32px;
color: var(--gray-5);
composes: g-type-body from global;
}

View File

@ -1,152 +0,0 @@
import * as React from 'react'
import Image from 'next/image'
import classNames from 'classnames'
import { Products } from '@hashicorp/platform-product-meta'
import Button from '@hashicorp/react-button'
import IoVideoCallout, {
IoHomeVideoCalloutProps,
} from 'components/io-video-callout'
import IoHomeFeature, { IoHomeFeatureProps } from 'components/io-home-feature'
import s from './style.module.css'
interface IoHomeIntroProps {
brand: Products
heading: string
description: string
features?: Array<IoHomeFeatureProps>
offerings?: {
image: {
src: string
width: number
height: number
alt: string
}
list: Array<{
heading: string
description: string
}>
cta?: {
title: string
link: string
}
}
video?: IoHomeVideoCalloutProps
}
export default function IoHomeIntro({
brand,
heading,
description,
features,
offerings,
video,
}: IoHomeIntroProps) {
return (
<section
className={classNames(
s.root,
s[brand],
features && s.withFeatures,
offerings && s.withOfferings
)}
style={
{
'--brand': `var(--${brand})`,
} as React.CSSProperties
}
>
<header className={s.header}>
<div className={s.container}>
<div className={s.headerInner}>
<h2 className={s.heading}>{heading}</h2>
<p className={s.description}>{description}</p>
</div>
</div>
</header>
{features ? (
<ul className={s.features}>
{features.map((feature, index) => {
return (
// Index is stable
// eslint-disable-next-line react/no-array-index-key
<li key={index}>
<div className={s.container}>
<IoHomeFeature
image={{
url: feature.image.url,
alt: feature.image.alt,
}}
heading={feature.heading}
description={feature.description}
link={feature.link}
/>
</div>
</li>
)
})}
</ul>
) : null}
{offerings ? (
<div className={s.offerings}>
{offerings.image ? (
<div className={s.offeringsMedia}>
<Image
src={offerings.image.src}
width={offerings.image.width}
height={offerings.image.height}
alt={offerings.image.alt}
/>
</div>
) : null}
<div className={s.offeringsContent}>
<ul className={s.offeringsList}>
{offerings.list.map((offering, index) => {
return (
// Index is stable
// eslint-disable-next-line react/no-array-index-key
<li key={index}>
<h3 className={s.offeringsListHeading}>
{offering.heading}
</h3>
<p className={s.offeringsListDescription}>
{offering.description}
</p>
</li>
)
})}
</ul>
{offerings.cta ? (
<div className={s.offeringsCta}>
<Button
title={offerings.cta.title}
url={offerings.cta.link}
theme={{
brand: 'neutral',
}}
/>
</div>
) : null}
</div>
</div>
) : null}
{video ? (
<div className={s.video}>
<IoVideoCallout
youtubeId={video.youtubeId}
thumbnail={video.thumbnail}
heading={video.heading}
description={video.description}
person={{
name: video.person.name,
description: video.person.description,
avatar: video.person.avatar,
}}
/>
</div>
) : null}
</section>
)
}

View File

@ -1,169 +0,0 @@
.root {
position: relative;
margin-bottom: 60px;
@media (--medium-up) {
margin-bottom: 120px;
}
&.withOfferings:not(.withFeatures)::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: radial-gradient(
93.55% 93.55% at 50% 0%,
var(--gray-6) 0%,
rgba(242, 242, 243, 0) 100%
);
@media (--large) {
border-radius: 6px;
left: 24px;
right: 24px;
}
}
}
.container {
composes: g-grid-container from global;
}
.header {
padding-top: 64px;
padding-bottom: 64px;
text-align: center;
@nest .withFeatures & {
background-color: var(--brand);
}
@nest .withFeatures.consul & {
color: var(--white);
}
}
.headerInner {
margin: auto;
@media (--medium-up) {
max-width: calc(100% * 7 / 12);
}
}
.heading {
margin: 0;
composes: g-type-display-2 from global;
}
.description {
margin: 24px 0 0;
composes: g-type-body-large from global;
@nest .withOfferings:not(.withFeatures) & {
color: var(--gray-3);
}
}
/*
* Features
*/
.features {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 32px;
& li:first-of-type {
background-image: linear-gradient(
to bottom,
var(--brand) 50%,
var(--white) 50%
);
}
}
/*
* Offerings
*/
.offerings {
--columns: 1;
composes: g-grid-container from global;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 64px 32px;
@media (--medium-up) {
--columns: 12;
}
@nest .features + & {
margin-top: 60px;
@media (--medium-up) {
margin-top: 120px;
}
}
}
.offeringsMedia {
grid-column: 1 / -1;
@media (--medium-up) {
grid-column: 1 / 6;
}
}
.offeringsContent {
grid-column: 1 / -1;
@media (--medium-up) {
grid-column: 7 / -1;
}
}
.offeringsList {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px;
@media (--small) {
grid-template-columns: repeat(1, 1fr);
}
}
.offeringsListHeading {
margin: 0;
composes: g-type-display-4 from global;
}
.offeringsListDescription {
margin: 16px 0 0;
composes: g-type-body-small from global;
}
.offeringsCta {
margin-top: 48px;
}
/*
* Video
*/
.video {
margin-top: 60px;
composes: g-grid-container from global;
@media (--medium-up) {
margin-top: 120px;
}
}

View File

@ -1,79 +0,0 @@
import * as React from 'react'
import classNames from 'classnames'
import { Products } from '@hashicorp/platform-product-meta'
import { IconArrowRight16 } from '@hashicorp/flight-icons/svg-react/arrow-right-16'
import s from './style.module.css'
interface IoHomePreFooterProps {
brand: Products
heading: string
description: string
ctas: [IoHomePreFooterCard, IoHomePreFooterCard, IoHomePreFooterCard]
}
export default function IoHomePreFooter({
brand,
heading,
description,
ctas,
}: IoHomePreFooterProps) {
return (
<div className={classNames(s.preFooter, s[brand])}>
<div className={s.container}>
<div className={s.content}>
<h2 className={s.heading}>{heading}</h2>
<p className={s.description}>{description}</p>
</div>
<div className={s.cards}>
{ctas.map((cta, index) => {
return (
<IoHomePreFooterCard
key={index}
brand={brand}
link={cta.link}
heading={cta.heading}
description={cta.description}
cta={cta.cta}
/>
)
})}
</div>
</div>
</div>
)
}
interface IoHomePreFooterCard {
brand?: string
link: string
heading: string
description: string
cta: string
}
function IoHomePreFooterCard({
brand,
link,
heading,
description,
cta,
}: IoHomePreFooterCard): React.ReactElement {
return (
<a
href={link}
className={s.card}
style={
{
'--primary': `var(--${brand})`,
'--secondary': `var(--${brand}-secondary)`,
} as React.CSSProperties
}
>
<h3 className={s.cardHeading}>{heading}</h3>
<p className={s.cardDescription}>{description}</p>
<span className={s.cardCta}>
{cta} <IconArrowRight16 />
</span>
</a>
)
}

View File

@ -1,119 +0,0 @@
.preFooter {
margin: 60px auto;
}
.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(1) {
@nest .consul & {
color: var(--white);
}
}
&: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);
}
}

View File

@ -1,71 +0,0 @@
import Image from 'next/image'
import * as React from 'react'
import { Products } from '@hashicorp/platform-product-meta'
import classNames from 'classnames'
import Button from '@hashicorp/react-button'
import s from './style.module.css'
interface IoUsecaseCallToActionProps {
brand: Products
theme?: 'light' | 'dark'
heading: string
description: string
links: Array<{
text: string
url: string
}>
// TODO document intended usage
pattern: string
}
export default function IoUsecaseCallToAction({
brand,
theme,
heading,
description,
links,
pattern,
}: IoUsecaseCallToActionProps): React.ReactElement {
return (
<div
className={classNames(s.callToAction, s[theme])}
style={
{
'--background-color': `var(--${brand})`,
} as React.CSSProperties
}
>
<h2 className={s.heading}>{heading}</h2>
<div className={s.content}>
<p className={s.description}>{description}</p>
<div className={s.links}>
{links.map((link, index) => {
return (
<Button
// Index is stable
// eslint-disable-next-line react/no-array-index-key
key={index}
title={link.text}
url={link.url}
theme={{
brand: 'neutral',
variant: index === 0 ? 'primary' : 'secondary',
background: theme,
}}
/>
)
})}
</div>
</div>
<div className={s.pattern}>
<Image
src={pattern}
layout="fill"
objectFit="cover"
objectPosition="center left"
alt=""
/>
</div>
</div>
)
}

View File

@ -1,66 +0,0 @@
.callToAction {
--columns: 1;
position: relative;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 0 32px;
padding: 32px;
background-color: var(--background-color);
border-radius: 6px;
&.light {
color: var(--black);
}
&.dark {
color: var(--white);
}
@media (--medium-up) {
--columns: 12;
padding: 0;
}
}
.heading {
grid-column: 1 / -1;
margin: 0 0 16px;
composes: g-type-display-3 from global;
@media (--medium-up) {
grid-column: 1 / 6;
padding: 88px 32px 88px 64px;
}
}
.content {
grid-column: 1 / -1;
@media (--medium-up) {
grid-column: 6 / 11;
padding: 88px 0;
}
}
.description {
margin: 0 0 32px;
composes: g-type-body-large from global;
}
.links {
display: flex;
flex-wrap: wrap;
gap: 16px 32px;
}
.pattern {
position: relative;
display: none;
@media (--medium-up) {
grid-column: 11 / -1;
display: flex;
}
}

View File

@ -1,86 +0,0 @@
import * as React from 'react'
import Image from 'next/image'
import Button from '@hashicorp/react-button'
import s from './style.module.css'
interface IoUsecaseCustomerProps {
media: {
src: string
width: string
height: string
alt: string
}
logo: {
src: string
width: string
height: string
alt: string
}
heading: string
description: string
stats?: Array<{
value: string
key: string
}>
link: string
}
export default function IoUsecaseCustomer({
media,
logo,
heading,
description,
stats,
link,
}: IoUsecaseCustomerProps): React.ReactElement {
return (
<section className={s.customer}>
<div className={s.container}>
<div className={s.columns}>
<div className={s.media}>
{/* eslint-disable-next-line jsx-a11y/alt-text */}
<Image {...media} layout="responsive" />
</div>
<div className={s.content}>
<div className={s.eyebrow}>
<div className={s.eyebrowLogo}>
{/* eslint-disable-next-line jsx-a11y/alt-text */}
<Image {...logo} />
</div>
<span className={s.eyebrowLabel}>Customer case study</span>
</div>
<h2 className={s.heading}>{heading}</h2>
<p className={s.description}>{description}</p>
{link ? (
<div className={s.cta}>
<Button
title="Read more"
url={link}
theme={{
brand: 'neutral',
variant: 'secondary',
background: 'dark',
}}
/>
</div>
) : null}
</div>
</div>
{stats.length > 0 ? (
<ul className={s.stats}>
{stats.map(({ key, value }, index) => {
return (
// Index is stable
// eslint-disable-next-line react/no-array-index-key
<li key={index}>
<p className={s.value}>{value}</p>
<p className={s.key}>{key}</p>
</li>
)
})}
</ul>
) : null}
</div>
</section>
)
}

View File

@ -1,118 +0,0 @@
.customer {
position: relative;
background-color: var(--black);
color: var(--white);
padding-bottom: 64px;
@media (--medium-up) {
padding-bottom: 132px;
}
}
.container {
composes: g-grid-container from global;
}
.columns {
--columns: 1;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 64px 32px;
@media (--medium-up) {
--columns: 12;
}
}
.media {
margin-top: -64px;
grid-column: 1 / -1;
@media (--medium-up) {
grid-column: 1 / 7;
}
}
.content {
grid-column: 1 / -1;
@media (--medium-up) {
padding-top: 64px;
grid-column: 8 / -1;
}
}
.eyebrow {
display: flex;
}
.eyebrowLogo {
display: flex;
max-width: 120px;
}
.eyebrowLabel {
padding-top: 8px;
padding-bottom: 8px;
padding-left: 12px;
margin-left: 12px;
border-left: 1px solid var(--gray-5);
align-self: center;
composes: g-type-label-small-strong from global;
}
.heading {
margin: 32px 0 24px;
composes: g-type-display-2 from global;
}
.description {
margin: 0;
composes: g-type-body from global;
}
.cta {
margin-top: 32px;
}
.stats {
--columns: 1;
list-style: none;
margin: 64px 0 0;
padding: 0;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 32px;
@media (--medium-up) {
--columns: 12;
margin-top: 132px;
}
& > li {
border-top: 1px solid var(--gray-2);
grid-column: span 4;
}
}
.value {
margin: 0;
padding-top: 32px;
font-family: var(--font-display);
font-size: 50px;
font-weight: 700;
line-height: 1;
@media (--large) {
font-size: 80px;
}
}
.key {
margin: 12px 0 0;
composes: g-type-display-4 from global;
color: var(--gray-3);
}

View File

@ -1,41 +0,0 @@
import * as React from 'react'
import Image from 'next/image'
import s from './style.module.css'
interface IoUsecaseHeroProps {
eyebrow: string
heading: string
description: string
pattern?: string
}
export default function IoUsecaseHero({
eyebrow,
heading,
description,
pattern,
}: IoUsecaseHeroProps): React.ReactElement {
return (
<header className={s.hero}>
<div className={s.container}>
<div className={s.pattern}>
{pattern ? (
<Image
src={pattern}
layout="responsive"
width={420}
height={500}
priority={true}
alt=""
/>
) : null}
</div>
<div className={s.content}>
<p className={s.eyebrow}>{eyebrow}</p>
<h1 className={s.heading}>{heading}</h1>
<p className={s.description}>{description}</p>
</div>
</div>
</header>
)
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -1,83 +0,0 @@
.hero {
position: relative;
max-width: 1600px;
margin-right: auto;
margin-left: auto;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: radial-gradient(
95.97% 95.97% at 50% 100%,
#f2f2f3 0%,
rgba(242, 242, 243, 0) 100%
);
@media (--medium-up) {
border-radius: 6px;
left: 24px;
right: 24px;
}
}
}
.container {
@media (--medium-up) {
display: grid;
grid-template-columns: 1fr max-content 1fr;
gap: 32px;
}
}
.pattern {
margin-left: 24px;
transform: translateY(24px);
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-end;
@media (--small) {
display: none;
}
@media (--medium) {
& > * {
display: none !important;
}
}
}
.content {
position: relative;
max-width: 520px;
width: 100%;
margin-right: auto;
margin-left: auto;
padding: 64px 24px;
@media (--medium-up) {
padding-top: 132px;
padding-bottom: 132px;
}
}
.eyebrow {
margin: 0;
composes: g-type-label-strong from global;
}
.heading {
margin: 24px 0;
composes: g-type-display-1 from global;
}
.description {
margin: 0;
composes: g-type-body-large from global;
color: var(--gray-2);
}

View File

@ -1,81 +0,0 @@
import * as React from 'react'
import { Products } from '@hashicorp/platform-product-meta'
import classNames from 'classnames'
import Image from 'next/image'
import Button from '@hashicorp/react-button'
import s from './style.module.css'
interface IoUsecaseSectionProps {
brand?: Products | 'neutral'
bottomIsFlush?: boolean
eyebrow: string
heading: string
description: string
media?: {
src: string
width: string
height: string
alt: string
}
cta?: {
text: string
link: string
}
}
export default function IoUsecaseSection({
brand = 'neutral',
bottomIsFlush = false,
eyebrow,
heading,
description,
media,
cta,
}: IoUsecaseSectionProps): React.ReactElement {
return (
<section
className={classNames(s.section, s[brand], bottomIsFlush && s.isFlush)}
>
<div className={s.container}>
<p className={s.eyebrow}>{eyebrow}</p>
<div className={s.columns}>
<div className={s.column}>
<h2 className={s.heading}>{heading}</h2>
{media?.src ? (
<div
className={s.description}
dangerouslySetInnerHTML={{
__html: description,
}}
/>
) : null}
{cta?.link && cta?.text ? (
<div className={s.cta}>
<Button
title={cta.text}
url={cta.link}
theme={{
brand: brand,
}}
/>
</div>
) : null}
</div>
<div className={s.column}>
{media?.src ? (
// eslint-disable-next-line jsx-a11y/alt-text
<Image {...media} />
) : (
<div
className={s.description}
dangerouslySetInnerHTML={{
__html: description,
}}
/>
)}
</div>
</div>
</div>
</section>
)
}

View File

@ -1,106 +0,0 @@
.section {
position: relative;
max-width: 1600px;
margin-right: auto;
margin-left: auto;
padding-top: 64px;
padding-bottom: 64px;
@media (--medium-up) {
padding-top: 132px;
padding-bottom: 132px;
}
& + .section {
padding-bottom: 132px;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--gray-6);
opacity: 0.4;
@media (--medium-up) {
border-radius: 6px;
left: 24px;
right: 24px;
}
}
}
&.isFlush {
padding-bottom: 96px;
@media (--medium-up) {
padding-bottom: 164px;
}
&::before {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
}
.container {
composes: g-grid-container from global;
}
.columns {
--columns: 1;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 32px;
@media (--medium-up) {
--columns: 12;
}
}
.column {
&:nth-child(1) {
@media (--medium-up) {
grid-column: 1 / 7;
}
}
&:nth-child(2) {
@media (--medium-up) {
grid-column: 8 / -1;
padding-top: 16px;
}
}
}
.eyebrow {
margin: 0;
composes: g-type-display-5 from global;
}
.heading {
margin: 16px 0 32px;
padding-bottom: 32px;
composes: g-type-display-3 from global;
border-bottom: 1px solid var(--black);
}
.description {
composes: g-type-body from global;
& > p {
margin: 0;
& + p {
margin-top: 16px;
}
}
}
.cta {
margin-top: 32px;
}

View File

@ -1,80 +0,0 @@
import * as React from 'react'
import Image from 'next/image'
import ReactPlayer from 'react-player'
import VisuallyHidden from '@reach/visually-hidden'
import IoDialog from 'components/io-dialog'
import PlayIcon from './play-icon'
import s from './style.module.css'
export interface IoHomeVideoCalloutProps {
youtubeId: string
thumbnail: string
heading: string
description: string
person: {
avatar: string
name: string
description: string
}
}
export default function IoVideoCallout({
youtubeId,
thumbnail,
heading,
description,
person,
}: IoHomeVideoCalloutProps): React.ReactElement {
const [showDialog, setShowDialog] = React.useState(false)
const showVideo = () => setShowDialog(true)
const hideVideo = () => setShowDialog(false)
return (
<>
<figure className={s.videoCallout}>
<button className={s.thumbnail} onClick={showVideo}>
<VisuallyHidden>Play video</VisuallyHidden>
<PlayIcon />
<Image src={thumbnail} layout="fill" objectFit="cover" alt="" />
</button>
<figcaption className={s.content}>
<h3 className={s.heading}>{heading}</h3>
<p className={s.description}>{description}</p>
{person && (
<div className={s.person}>
{person.avatar ? (
<div className={s.personThumbnail}>
<Image
src={person.avatar}
width={52}
height={52}
alt={`${person.name} avatar`}
/>
</div>
) : null}
<div>
<p className={s.personName}>{person.name}</p>
<p className={s.personDescription}>{person.description}</p>
</div>
</div>
)}
</figcaption>
</figure>
<IoDialog
isOpen={showDialog}
onDismiss={hideVideo}
label={`${heading} video}`}
>
<h2 className={s.videoHeading}>{heading}</h2>
<div className={s.video}>
<ReactPlayer
url={`https://www.youtube.com/watch?v=${youtubeId}`}
width="100%"
height="100%"
playing={true}
controls={true}
/>
</div>
</IoDialog>
</>
)
}

View File

@ -1,23 +0,0 @@
import * as React from 'react'
export default function PlayIcon(): React.ReactElement {
return (
<svg
width="96"
height="96"
viewBox="0 0 96 96"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="48" cy="48" r="48" fill="#fff" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="m63.254 46.653-22.75-14.4a1.647 1.647 0 0 0-1.657-.057c-.522.28-.847.82-.847 1.405V62.4c0 .584.325 1.123.847 1.403a1.639 1.639 0 0 0 1.657-.057l22.75-14.4c.465-.294.746-.802.746-1.346 0-.545-.281-1.052-.746-1.347Z"
fill="#fff"
stroke="#000"
strokeWidth="2"
/>
</svg>
)
}

View File

@ -1,128 +0,0 @@
.videoCallout {
--columns: 1;
margin: 0;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
gap: 32px;
background-color: var(--black);
border-radius: 6px;
overflow: hidden;
@media (--medium-up) {
--columns: 12;
}
}
.thumbnail {
position: relative;
display: grid;
place-items: center;
grid-column: 1 / -1;
background-color: transparent;
border: 0;
cursor: pointer;
padding: 96px 32px;
min-height: 300px;
@media (--medium-up) {
grid-column: 1 / 7;
}
@media (--large) {
grid-column: 1 / 9;
}
& > svg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
@media (--small) {
width: 52px;
height: 52px;
}
}
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #000;
opacity: 0.45;
transition: opacity ease-in-out 0.2s;
}
&:hover::after {
opacity: 0.2;
}
}
.content {
padding: 32px;
grid-column: 1 / -1;
@media (--medium-up) {
padding: 80px 32px;
grid-column: 7 / -1;
}
@media (--large) {
grid-column: 9 / -1;
}
}
.heading {
margin: 0;
composes: g-type-display-4 from global;
color: var(--white);
}
.description {
margin: 8px 0 0;
composes: g-type-body-small from global;
color: var(--white);
}
.person {
margin-top: 64px;
display: flex;
align-items: center;
gap: 16px;
}
.personThumbnail {
display: flex;
border-radius: 9999px;
overflow: hidden;
}
.personName {
margin: 0;
composes: g-type-body-strong from global;
color: var(--white);
}
.personDescription {
margin: 4px 0 0;
composes: g-type-label-strong from global;
color: var(--gray-3);
}
.videoHeading {
margin-top: 0;
margin-bottom: 32px;
padding-right: 100px;
composes: g-type-display-4 from global;
}
.video {
position: relative;
background-color: var(--gray-2);
aspect-ratio: 16 / 9;
}

View File

@ -1,41 +0,0 @@
import Subnav from '@hashicorp/react-subnav'
import classNames from 'classnames'
import { useRouter } from 'next/router'
import s from './style.module.css'
export default function ProductSubnav({ menuItems }) {
const router = useRouter()
return (
<Subnav
className={classNames('g-product-subnav', s.subnav)}
hideGithubStars={true}
titleLink={{
text: 'HashiCorp Vault',
url: '/',
}}
ctaLinks={[
{
text: 'GitHub',
url: 'https://www.github.com/hashicorp/vault',
},
{
text: 'Try Cloud',
url: 'https://portal.cloud.hashicorp.com/sign-up?utm_source=vault_io&utm_content=top_nav_vault',
},
{
text: 'Download',
url: '/downloads',
theme: {
brand: 'vault',
},
},
]}
currentPath={router.asPath}
menuItems={menuItems}
menuItemsAlign="right"
constrainWidth
matchOnBasePath
/>
)
}

View File

@ -1,3 +0,0 @@
.subnav {
border-top: 1px solid transparent;
}

View File

@ -1,28 +0,0 @@
import Button from '@hashicorp/react-button'
export default function UseCaseCtaSection() {
return (
<section className="g-section-block g-cta-section">
<div>
<h2 className="g-type-display-2">Ready to get started?</h2>
<Button
url="/downloads"
title="Download"
label="Download CLI"
linkType="download"
className="g-btn"
theme={{
variant: 'primary',
brand: 'neutral',
}}
/>
<Button
url="/docs"
title="Explore Docs"
className="g-btn"
theme={{ variant: 'secondary' }}
/>
</div>
</section>
)
}

View File

@ -1,49 +0,0 @@
.g-section-block.g-cta-section {
-webkit-box-align: center;
align-items: center;
background: var(--vault-secondary);
display: -webkit-box;
display: flex;
-webkit-box-pack: center;
justify-content: center;
padding-left: 15px;
padding-right: 15px;
position: relative;
text-align: center;
color: var(--black);
& .g-btn.white {
background: var(--white);
border: 2px solid var(--white);
color: var(--gray-2);
&:hover {
background-color: var(--gray-1);
border-color: var(--gray-1);
color: var(--white);
& path {
fill: var(--white);
}
}
}
& .g-btn.white-outline {
background: none;
border: 2px solid var(--white);
color: var(--white);
&:hover {
background-color: var(--white);
color: var(--gray-2);
}
}
& .g-btn.download svg {
margin: 0 4px -4px 0;
}
& .g-btn + .g-btn {
margin-left: 18px;
}
}

View File

@ -1,13 +0,0 @@
export const ALERT_BANNER_ACTIVE = false
// https://github.com/hashicorp/web-components/tree/master/packages/alert-banner
export default {
tag: 'Blog post',
url: 'https://www.hashicorp.com/blog/a-new-chapter-for-hashicorp',
text:
'HashiCorp shares have begun trading on the Nasdaq. Read the blog from our founders, Mitchell Hashimoto and Armon Dadgar.',
linkText: 'Read the post',
// Set the expirationDate prop with a datetime string (e.g. '2020-01-31T12:00:00-07:00')
// if you'd like the component to stop showing at or after a certain date
expirationDate: '2021-12-17T23:00:00-07:00',
}

View File

@ -1,2 +0,0 @@
export const productName = 'Vault'
export const productSlug = 'vault'

View File

@ -1,53 +0,0 @@
export const VERSION = '1.10.0'
export const CHANGELOG_URL =
'https://github.com/hashicorp/vault/blob/main/CHANGELOG.md#1100'
// HashiCorp officially supported package managers
export const packageManagers = [
{
label: 'Homebrew',
commands: ['brew tap hashicorp/tap', 'brew install hashicorp/tap/vault'],
os: 'darwin',
},
{
label: 'Ubuntu/Debian',
commands: [
'curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -',
'sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"',
'sudo apt-get update && sudo apt-get install vault',
],
os: 'linux',
},
{
label: 'CentOS/RHEL',
commands: [
'sudo yum install -y yum-utils',
'sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo',
'sudo yum -y install vault',
],
os: 'linux',
},
{
label: 'Fedora',
commands: [
'sudo dnf install -y dnf-plugins-core',
'sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/fedora/hashicorp.repo',
'sudo dnf -y install vault',
],
os: 'linux',
},
{
label: 'Amazon Linux',
commands: [
'sudo yum install -y yum-utils',
'sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo',
'sudo yum -y install vault',
],
os: 'linux',
},
{
label: 'Homebrew',
commands: ['brew tap hashicorp/tap', 'brew install hashicorp/tap/vault'],
os: 'linux',
},
]

1
website/global.d.ts vendored
View File

@ -1 +0,0 @@
/// <reference types="@hashicorp/platform-types" />

View File

@ -1,6 +0,0 @@
{
"compilerOptions": {
"baseUrl": "."
},
"exclude": ["node_modules", ".next", "out"]
}

View File

@ -1,52 +0,0 @@
import query from './query.graphql'
import ProductSubnav from 'components/subnav'
import Footer from 'components/footer'
import { open } from '@hashicorp/react-consent-manager'
export default function StandardLayout(props: Props): React.ReactElement {
const { useCaseNavItems } = props.data
return (
<>
<ProductSubnav
menuItems={[
{ text: 'Overview', url: '/' },
{
text: 'Use Cases',
submenu: useCaseNavItems
.sort((a, b) => a.text.localeCompare(b.text))
.map((item) => {
return {
text: item.text,
url: `/use-cases/${item.url}`,
}
}),
},
{
text: 'Enterprise',
url: 'https://www.hashicorp.com/products/vault/enterprise',
},
'divider',
{ text: 'Tutorials', url: 'https://learn.hashicorp.com/vault' },
{ text: 'Docs', url: '/docs' },
{ text: 'API', url: '/api-docs' },
{ text: 'Community', url: '/community' },
]}
/>
{props.children}
<Footer openConsentManager={open} />
</>
)
}
StandardLayout.rivetParams = {
query,
dependencies: [],
}
interface Props {
children: React.ReactChildren
data: {
useCaseNavItems: Array<{ url: string; text: string }>
}
}

View File

@ -1,6 +0,0 @@
query UseCasesQuery {
useCaseNavItems: allVaultUseCases {
url: slug
text: heroHeading
}
}

View File

@ -1,22 +0,0 @@
import { ConsentManagerService } from '@hashicorp/react-consent-manager/types'
const localConsentManagerServices: ConsentManagerService[] = [
{
name: 'Qualified Chatbot',
description:
'Qualified is a chatbot service that allows visitors to chat with our sales staff through the website.',
category: 'Email Marketing',
url: 'https://js.qualified.com/qualified.js?token=CWQA3q9CaEKHNF2t',
async: true,
},
{
name: 'Demandbase Tag',
description:
'The Demandbase tag is a tracking service to identify website visitors and measure interest on our website.',
category: 'Analytics',
url: 'https://tag.demandbase.com/960ab0a0f20fb102.min.js',
async: true,
},
]
export default localConsentManagerServices

View File

@ -1,18 +0,0 @@
export const isInternalLink = (link: string): boolean => {
if (
link.startsWith('/') ||
link.startsWith('#') ||
link.startsWith('https://vaultproject.io') ||
link.startsWith('https://www.vaultproject.io')
) {
return true
}
return false
}
export const chunk = (arr, chunkSize = 1, cache = []) => {
const tmp = [...arr]
if (chunkSize <= 0) return cache
while (tmp.length) cache.push(tmp.splice(0, chunkSize))
return cache
}

View File

@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@ -1,38 +0,0 @@
const withHashicorp = require('@hashicorp/platform-nextjs-plugin')
const redirects = require('./redirects')
// log out our primary environment variables for clarity in build logs
console.log(`HASHI_ENV: ${process.env.HASHI_ENV}`)
console.log(`NODE_ENV: ${process.env.NODE_ENV}`)
console.log(`VERCEL_ENV: ${process.env.VERCEL_ENV}`)
console.log(`MKTG_CONTENT_API: ${process.env.MKTG_CONTENT_API}`)
console.log(`ENABLE_VERSIONED_DOCS: ${process.env.ENABLE_VERSIONED_DOCS}`)
module.exports = withHashicorp({
dato: {
// This token is safe to be in this public repository, it only has access to content that is publicly viewable on the website
token: '88b4984480dad56295a8aadae6caad',
},
nextOptimizedImages: true,
transpileModules: ['@hashicorp/flight-icons'],
})({
svgo: { plugins: [{ removeViewBox: false }] },
rewrites: () => [
{
source: '/api/:path*',
destination: '/api-docs/:path*',
},
],
redirects: () => redirects,
env: {
HASHI_ENV: process.env.HASHI_ENV || 'development',
SEGMENT_WRITE_KEY: 'OdSFDq9PfujQpmkZf03dFpcUlywme4sC',
BUGSNAG_CLIENT_KEY: '07ff2d76ce27aded8833bf4804b73350',
BUGSNAG_SERVER_KEY: 'fb2dc40bb48b17140628754eac6c1b11',
ENABLE_VERSIONED_DOCS: process.env.ENABLE_VERSIONED_DOCS || false,
},
images: {
domains: ['www.datocms-assets.com'],
disableStaticImages: true,
},
})

20027
website/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,53 +3,12 @@
"description": "HashiCorp Vault documentation website",
"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/nextjs-scripts": "^19.0.3",
"@hashicorp/platform-analytics": "^0.2.0",
"@hashicorp/platform-code-highlighting": "^0.1.2",
"@hashicorp/platform-markdown-utils": "^0.1.3",
"@hashicorp/platform-runtime-error-monitoring": "^0.1.0",
"@hashicorp/platform-util": "^0.1.0",
"@hashicorp/react-alert-banner": "^7.0.1",
"@hashicorp/react-button": "^6.0.1",
"@hashicorp/react-call-to-action": "^4.1.1",
"@hashicorp/react-consent-manager": "^7.0.1",
"@hashicorp/react-docs-page": "^14.14.3",
"@hashicorp/react-featured-slider": "^5.0.1",
"@hashicorp/react-head": "^3.1.2",
"@hashicorp/react-hero": "^8.0.2",
"@hashicorp/react-image": "^4.0.3",
"@hashicorp/react-inline-svg": "^6.0.3",
"@hashicorp/react-markdown-page": "^1.4.3",
"@hashicorp/react-product-downloads-page": "^2.5.3",
"@hashicorp/react-section-header": "^5.0.4",
"@hashicorp/react-subnav": "^9.3.2",
"@hashicorp/react-text-splits": "^3.2.7",
"@hashicorp/react-use-cases": "^5.0.0",
"@hashicorp/react-vertical-text-block-list": "^7.0.0",
"@reach/dialog": "^0.16.2",
"@reach/visually-hidden": "^0.16.0",
"framer-motion": "^5.3.0",
"next": "^11.1.2",
"next-mdx-remote": "^3.0.8",
"next-remote-watch": "1.0.0",
"react": "^17.0.2",
"react-datocms": "^1.6.6",
"react-dom": "^17.0.2",
"react-player": "^2.9.0"
},
"dependencies": {},
"devDependencies": {
"@hashicorp/platform-cli": "^1.2.0",
"@hashicorp/platform-nextjs-plugin": "^1.0.1",
"@hashicorp/platform-types": "^0.1.1",
"@types/react": "^17.0.27",
"dart-linkcheck": "^2.0.15",
"prettier": "^2.4.1",
"dart-linkcheck": "2.0.15",
"simple-git-hooks": "^2.6.1",
"typescript": "^4.4.3"
"prettier": "2.2.1"
},
"engines": {
"npm": ">=7.0.0",
@ -62,16 +21,13 @@
},
"scripts": {
"build": "./scripts/website-build.sh",
"dynamic": "NODE_ENV=production next build && next start",
"export": "next export",
"format": "next-hashicorp format",
"generate:component": "next-hashicorp generate component",
"generate:readme": "next-hashicorp markdown-blocks README.md",
"linkcheck": "linkcheck https://www.vaultproject.io",
"lint": "next-hashicorp lint",
"postinstall": "simple-git-hooks",
"start": "./scripts/website-start.sh",
"static": "npm run build && npm run export && cp _redirects out/."
"start": "./scripts/website-start.sh"
},
"simple-git-hooks": {
"pre-commit": "cd website && ./node_modules/.bin/next-hashicorp precommit"

View File

@ -1,2 +0,0 @@
import NotFound from './not-found'
export default NotFound

View File

@ -1,86 +0,0 @@
import './style.css'
import '@hashicorp/platform-util/nprogress/style.css'
import Router from 'next/router'
import Head from 'next/head'
import rivetQuery from '@hashicorp/nextjs-scripts/dato/client'
import { ErrorBoundary } from '@hashicorp/platform-runtime-error-monitoring'
import createConsentManager from '@hashicorp/react-consent-manager/loader'
import localConsentManagerServices from 'lib/consent-manager-services'
import NProgress from '@hashicorp/platform-util/nprogress'
import useFathomAnalytics from '@hashicorp/platform-analytics'
import useAnchorLinkAnalytics from '@hashicorp/platform-util/anchor-link-analytics'
import HashiHead from '@hashicorp/react-head'
import Error from './_error'
import AlertBanner from '@hashicorp/react-alert-banner'
import alertBannerData, { ALERT_BANNER_ACTIVE } from '../data/alert-banner'
import StandardLayout from 'layouts/standard'
NProgress({ Router })
const { ConsentManager } = createConsentManager({
preset: 'oss',
otherServices: [...localConsentManagerServices],
})
export default function App({ Component, pageProps, layoutData }) {
useFathomAnalytics()
useAnchorLinkAnalytics()
const Layout = Component.layout ?? StandardLayout
return (
<ErrorBoundary FallbackComponent={Error}>
<HashiHead
is={Head}
title="Vault by HashiCorp"
siteName="Vault by HashiCorp"
description="Vault secures, stores, and tightly controls access to tokens, passwords, certificates, API keys, and other secrets in modern computing. Vault handles leasing, key revocation, key rolling, auditing, and provides secrets as a service through a unified API."
image="https://www.vaultproject.io/img/og-image.png"
icon={[
{
href: 'https://www.datocms-assets.com/2885/1597163356-vault-favicon.png?h=16&w=16',
type: 'image/png',
sizes: '16x16',
},
{
href: 'https://www.datocms-assets.com/2885/1597163356-vault-favicon.png?h=32&w=32',
type: 'image/png',
sizes: '32x32',
},
{
href: 'https://www.datocms-assets.com/2885/1597163356-vault-favicon.png?h=96&w=96',
type: 'image/png',
sizes: '96x96',
},
{
href: 'https://www.datocms-assets.com/2885/1597163356-vault-favicon.png?h=192&w=192',
type: 'image/png',
sizes: '192x192',
},
]}
/>
{ALERT_BANNER_ACTIVE && (
<AlertBanner {...alertBannerData} product="vault" hideOnMobile />
)}
<Layout {...(layoutData && { data: layoutData })}>
<Component {...pageProps} />
</Layout>
<ConsentManager className="g-consent-manager" />
</ErrorBoundary>
)
}
App.getInitialProps = async ({ Component, ctx }) => {
const layoutQuery = Component.layout
? Component.layout?.rivetParams ?? null
: StandardLayout.rivetParams
const layoutData = layoutQuery ? await rivetQuery(layoutQuery) : null
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps, layoutData }
}

View File

@ -1,29 +0,0 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'
import HashiHead from '@hashicorp/react-head'
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html lang="en">
<Head>
<HashiHead />
</Head>
<body>
<Main />
<NextScript />
<script
noModule
dangerouslySetInnerHTML={{
__html: `window.MSInputMethodContext && document.documentMode && document.write('<script src="/ie-custom-properties.js"><\\x2fscript>');`,
}}
/>
</body>
</Html>
)
}
}

View File

@ -1,14 +0,0 @@
import NotFound from './404'
import Bugsnag from '@hashicorp/platform-runtime-error-monitoring'
function Error({ statusCode }) {
return <NotFound statusCode={statusCode} />
}
Error.getInitialProps = ({ res, err }) => {
if (err) Bugsnag.notify(err)
const statusCode = res ? res.statusCode : err ? err.statusCode : 404
return { statusCode }
}
export default Error

View File

@ -1,38 +0,0 @@
import { productName, productSlug } from 'data/metadata'
import DocsPage from '@hashicorp/react-docs-page'
// Imports below are used in server-side only
import { getStaticGenerationFunctions } from '@hashicorp/react-docs-page/server'
const NAV_DATA_FILE_HIDDEN = 'data/api-docs-nav-data-hidden.json'
const NAV_DATA_FILE = 'data/api-docs-nav-data.json'
const CONTENT_DIR = 'content/api-docs'
const basePath = 'api-docs'
export default function DocsLayout(props) {
return (
<DocsPage
product={{ name: productName, slug: productSlug }}
baseRoute={basePath}
staticProps={props}
/>
)
}
const { getStaticPaths, getStaticProps } = getStaticGenerationFunctions(
process.env.ENABLE_VERSIONED_DOCS === 'true'
? {
strategy: 'remote',
basePath: basePath,
fallback: 'blocking',
revalidate: 360, // 1 hour
product: productSlug,
}
: {
strategy: 'fs',
localContentDir: CONTENT_DIR,
navDataFile: NAV_DATA_FILE,
product: productSlug,
}
)
export { getStaticPaths, getStaticProps }

View File

@ -1,45 +0,0 @@
import VerticalTextBlockList from '@hashicorp/react-vertical-text-block-list/index.tsx'
import SectionHeader from '@hashicorp/react-section-header'
import Head from 'next/head'
import HashiHead from '@hashicorp/react-head'
import s from './style.module.css'
function CommunityPage() {
return (
<main className={s.root}>
<HashiHead is={Head} title="Community | Vault by HashiCorp" />
<SectionHeader
headline="Community"
description="Vault is an open source project with a growing community. There are active, dedicated users willing to help you through various mediums."
useH1={true}
/>
<VerticalTextBlockList
product="vault"
data={[
{
header: 'Discussion List',
body:
'<a href="https://discuss.hashicorp.com/c/vault">Vault Community Forum</a>',
},
{
header: 'Bug Tracker',
body:
'<a href="https://github.com/hashicorp/vault/issues">Issue tracker on GitHub</a> for reporting bugs. Use IRC or the mailing list for general help.',
},
{
header: 'Training',
body:
'<a href="https://www.hashicorp.com/training">Paid HashiCorp</a> training courses are available in a city near you. Private training courses are also available.',
},
{
header: 'Certification',
body:
'Learn more about our <a href="https://www.hashicorp.com/certification/">Cloud Engineer Certification program</a> and <a href="https://www.hashicorp.com/certification/vault-associate/">HashiCorp&apos;s Security Automation Certification</a> exams.',
},
]}
/>
</main>
)
}
export default CommunityPage

View File

@ -1,9 +0,0 @@
.root {
composes: g-grid-container from global;
margin-top: 72px;
margin-bottom: 72px;
& :global(.g-section-header) {
margin-bottom: 100px;
}
}

View File

@ -1,41 +0,0 @@
import { productName, productSlug } from 'data/metadata'
import DocsPage from '@hashicorp/react-docs-page'
import Columns from 'components/columns'
import Tag from 'components/inline-tag'
// Imports below are used in server-side only
import { getStaticGenerationFunctions } from '@hashicorp/react-docs-page/server'
const NAV_DATA_FILE = 'data/docs-nav-data.json'
const CONTENT_DIR = 'content/docs'
const basePath = 'docs'
const additionalComponents = { Columns, Tag }
export default function DocsLayout(props) {
return (
<DocsPage
product={{ name: productName, slug: productSlug }}
baseRoute={basePath}
staticProps={props}
additionalComponents={additionalComponents}
/>
)
}
const { getStaticPaths, getStaticProps } = getStaticGenerationFunctions(
process.env.ENABLE_VERSIONED_DOCS === 'true'
? {
strategy: 'remote',
basePath: basePath,
fallback: 'blocking',
revalidate: 360, // 1 hour
product: productSlug,
}
: {
strategy: 'fs',
localContentDir: CONTENT_DIR,
navDataFile: NAV_DATA_FILE,
product: productSlug,
}
)
export { getStaticPaths, getStaticProps }

View File

@ -1,38 +0,0 @@
import { VERSION } from 'data/version'
import { productSlug } from 'data/metadata'
import ProductDownloadsPage from '@hashicorp/react-product-downloads-page'
import { generateStaticProps } from '@hashicorp/react-product-downloads-page/server'
import baseProps from 'components/downloads-props'
import s from './style.module.css'
export default function DownloadsPage(staticProps) {
return (
<>
<ProductDownloadsPage
enterpriseMode={true}
{...baseProps(
<p className={s.legalNotice}>
<em>
The following shall apply unless your organization has a
separately signed Enterprise License Agreement or Evaluation
Agreement governing your use of the package: Enterprise packages
in this repository are subject to the license terms located in the
package. Please read the license terms prior to using the package.
Your installation and use of the package constitutes your
acceptance of these terms. If you do not accept the terms, do not
use the package.
</em>
</p>
)}
{...staticProps}
/>
</>
)
}
export async function getStaticProps() {
return generateStaticProps({
product: productSlug,
latestVersion: VERSION,
})
}

View File

@ -1,16 +0,0 @@
import ProductDownloadsPage from '@hashicorp/react-product-downloads-page'
import { generateStaticProps } from '@hashicorp/react-product-downloads-page/server'
import { VERSION } from 'data/version'
import { productSlug } from 'data/metadata'
import baseProps from 'components/downloads-props'
export default function DownloadsPage(staticProps) {
return <ProductDownloadsPage {...baseProps()} {...staticProps} />
}
export function getStaticProps() {
return generateStaticProps({
product: productSlug,
latestVersion: VERSION,
})
}

View File

@ -1,45 +0,0 @@
.root {
composes: .g-grid-container from global;
margin-top: 72px;
margin-bottom: 72px;
}
.logo {
width: 105px;
}
.releaseNote {
composes: .g-type-body from global;
text-align: center;
}
.merchandisingSlot {
width: 100%;
border: 1px solid var(--gray-5);
padding: 16px;
display: flex;
justify-content: center;
& .centerWrapper {
display: flex;
align-items: center;
& p {
margin: 0;
max-width: 400px;
margin-right: 26px;
}
@media (max-width: 800px) {
flex-direction: column;
& p {
text-align: center;
margin-bottom: 12px;
}
}
}
}
.legalNotice {
margin-bottom: 50px;
}

View File

@ -1 +0,0 @@
{}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1 +0,0 @@
<svg fill="none" height="80" viewBox="0 0 80 80" width="80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="55.9123" y1="52" y2="12.7793"><stop offset="0" stop-color="#fff9cf"/><stop offset="1" stop-color="#ffec6e"/></linearGradient><path d="m0 0h80v80h-80z" fill="#fff"/><rect fill="url(#a)" height="44" opacity=".6" rx="1.5" width="44" x="8" y="8"/><path d="m26 26h44v44h-44z" fill="#000"/><rect fill="#ffec6e" height="44" rx="1.5" width="44" x="17" y="17"/><g stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path clip-rule="evenodd" d="m31.5 40.167c0-1.1046.8954-2 2-2h11c1.1046 0 2 .8954 2 2v5.1667c0 1.1045-.8954 2-2 2h-11c-1.1046 0-2-.8955-2-2z" fill-rule="evenodd"/><path d="m34.833 38.167v-3.3333c0-2.3012 1.8655-4.1667 4.1667-4.1667s4.1666 1.8655 4.1666 4.1667v3.3333"/></g></svg>

Before

Width:  |  Height:  |  Size: 915 B

View File

@ -1 +0,0 @@
<svg fill="none" height="80" viewBox="0 0 80 80" width="80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="14" x2="31.342" y1="9" y2="11.7336"><stop offset="0" stop-color="#fff9cf"/><stop offset="1" stop-color="#ffec6e"/></linearGradient><path d="m0 0h80v80h-80z" fill="#fff"/><rect fill="#000" height="54" rx="1.5" width="43" x="23" y="13"/><path d="m57 69.5c0 .8284-.6716 1.5-1.5 1.5h-33.5v-62h33.5c.8284 0 1.5.67158 1.5 1.5z" fill="#ffec6e"/><rect fill="#000" height="3" rx="1.5" width="12" x="32" y="14"/><path d="m22 9v62h-6.5c-.8284 0-1.5-.6716-1.5-1.5v-59c0-.82843.6716-1.5 1.5-1.5z" fill="url(#a)" opacity=".7"/><g stroke="#000" stroke-linecap="round" stroke-linejoin="round"><path d="m43.6663 53.5v-1.6667c0-1.8409-1.4923-3.3333-3.3333-3.3333h-6.6667c-1.8409 0-3.3333 1.4924-3.3333 3.3333v1.6667" stroke-width="2"/><path clip-rule="evenodd" d="m37.0003 45.1667c1.841 0 3.3334-1.4924 3.3334-3.3334 0-1.8409-1.4924-3.3333-3.3334-3.3333-1.8409 0-3.3333 1.4924-3.3333 3.3333 0 1.841 1.4924 3.3334 3.3333 3.3334z" fill-rule="evenodd" stroke-width="1.5"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +0,0 @@
<svg fill="none" height="80" viewBox="0 0 80 80" width="80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="11.5" x2="36.3586" y1="45" y2="71.4797"><stop offset="0" stop-color="#fff9cf"/><stop offset="1" stop-color="#ffec6e"/></linearGradient><path d="m0 0h80v80h-80z" fill="#fff"/><circle cx="40" cy="40" fill="url(#a)" fill-opacity=".65" r="32"/><path d="m40 69c16.0163 0 29-12.9837 29-29s-12.9837-29-29-29-29 12.9837-29 29" stroke="#000" stroke-width="6"/><circle cx="40" cy="40" fill="#ffec6e" r="26"/><path d="m48.0259 31.978c-1.2759-1.2756-2.9739-1.978-4.779-1.978-.2992 0-.6017.0189-.8978.0598-2.0729.2772-3.9316 1.5276-4.9711 3.3449-1.0396 1.8173-1.1719 4.0504-.3592 5.9811.0126.0284.0063.063-.0189.0882l-6.9999 6.9984v3.5276h3.5126l.9041-.9039c.0914-.0977.1323-.2142.126-.3307v-1.6599c0-.1512.1229-.2772.2772-.2772h1.5122c.1575 0 .3118-.0629.4221-.1763.1103-.1166.1701-.2614.1733-.4158v-1.5023c0-.1512.1228-.2772.2772-.2772h1.6885c.1198 0 .2363-.0504.3182-.1386l1.3263-1.326c.0157-.0126.0315-.0189.0504-.0189l.0315.0063c.8348.3496 1.7169.5292 2.621.5292 1.1751 0 2.3344-.3087 3.3551-.8914 1.8145-1.0362 3.0652-2.8945 3.3456-4.9669.2772-2.0724-.4379-4.1921-1.9154-5.6724zm-2.347 5.4299c-.4095.4126-.9576.6393-1.5404.6393-.8853 0-1.6728-.5291-2.0131-1.3448-.3371-.8158-.1543-1.7481.4726-2.3717.4127-.4126.9577-.6362 1.5405-.6362.2866 0 .567.0567.8316.1669.816.3402 1.3452 1.1276 1.3452 2.0126 0 .5669-.2331 1.1276-.6364 1.5339z" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.75"/></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Binary file not shown.

View File

@ -1,170 +0,0 @@
import * as React from 'react'
import Head from 'next/head'
import rivetQuery from '@hashicorp/nextjs-scripts/dato/client'
import homepageQuery from './query.graphql'
import { renderMetaTags } from 'react-datocms'
import IoHomeHero from 'components/io-home-hero'
import IoHomeIntro from 'components/io-home-intro'
import IoHomeInPractice from 'components/io-home-in-practice'
import IoCardContainer from 'components/io-card-container'
import IoHomeCaseStudies from 'components/io-home-case-studies'
import IoHomeCallToAction from 'components/io-home-call-to-action'
import IoHomePreFooter from 'components/io-home-pre-footer'
import s from './style.module.css'
export default function Homepage({ data }): React.ReactElement {
const {
seo,
heroHeading,
heroDescription,
heroCtas,
heroCards,
introHeading,
introDescription,
introFeatures,
introVideo,
inPracticeHeading,
inPracticeDescription,
inPracticeCards,
inPracticeCtaHeading,
inPracticeCtaDescription,
inPracticeCtaLink,
inPracticeCtaImage,
useCasesHeading,
useCasesDescription,
useCasesCards,
caseStudiesHeading,
caseStudiesDescription,
caseStudiesFeatured,
caseStudiesLinks,
callToActionHeading,
callToActionDescription,
callToActionCtas,
preFooterHeading,
preFooterDescription,
preFooterCtas,
} = data
const _introVideo = introVideo[0]
return (
<>
<Head>{renderMetaTags(seo)}</Head>
<IoHomeHero
pattern="/img/home-hero-pattern.svg"
brand="vault"
heading={heroHeading}
description={heroDescription}
ctas={heroCtas}
cards={heroCards.map((card) => {
return {
...card,
cta: card.cta[0],
}
})}
/>
<IoHomeIntro
brand="vault"
heading={introHeading}
description={introDescription}
features={introFeatures}
video={{
youtubeId: _introVideo.youtubeId,
thumbnail: _introVideo.thumbnail.url,
heading: _introVideo.heading,
description: _introVideo.description,
person: {
name: _introVideo.personName,
description: _introVideo.personDescription,
avatar: _introVideo.personAvatar?.url,
},
}}
/>
<section className={s.useCases}>
<div className={s.container}>
<IoCardContainer
heading={useCasesHeading}
description={useCasesDescription}
cardsPerRow={4}
cards={useCasesCards.map((card) => {
return {
eyebrow: card.eyebrow,
link: {
url: card.link,
type: 'inbound',
},
heading: card.heading,
description: card.description,
products: card.products,
}
})}
/>
</div>
</section>
<IoHomeInPractice
brand="vault"
pattern="/img/practice-pattern.svg"
heading={inPracticeHeading}
description={inPracticeDescription}
cards={inPracticeCards.map((card) => {
return {
eyebrow: card.eyebrow,
link: {
url: card.link,
type: 'inbound',
},
heading: card.heading,
description: card.description,
products: card.products,
}
})}
cta={{
heading: inPracticeCtaHeading,
description: inPracticeCtaDescription,
link: inPracticeCtaLink,
image: inPracticeCtaImage,
}}
/>
<IoHomeCaseStudies
heading={caseStudiesHeading}
description={caseStudiesDescription}
primary={caseStudiesFeatured}
secondary={caseStudiesLinks}
/>
<IoHomeCallToAction
brand="vault"
heading={callToActionHeading}
content={callToActionDescription}
links={callToActionCtas}
/>
<IoHomePreFooter
brand="vault"
heading={preFooterHeading}
description={preFooterDescription}
ctas={preFooterCtas}
/>
</>
)
}
export async function getStaticProps() {
const { vaultHomepage } = await rivetQuery({
query: homepageQuery,
})
return {
props: {
data: vaultHomepage,
},
revalidate:
process.env.HASHI_ENV === 'production'
? process.env.GLOBAL_REVALIDATE
: 10,
}
}

View File

@ -1,107 +0,0 @@
query homepageQuery {
vaultHomepage {
seo: _seoMetaTags {
attributes
content
tag
}
heroHeading
heroDescription
heroCtas {
title
link
}
heroCards {
heading
description
cta {
title
link
}
subText: subtext
}
introHeading
introDescription
introFeatures {
heading
description
link
image {
url
alt
}
}
introVideo {
youtubeId
heading
description
thumbnail {
url
}
personName
personDescription
personAvatar {
url
}
}
inPracticeHeading
inPracticeDescription
inPracticeCards {
eyebrow
heading
description
link
products {
name
}
}
inPracticeCtaHeading
inPracticeCtaDescription
inPracticeCtaLink
inPracticeCtaImage {
url
alt
width
height
}
useCasesHeading
useCasesDescription
useCasesCards {
eyebrow
heading
description
link
products {
name
}
}
caseStudiesHeading
caseStudiesDescription
caseStudiesFeatured {
thumbnail {
url
alt
}
heading
link
}
caseStudiesLinks {
heading
link
}
callToActionHeading
callToActionDescription
callToActionCtas {
text: title
url: link
}
preFooterHeading
preFooterDescription
preFooterCtas {
link
heading
description
cta
}
}
}

View File

@ -1,15 +0,0 @@
.container {
composes: g-grid-container from global;
}
/*
* Use cases
*/
.useCases {
margin: 60px auto;
@media (--medium-up) {
margin: 120px auto;
}
}

View File

@ -1,3 +0,0 @@
import Homepage, { getStaticProps } from './home'
export { getStaticProps }
export default Homepage

View File

@ -1,32 +0,0 @@
import Link from 'next/link'
import { useEffect } from 'react'
export default function NotFound() {
useEffect(() => {
if (
typeof window !== 'undefined' &&
typeof window?.analytics?.track === 'function' &&
typeof window?.document?.referrer === 'string' &&
typeof window?.location?.href === 'string'
)
window.analytics.track(window.location.href, {
category: '404 Response',
label: window.document.referrer || 'No Referrer',
})
}, [])
return (
<main id="p-404">
<h1 className="g-type-display-1">Page Not Found</h1>
<p>
We&lsquo;re sorry but we can&lsquo;t find the page you&lsquo;re looking
for.
</p>
<p>
<Link href="/">
<a>Back to Home</a>
</Link>
</p>
</main>
)
}

View File

@ -1,31 +0,0 @@
#p-404 {
display: flex;
flex-direction: column;
justify-content: center;
margin: 64px auto; /* this is being overridden at the request of the learn team */
max-width: 784px;
min-height: 50vh;
padding-inline: 32px;
text-align: center;
@media (--large) {
padding-inline: 24px;
}
& h1 {
font-size: 1.5rem;
letter-spacing: -0.004em;
line-height: 1.375em;
@media (--medium-up) {
font-size: 1.75rem;
line-height: 1.321em;
}
@media (--large) {
font-size: 2rem;
letter-spacing: -0.006em;
line-height: 1.313em;
}
}
}

View File

@ -1,229 +0,0 @@
/* Print Styles - Hide Elements */
@media print {
iframe,
[class*='hashi-stack-menu'],
.g-footer,
[aria-hidden='true'],
[id='sidebar'],
[id='edit-this-page'],
[id='jump-to-section'],
[id='__next-build-watcher'] {
display: none !important;
}
}
/* Print Styles - Page Spacing */
@media print {
@page {
margin: 2cm 0.5cm;
}
@page :first {
margin-top: 0;
}
@page :last {
margin-top: 0;
}
blockquote {
break-inside: avoid;
}
body {
margin-bottom: 2cm;
margin-top: 2cm;
}
dl,
ol,
ul {
break-before: avoid;
}
h1,
h2,
h3,
h4,
h5,
h6 {
break-after: avoid;
break-inside: avoid;
}
img {
break-inside: avoid;
break-after: avoid;
}
pre,
table {
break-inside: avoid;
}
pre {
white-space: pre-wrap;
}
}
@media print {
/* @todo: remove alongside @hashicorp/react-global-styles/_temporary-to-remove/layout.css */
.g-grid-container {
/*
* A measure is the number of characters in a line of text.
* Long lines fatique readers as they find the start of a new line of text.
* It seems widely accepted that an ideal measure is 66 characters per line.
* An average character takes up .5em, and so we define a measure of 33em.
* See: https://webtypography.net/2.1.2
*/
max-width: 33em;
padding-left: 0;
padding-right: 0;
word-break: break-word;
}
/* @todo: remove alongside @hashicorp/react-global-styles/_temporary-to-remove/tables.css */
table {
margin-bottom: 0;
margin-top: 20px;
}
}
/* @todo: move print styles to @hashicorp/react-global-styles/global.css */
@media print {
pre code,
code,
pre {
font-weight: inherit;
}
pre {
background: transparent;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);
color: inherit;
padding: 0.5em;
& > code {
white-space: inherit;
}
}
}
/* @todo: move print styles to @hashicorp/react-content/dist/style.css */
@media print {
.g-content {
& a {
color: inherit;
font-weight: 700;
&:not(.anchor) {
&::after {
background-color: transparent;
position: static;
opacity: 1;
}
}
&[href^='http'] {
&::after {
content: ' <' attr(href) '>';
font-size: 0.8em;
font-style: italic;
letter-spacing: -0.01875em;
vertical-align: top;
}
}
&:not([href^='http']) {
text-decoration: underline;
}
& > code {
color: inherit;
font-weight: 700;
&::before,
&::after {
content: none;
}
}
}
& h1,
& h2,
& h3,
& h4,
& h5,
& h6 {
& code {
background: none;
font-size: 1em;
padding: 0;
}
}
& h2 {
margin: 1em 0 0;
}
& h3 {
margin: 1em 0 0;
padding-bottom: 0.25em;
}
& img {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
margin: 1em 0 0;
}
& ol,
& ul {
margin: 1em 0 0;
& li {
margin-bottom: 0;
margin-top: 0.5em;
& p:first-child {
margin-top: 0;
}
}
}
& p {
margin: 1em 0 0;
}
& pre {
background-color: transparent;
border-radius: 0;
margin: 1.5em 0 0;
& code {
background: transparent;
color: inherit;
}
}
& dd,
& dt,
& li,
& p,
& td,
& th {
& > :not(pre) code,
& > code {
background: transparent;
font-weight: 700;
padding: 0;
}
}
& .alert.alert-danger,
& .alert.alert-info,
& .alert.alert-success,
& .alert.alert-warning {
background-color: transparent;
}
}
}

View File

@ -1,10 +0,0 @@
import MarkdownPage from '@hashicorp/react-markdown-page'
import generateStaticProps from '@hashicorp/react-markdown-page/server'
export default function SecurityPage(staticProps) {
return <MarkdownPage {...staticProps} />
}
export const getStaticProps = generateStaticProps({
pagePath: 'content/security.mdx',
})

View File

@ -1,60 +0,0 @@
/* Global Component Styles */
@import '~@hashicorp/mktg-global-styles/style.css';
:root {
--highlight-color: var(--brand-link);
}
/* Local Components */
@import '../components/footer/style.css';
@import '../components/use-case-cta-section/style.css';
/* Pages */
@import './not-found/style.css';
/* Print Styles */
@import './print.css';
/*
* Layout Styles
*
* Note: this is possibly debt we want to replace.
* ref: https://app.asana.com/0/1100423001970639/1200768863236365/f
*/
.g-section-block section {
padding-top: 96px;
padding-bottom: 96px;
& > .g-section-header + *,
& > .g-grid-container > .g-section-header + * {
margin-top: 72px;
}
&:not(.no-section-spacing) > * + *,
&:not(.g-featured-slider-section) > .g-grid-container > * + * {
margin-top: 96px;
}
}
.g-section-block .button-container {
display: -webkit-box;
display: flex;
flex-wrap: wrap;
-webkit-box-pack: center;
justify-content: center;
margin: auto -8px -16px;
}
.g-section-block section > * + .button-container,
.g-section-block section > .g-grid-container > * + .button-container {
margin-top: 40px;
}
.g-section-block .button-container > * {
margin: auto 8px 16px;
}
.g-section-block.theme-black-background-white-text {
background: var(--gray-1);
color: white;
}

Some files were not shown because too many files have changed in this diff Show More