Skip to content

Commit af6fb8b

Browse files
committed
abstraction, pages, info
1 parent 6864b62 commit af6fb8b

17 files changed

Lines changed: 608 additions & 177 deletions

src/components/InfoCard.css

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
.info-card-screen {
2+
min-height: 100%;
3+
height: 100%;
4+
display: flex;
5+
align-items: center;
6+
justify-content: center;
7+
padding: clamp(1rem, 3vw, 2rem);
8+
background: transparent;
9+
overflow: hidden;
10+
}
11+
12+
.info-card-shell {
13+
--info-card-max-height: calc((100vh - var(--screen-closed-height)) * 0.75);
14+
--info-card-deal-tilt: 0deg;
15+
--info-card-rest-tilt-scale: 0.56;
16+
--info-card-entry-tilt-scale: 1.85;
17+
--info-card-mid-tilt-scale: 0.45;
18+
width: min(calc(var(--info-card-max-height) * 1.3333333), calc(100vw - 2.5rem));
19+
height: min(var(--info-card-max-height), calc((100vw - 2.5rem) * 0.75));
20+
aspect-ratio: 4 / 3;
21+
border: var(--frame-strong);
22+
background: var(--surface);
23+
box-shadow: var(--underlay-shadow);
24+
border-radius: 0.5rem;
25+
padding: var(--info-card-padding, clamp(1.3rem, 3.2vw, 2.25rem));
26+
display: flex;
27+
flex-direction: column;
28+
align-items: center;
29+
justify-content: center;
30+
gap: var(--info-card-gap, clamp(0.375rem, 0.825vw, 0.5rem));
31+
text-align: center;
32+
transform-origin: center 115%;
33+
animation: info-card-deal-in 820ms cubic-bezier(0.2, 0.82, 0.22, 1) both;
34+
will-change: transform;
35+
}
36+
37+
@media (min-width: 901px) {
38+
.info-card-shell {
39+
--info-card-rest-tilt-scale: 1;
40+
--info-card-entry-tilt-scale: 3.4;
41+
--info-card-mid-tilt-scale: 0.86;
42+
}
43+
}
44+
45+
@keyframes info-card-deal-in {
46+
0% {
47+
transform: translate3d(0, 120vh, 0) rotate(calc(var(--info-card-deal-tilt) * var(--info-card-entry-tilt-scale)));
48+
}
49+
72% {
50+
transform: translate3d(0, -0.8rem, 0) rotate(calc(var(--info-card-deal-tilt) * var(--info-card-mid-tilt-scale)));
51+
}
52+
100% {
53+
transform: translate3d(0, 0, 0) rotate(calc(var(--info-card-deal-tilt) * var(--info-card-rest-tilt-scale)));
54+
}
55+
}
56+
57+
@media (prefers-reduced-motion: reduce) {
58+
.info-card-shell {
59+
animation: none;
60+
transform: rotate(calc(var(--info-card-deal-tilt) * var(--info-card-rest-tilt-scale)));
61+
will-change: auto;
62+
}
63+
}

src/components/InfoCard.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React, { useMemo } from "react";
2+
import "./InfoCard.css";
3+
4+
const DEAL_TILT_MIN_DEG = 1.2;
5+
const DEAL_TILT_MAX_DEG = 3.4;
6+
const DEAL_TILT_MIN_DELTA_DEG = 0.45;
7+
const RECENT_TILT_WINDOW = 5;
8+
const recentTiltMagnitudes = [];
9+
10+
function sampleTiltMagnitude() {
11+
return DEAL_TILT_MIN_DEG + Math.random() * (DEAL_TILT_MAX_DEG - DEAL_TILT_MIN_DEG);
12+
}
13+
14+
function generateCardTilt(dealIndex) {
15+
const direction = Number.isFinite(dealIndex)
16+
? dealIndex % 2 === 0
17+
? -1
18+
: 1
19+
: Math.random() < 0.5
20+
? -1
21+
: 1;
22+
let magnitude = sampleTiltMagnitude();
23+
let attempt = 0;
24+
25+
while (attempt < 12) {
26+
const isTooCloseToRecent = recentTiltMagnitudes.some(
27+
(recentMagnitude) => Math.abs(recentMagnitude - magnitude) < DEAL_TILT_MIN_DELTA_DEG
28+
);
29+
if (!isTooCloseToRecent) {
30+
break;
31+
}
32+
magnitude = sampleTiltMagnitude();
33+
attempt += 1;
34+
}
35+
36+
recentTiltMagnitudes.push(magnitude);
37+
if (recentTiltMagnitudes.length > RECENT_TILT_WINDOW) {
38+
recentTiltMagnitudes.shift();
39+
}
40+
41+
const tilt = magnitude * direction;
42+
return tilt.toFixed(2);
43+
}
44+
45+
function InfoCard({
46+
screenClassName = "",
47+
cardClassName = "",
48+
role = "region",
49+
ariaLabel,
50+
dealIndex = null,
51+
children,
52+
}) {
53+
const screenClassNames = ["info-card-screen", screenClassName]
54+
.filter(Boolean)
55+
.join(" ");
56+
const cardClassNames = ["info-card-shell", cardClassName]
57+
.filter(Boolean)
58+
.join(" ");
59+
const cardTilt = useMemo(() => generateCardTilt(dealIndex), [dealIndex]);
60+
const cardStyle = {
61+
"--info-card-deal-tilt": `${cardTilt}deg`,
62+
};
63+
64+
return (
65+
<main className={screenClassNames} aria-live="polite">
66+
<section className={cardClassNames} role={role} aria-label={ariaLabel} style={cardStyle}>
67+
{children}
68+
</section>
69+
</main>
70+
);
71+
}
72+
73+
export default InfoCard;

0 commit comments

Comments
 (0)