diff --git a/components/QuotationCarousel.module.css b/components/QuotationCarousel.module.css
new file mode 100644
index 0000000..40241da
--- /dev/null
+++ b/components/QuotationCarousel.module.css
@@ -0,0 +1,124 @@
+.carousel {
+ position: relative;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: calc(8rem / 16);
+}
+
+.circle {
+ position: absolute;
+ top: 30%;
+ right: 52%;
+ z-index: -1;
+
+ background-color: var(--tertiary-background);
+ clip-path: circle();
+}
+
+.right.circle {
+ top: unset;
+ right: unset;
+ bottom: 30%;
+ left: 52%;
+}
+
+.carouselButton {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ padding: calc(16rem / 16);
+ height: min-content;
+
+ background: none;
+ border: none;
+
+ cursor: pointer;
+}
+
+.arrow {
+ position: relative;
+ width: calc(20rem / 16);
+ height: calc(40rem / 16);
+
+ transition: 0.2s;
+}
+
+.previous.arrow {
+ transform: rotate(180deg);
+}
+
+.carouselButton:hover > .arrow {
+ translate: calc(4rem / 16);
+}
+
+.carouselButton:hover > .previous.arrow {
+ translate: calc(-4rem / 16);
+}
+
+.card {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: stretch;
+ gap: calc(16rem / 16);
+
+ min-height: inherit;
+ height: 100%;
+ width: 100%;
+ padding: calc(30rem / 16);
+
+ background-color: var(--translucent-accent);
+ border: calc(2rem / 16) solid var(--primary-text);
+ border-radius: calc(12rem / 16);
+ box-shadow: 0 calc(1rem / 16) calc(10rem / 16) var(--primary-accent);
+}
+
+.card ul {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ position: relative;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ flex-grow: 1;
+}
+
+.card li {
+ position: absolute;
+ left: 0;
+ right: 0;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+
+ visibility: visible;
+ opacity: 1;
+ transition: 0.1s;
+}
+
+.card li.hidden {
+ visibility: hidden;
+ opacity: 0;
+}
+
+.card p {
+ margin: 0 calc(16rem / 16);
+ font-weight: bold;
+ text-align: center;
+}
+
+.quotationMark {
+ width: calc(20rem / 16);
+ height: calc(20rem / 16);
+}
+
+.right.quotationMark {
+ transform: rotate(180deg);
+ align-self: end;
+}
diff --git a/components/QuotationCarousel.tsx b/components/QuotationCarousel.tsx
new file mode 100644
index 0000000..fed81a7
--- /dev/null
+++ b/components/QuotationCarousel.tsx
@@ -0,0 +1,130 @@
+import React, { useState } from "react";
+import { Color } from "utils/Color";
+
+import styles from "./QuotationCarousel.module.css";
+
+interface QuotationCarouselProps {
+ data: string[];
+ /** Width of the entire carousel including the buttons, in px. */
+ width?: number;
+ /** Minimum height of the carousel, in px. */
+ height?: number;
+ /** Diameter of the background circles, in px. Set to 0 for no circles. */
+ circleDiameter?: number;
+ className?: string;
+}
+
+interface CarouselButtonProps {
+ onClick: () => void;
+ isPrevious?: boolean;
+}
+
+export function QuotationCarousel(props: QuotationCarouselProps) {
+ const {
+ data,
+ width = 600,
+ height = 100,
+ circleDiameter = 120,
+ className,
+ } = props;
+
+ const [activeIdx, setActiveIdx] = useState(0);
+
+ function showNextCard() {
+ setActiveIdx((activeIdx + 1) % data.length);
+ }
+
+ function showPreviousCard() {
+ setActiveIdx((activeIdx - 1 + data.length) % data.length);
+ }
+
+ return (
+
+
+
+
+
+
+
+ {data.map((quote, idx) => (
+ -
+
{quote}
+
+ ))}
+
+
+
+
+
+ );
+}
+
+function Circle({
+ className,
+ diameter,
+}: {
+ className: string;
+ diameter: number;
+}) {
+ return (
+
+ );
+}
+
+function CarouselButton({ isPrevious, onClick }: CarouselButtonProps) {
+ return (
+
+ );
+}
+
+function QuotationMark({ className }: { className: string }) {
+ return (
+
+ );
+}
diff --git a/data/mocks.ts b/data/mocks.ts
index 162f5aa..3631cd6 100644
--- a/data/mocks.ts
+++ b/data/mocks.ts
@@ -64,3 +64,15 @@ export const moreMockCategoricalData = [
{ key: "Ada", value: 2.21 },
{ key: "Dart", value: 2.21 },
];
+
+export const mockQuoteData = [
+ "The quick brown fox jumps over the lazy dog.",
+ "Sphinx of black quartz, judge my vow!",
+ "Pack my box with five dozen liquor jugs.",
+];
+
+export const mockQuoteDataLong = [
+ "Here, have some quotes of varying lengths, and see how they look.",
+ "Hello, world!",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla in enim neque. Sed sit amet convallis tellus. Integer condimentum a felis id gravida. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam metus libero, sagittis in consectetur in, scelerisque sed sapien. Nullam ut feugiat sapien. Praesent dictum ac ipsum ac lacinia.",
+];
diff --git a/pages/_app.css b/pages/_app.css
index 30aea2c..8c5d52f 100644
--- a/pages/_app.css
+++ b/pages/_app.css
@@ -20,6 +20,7 @@ body {
--light--primary-accent-light: var(--orange);
--light--primary-accent-lighter: #FFBC9F;
--light--secondary-accent: #E55F98;
+ --light--translucent-accent: rgba(255, 231, 231, 0.75);
--light--secondary-accent-light: #FEA0C8;
--light--primary-heading: #D02B53;
--light--primary-text: #483B35;
@@ -37,6 +38,7 @@ body {
--dark--primary-accent-lighter: var(--lighter-pink);
--dark--secondary-accent: var(--orange);
--dark--secondary-accent-light: var(--light-orange);
+ --dark--translucent-accent: rgba(239, 131, 157, 0.75);
--dark--primary-heading: #FFC48D;
--dark--secondary-heading: var(--pink);
--dark--link: var(--pink);
@@ -54,6 +56,7 @@ body {
--primary-accent-lighter: var(--dark--primary-accent-lighter);
--secondary-accent: var(--dark--secondary-accent);
--secondary-accent-light: var(--dark--secondary-accent-light);
+ --translucent-accent: var(--dark--translucent-accent);
--primary-heading: var(--dark--primary-heading);
--secondary-heading: var(--dark--secondary-heading);
--link: var(--dark--link);
@@ -117,4 +120,4 @@ p {
--card-background: var(--dark--card-background);
--label: var(--dark--label);
}
-}
\ No newline at end of file
+}
diff --git a/pages/playground.module.css b/pages/playground.module.css
index a7aefd8..97f5eac 100644
--- a/pages/playground.module.css
+++ b/pages/playground.module.css
@@ -5,3 +5,11 @@
.barGraphDemo {
border: calc(1rem / 16) solid black;
}
+
+.quotationCarouselDemo {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: calc(48rem / 16);
+ margin: calc(32rem / 16);
+}
diff --git a/pages/playground.tsx b/pages/playground.tsx
index 6630d0c..c7ed840 100644
--- a/pages/playground.tsx
+++ b/pages/playground.tsx
@@ -1,10 +1,17 @@
import { BarGraphHorizontal, BarGraphVertical } from "components/BarGraph";
-import { mockCategoricalData, moreMockCategoricalData } from "data/mocks";
+import {
+ mockCategoricalData,
+ moreMockCategoricalData,
+ mockQuoteData,
+ mockQuoteDataLong,
+} from "data/mocks";
import { sectionsData } from "data/routes";
import React from "react";
import Sections from "@/components/Sections";
+import { QuotationCarousel } from "@/components/QuotationCarousel";
+
import { ColorPalette } from "../components/ColorPalette";
import { WordCloud } from "../components/WordCloud";
@@ -62,6 +69,19 @@ export default function Home() {
value: word.value,
}))}
/>
+
+
+ {""}
+
+
+
+
+
{""}