Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Homepage] <IoHomeVideo /> component #13161

Merged
merged 12 commits into from
Nov 17, 2021
47 changes: 47 additions & 0 deletions website/components/io-home-dialog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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 IoHomeDialogProps extends DialogOverlayProps {
label: string
}

export default function IoHomeDialog({
isOpen,
onDismiss,
children,
label,
}: IoHomeDialogProps) {
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>
)
}
61 changes: 61 additions & 0 deletions website/components/io-home-dialog/style.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
.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;

@media (min-width: 768px) {
right: 48px;
top: 48px;
}

@nest html[dir='rtl'] & {
left: 24px;
right: auto;

@media (min-width: 768px) {
left: 48px;
right: auto;
}
}
}
55 changes: 55 additions & 0 deletions website/components/io-home-video-callout/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as React from 'react'
import Image from 'next/image'
import VisuallyHidden from '@reach/visually-hidden'
import IoHomeDialog from 'components/io-home-dialog'
import PlayIcon from './play-icon'
import s from './style.module.css'

export default function IoHomeVideoCallout({
thumbnail,
heading,
description,
person,
}) {
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" />
</button>
<figcaption className={s.content}>
<h3 className={s.heading}>{heading}</h3>
<p className={s.description}>{description}</p>
{person && (
<div className={s.person}>
<div className={s.personThumbnail}>
<Image
src={person.thumbnail}
width={52}
height={52}
alt={`${person.name} avatar`}
/>
</div>
<div>
<p className={s.personName}>{person.name}</p>
<p className={s.personDescription}>{person.description}</p>
</div>
</div>
)}
</figcaption>
</figure>
<IoHomeDialog
isOpen={showDialog}
onDismiss={hideVideo}
label={`${heading} video}`}
>
<div className={s.video}></div>
</IoHomeDialog>
</>
)
}
21 changes: 21 additions & 0 deletions website/components/io-home-video-callout/play-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default function PlayIcon() {
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>
)
}
118 changes: 118 additions & 0 deletions website/components/io-home-video-callout/style.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
.videoCallout {
--columns: 1;

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;

@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);
}

.video {
background-color: var(--gray-2);
aspect-ratio: 16 / 9;
}
Loading