diff --git a/components/OrganizedContent/ReadAll.tsx b/components/OrganizedContent/ReadAll.tsx index 6a610346..5275d6c0 100644 --- a/components/OrganizedContent/ReadAll.tsx +++ b/components/OrganizedContent/ReadAll.tsx @@ -8,6 +8,8 @@ import { OrganizedContent, } from "@/components/OrganizedContent"; +import { GetShapesConfig } from "../ShapesBackground"; + import { Header } from "./Header"; export interface SerializedSection { @@ -26,6 +28,7 @@ export interface Options { pagePath: string; title: string; image: string; + getShapesConfig?: GetShapesConfig; imagePosition?: "left" | "right"; link?: ComponentType; description?: string; @@ -35,13 +38,14 @@ export function createReadAllPage({ title, image, pagePath, + getShapesConfig, link, description, imagePosition, }: Options) { const Link = link ?? createLink(pagePath); - return function Page({ sections }: Props) { + function Page({ sections }: Props) { const readAllSection = createReadAllSection( sections.map(({ section, content }) => ({ section, @@ -71,5 +75,9 @@ export function createReadAllPage({ ); - }; + } + + Page.getShapesConfig = getShapesConfig; + + return Page; } diff --git a/components/OrganizedContent/Section.tsx b/components/OrganizedContent/Section.tsx index a384060f..ee08f75b 100644 --- a/components/OrganizedContent/Section.tsx +++ b/components/OrganizedContent/Section.tsx @@ -21,13 +21,14 @@ export function createSectionPage({ title, image, pagePath, + getShapesConfig, link, description, imagePosition, }: Options) { const Link = link ?? createLink(pagePath); - return function Page(this: void, { content, sections, current }: Props) { + function Page(this: void, { content, sections, current }: Props) { return (
); - }; + } + + Page.getShapesConfig = getShapesConfig; + + return Page; } diff --git a/components/ShapesBackground.module.css b/components/ShapesBackground.module.css new file mode 100644 index 00000000..3072ad3c --- /dev/null +++ b/components/ShapesBackground.module.css @@ -0,0 +1,20 @@ +.shapesContainer { + position: absolute; + overflow: hidden; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -10; +} + +.shape { + --blue: invert(53%) sepia(80%) saturate(4689%) hue-rotate(189deg) + brightness(92%) contrast(93%); + --teal: invert(76%) sepia(39%) saturate(578%) hue-rotate(110deg) + brightness(91%) contrast(88%); + + position: absolute; + filter: var(--blue); + opacity: 20%; +} diff --git a/components/ShapesBackground.tsx b/components/ShapesBackground.tsx new file mode 100644 index 00000000..a34a0316 --- /dev/null +++ b/components/ShapesBackground.tsx @@ -0,0 +1,308 @@ +import { useWindowDimension } from "hooks/useWindowDimension"; +import { useRouter } from "next/router"; +import React, { CSSProperties, useEffect, useRef, useState } from "react"; + +import { Image } from "./Image"; + +import styles from "./ShapesBackground.module.css"; + +const MOBILE_WIDTH = 768; + +interface Props { + getConfig?: GetShapesConfig; +} + +export function ShapesBackground({ getConfig }: Props) { + const [config, setConfig] = useState({}); + const [prevWidth, setPrevWidth] = useState(-1); + const [prevRoute, setPrevRoute] = useState(""); + const { width, height } = useWindowDimension(); + const shapesContainerRef = useRef(null); + const router = useRouter(); + + useEffect(() => { + const containerWidth = shapesContainerRef.current?.offsetWidth; + const containerHeight = shapesContainerRef.current?.offsetHeight; + + // In general, rerun getShapesConfig() if the screen size changes from desktop to mobile (or vice versa) + if ( + containerWidth == null || + containerHeight == null || + !( + router.asPath === "/" || + router.asPath !== prevRoute || + prevWidth < 0 || + (prevWidth <= MOBILE_WIDTH && MOBILE_WIDTH < width) || + (prevWidth > MOBILE_WIDTH && MOBILE_WIDTH >= width) + ) + ) { + return; + } + + setPrevWidth(width); + setPrevRoute(router.asPath); + setConfig(getConfig?.(containerWidth, containerHeight) ?? {}); + }, [getConfig, width, height, prevWidth, prevRoute, router.asPath]); + + return ( +
+ {Object.entries(config).map(([type, instances]) => + instances.map((attributes, idx) => ( + + )) + )} +
+ ); +} + +function Shape(props: { type: ShapeType; style: CSSProperties }) { + return ( + + ); +} + +export type ShapeType = + | "asterisk" + | "circle" + | "cross" + | "dots" + | "hash" + | "plus" + | "ring" + | "triangle" + | "triangleBig" + | "waves" + | "wavesBig"; + +export type ShapesConfig = { + [key in ShapeType]?: CSSProperties[]; +}; + +export type GetShapesConfig = (width: number, height: number) => ShapesConfig; + +type ShapeSize = { + [key in ShapeType]: { + size: "big" | "small"; + width: number; + height: number; + // 0 <= minAngle, maxAngle <= 180 + minAngle?: number; + maxAngle?: number; + }; +}; + +const shapeTypes: ShapeType[] = [ + "asterisk", + "circle", + "cross", + "dots", + "hash", + "plus", + "ring", + "triangle", + "triangleBig", + "waves", + "wavesBig", +]; + +const shapesSizes: ShapeSize = { + asterisk: { + size: "big", + width: 168, + height: 168, + }, + circle: { + size: "big", + width: 132, + height: 132, + }, + cross: { + size: "big", + width: 150, + height: 150, + }, + dots: { + size: "big", + width: 232, + height: 250, + }, + hash: { + size: "small", + width: 60, + height: 60, + }, + plus: { + size: "small", + width: 48, + height: 48, + }, + ring: { + size: "small", + width: 70, + height: 70, + }, + triangle: { + size: "small", + width: 68, + height: 68, + minAngle: 15, + maxAngle: 26, + }, + triangleBig: { + size: "big", + width: 138, + height: 138, + minAngle: 15, + maxAngle: 26, + }, + waves: { + size: "small", + width: 102, + height: 50, + }, + wavesBig: { + size: "big", + width: 252, + height: 132, + }, +}; + +const shapesBySize = { + big: shapeTypes.filter((shape) => shapesSizes[shape]["size"] == "big"), + small: shapeTypes.filter((shape) => shapesSizes[shape]["size"] == "small"), +}; + +// Used to generate random shapes in the margins +export const defaultGetShapesConfig = ((pageWidth, pageHeight) => { + if (window.innerWidth <= MOBILE_WIDTH) { + return mobileShapesConfig; + } + + const defaultConfig: ShapesConfig = {}; + const gap = 20; + const minBoxWidth = 150; + const boxWidth = Math.max(minBoxWidth, (pageWidth - 800 - 2 * gap) / 2); + const boxHeight = 400; + const shapeGenerationProbability = 0.85; + + for (let y = 0; y + boxHeight <= pageHeight; y += gap + boxHeight) { + for (let x = 0; x <= 1; ++x) { + if (Math.random() > shapeGenerationProbability) { + continue; + } + + const size = + boxWidth > minBoxWidth && (y == 0 || y + 2 * boxHeight > pageHeight) + ? "big" + : "small"; + const shape: ShapeType = getRandomShape(size); + const verticalOffset = getVerticalOffset(boxHeight, shape); + const horizontalOffset = getHorizontalOffset(boxWidth - 2 * gap, shape); + const shapeWidth = shapesSizes[shape]["width"]; + const shapeHeight = shapesSizes[shape]["height"]; + const angle = getRandomAngle(shape); + const colour = getRandomColour(); + const opacity = getRandomOpacity(colour); + + defaultConfig[shape] ??= []; + defaultConfig[shape]?.push({ + top: `${((y + verticalOffset) / 16).toFixed(5)}rem`, + left: + x == 0 + ? `${(((horizontalOffset + gap) / window.innerWidth) * 100).toFixed( + 5 + )}vw` + : "unset", + right: + x == 1 + ? `${(((horizontalOffset + gap) / window.innerWidth) * 100).toFixed( + 5 + )}vw` + : "unset", + width: `${(shapeWidth / 16).toFixed(5)}rem`, + height: `${(shapeHeight / 16).toFixed(5)}rem`, + transform: `rotate(${angle}deg)`, + filter: `var(--${colour})`, + opacity: `${opacity}%`, + }); + } + } + + return defaultConfig; +}) as GetShapesConfig; + +function getRandomShape(size: "big" | "small"): ShapeType { + const idx = Math.floor(Math.random() * shapesBySize[size].length); + return shapesBySize[size][idx]; +} + +function getVerticalOffset(boxHeight: number, shape: ShapeType): number { + const padding = shapesSizes[shape]["height"]; + return Math.floor(Math.random() * (boxHeight - padding)); +} + +function getHorizontalOffset(boxWidth: number, shape: ShapeType): number { + const padding = shapesSizes[shape]["width"]; + return shapesSizes[shape]["size"] == "big" + ? Math.floor(Math.random() * (boxWidth - padding / 2) - padding / 2) + : Math.floor(Math.random() * (boxWidth - padding)); +} + +function getRandomAngle(shape: ShapeType): number { + const minAngle = shapesSizes[shape]["minAngle"] ?? 0; + const maxAngle = shapesSizes[shape]["maxAngle"] ?? 0; + const direction = Math.random() < 0.5 ? 1 : -1; + return ( + (minAngle + Math.floor(Math.random() * (maxAngle - minAngle + 1))) * + direction + ); +} + +function getRandomColour(): "blue" | "teal" { + return Math.random() < 0.7 ? "blue" : "teal"; +} + +function getRandomOpacity(colour: "blue" | "teal"): number { + if (colour === "blue") { + return Math.random() < 0.8 ? 20 : 25; + } else { + return Math.random() < 0.8 ? 25 : 30; + } +} + +// Used for most mobile pages +export const mobileShapesConfig = { + dots: [ + { + top: "calc(-6rem / 16)", + left: "calc(-95rem / 16)", + width: "calc(166rem / 16)", + height: "calc(150rem / 16)", + }, + ], + hash: [ + { + top: "calc(88rem / 16)", + right: "15vw", + width: "calc(40rem / 16)", + height: "calc(40rem / 16)", + }, + ], + triangle: [ + { + top: "calc(20rem / 16)", + right: "1vw", + width: "calc(45rem / 16)", + height: "calc(45rem / 16)", + transform: "rotate(17deg)", + }, + ], +}; diff --git a/pages/_app.module.css b/pages/_app.module.css index a9bb4d65..81ee01b5 100644 --- a/pages/_app.module.css +++ b/pages/_app.module.css @@ -4,9 +4,9 @@ min-height: 100vh; } -/* This makes the footer stay at the bottom, even if there's not much content - * on the screen. - */ .contentContainer { + position: relative; + + /* This makes the footer stay at the bottom, even if there's not much content on the screen.*/ flex-grow: 1; } diff --git a/pages/_app.tsx b/pages/_app.tsx index 01452742..e705b5ec 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -11,6 +11,11 @@ import { HorizontalLine } from "@/components/HorizontalLine"; import { Link } from "@/components/Link"; import { Navbar } from "@/components/Navbar"; import { Pre } from "@/components/Pre"; +import { + defaultGetShapesConfig, + GetShapesConfig, + ShapesBackground, +} from "@/components/ShapesBackground"; import { ThemeProvider } from "@/components/Theme"; import styles from "./_app.module.css"; @@ -36,6 +41,9 @@ export default function App({ Component, pageProps }: AppProps): JSX.Element { {/* Wrapping content with a div to allow for a display: block parent */}
+ @@ -53,6 +61,7 @@ type PageComponent = NextComponentType< Record > & { Layout?: ComponentType<{ children: ReactNode }>; + getShapesConfig?: GetShapesConfig; }; type AppProps = DefaultAppProps & { diff --git a/pages/about/index.module.css b/pages/about/index.module.css index 2bcd8b39..b0346a7d 100644 --- a/pages/about/index.module.css +++ b/pages/about/index.module.css @@ -1,5 +1,5 @@ .page { - margin-bottom: calc(60rem / 16); + margin: calc(20rem / 16) 0 calc(60rem / 16); } .title { @@ -10,7 +10,6 @@ .content { color: var(--text); - background-color: var(--primary-background); } .content span { diff --git a/pages/about/index.tsx b/pages/about/index.tsx index ad641899..53e42715 100644 --- a/pages/about/index.tsx +++ b/pages/about/index.tsx @@ -4,6 +4,10 @@ import { ConnectWithUs } from "@/components/ConnectWithUs"; import { DefaultLayout } from "@/components/DefaultLayout"; import { EmailSignup } from "@/components/EmailSignup"; import { Image } from "@/components/Image"; +import { + GetShapesConfig, + mobileShapesConfig, +} from "@/components/ShapesBackground"; import Content from "../../content/about/index.mdx"; @@ -30,3 +34,83 @@ export default function AboutUs() { AboutUs.Layout = function AboutUsLayout(props: { children: React.ReactNode }) { return
{props.children}
; }; + +AboutUs.getShapesConfig = (() => { + const desktopConfig = { + cross: [ + { + top: "calc(16rem / 16)", + left: "calc(-78rem / 16)", + width: "calc(150rem / 16)", + height: "calc(150rem / 16)", + transform: "rotate(30deg)", + }, + ], + dots: [ + { + top: "calc(520rem / 16)", + right: "calc(-120rem / 16)", + width: "calc(292rem / 16)", + height: "calc(330rem / 16)", + transform: "rotate(-29deg)", + }, + ], + hash: [ + { + top: "calc(528rem / 16)", + left: "60vw", + width: "calc(60rem / 16)", + height: "calc(60rem / 16)", + }, + { + bottom: "calc(440rem / 16)", + right: "84vw", + width: "calc(60rem / 16)", + height: "calc(60rem / 16)", + }, + ], + triangle: [ + { + top: "calc(554rem / 16)", + right: "80vw", + width: "calc(68rem / 16)", + height: "calc(68rem / 16)", + transform: "rotate(-26deg)", + }, + { + top: "calc(2190rem / 16)", + right: "4vw", + width: "calc(68rem / 16)", + height: "calc(68rem / 16)", + transform: "rotate(-26deg)", + }, + ], + waves: [ + { + top: "calc(1300rem / 16)", + left: "2vw", + width: "calc(102rem / 16)", + height: "calc(50rem / 16)", + }, + ], + wavesBig: [ + { + top: "calc(42rem / 16)", + right: "calc(-160rem / 16)", + width: "calc(376rem / 16)", + height: "calc(132rem / 16)", + }, + { + bottom: "calc(40rem / 16)", + left: "calc(-174rem / 16)", + width: "calc(376rem / 16)", + height: "calc(132rem / 16)", + }, + ], + }; + + if (window.innerWidth <= 768) { + return mobileShapesConfig; + } + return desktopConfig; +}) as GetShapesConfig; diff --git a/pages/index.module.css b/pages/index.module.css index 3d098dc4..e62ad859 100644 --- a/pages/index.module.css +++ b/pages/index.module.css @@ -110,7 +110,7 @@ gap: 1rem; } -/* On a smaller desktop screen, make the events/new flow vertically */ +/* On a smaller desktop screen, make the events/news flow vertically */ @media only screen and (max-width: calc(1100rem / 16)) { .cards { flex-direction: column; diff --git a/pages/index.tsx b/pages/index.tsx index ab7d2f71..5db3414c 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -9,6 +9,7 @@ import { EventDescriptionCard } from "@/components/EventDescriptionCard"; import { Image } from "@/components/Image"; import { Link } from "@/components/Link"; import { NewsCard } from "@/components/NewsCard"; +import { GetShapesConfig } from "@/components/ShapesBackground"; import { SocialLinks } from "@/components/SocialLinks"; import { Event, getUpcomingEvents } from "@/lib/events"; import { News, getRecentNews } from "@/lib/news"; @@ -117,3 +118,188 @@ export const getStaticProps: GetStaticProps = async () => { }, }; }; + +Home.getShapesConfig = (() => { + const bigDesktopConfig = { + dots: [ + { + top: "calc(0.06 * (580rem / 0.65) / 16)", + right: "90vw", + width: "calc(168rem / 16)", + height: "calc(204rem / 16)", + filter: "var(--teal)", + opacity: "25%", + }, + ], + hash: [ + { + top: "calc(0.32 * (580rem / 0.65) / 16)", + left: "12vw", + width: "calc(60rem / 16)", + height: "calc(60rem / 16)", + }, + { + top: "calc(0.25 * (580rem / 0.65) / 16)", + right: "9vw", + width: "calc(60rem / 16)", + height: "calc(60rem / 16)", + }, + ], + plus: [ + { + top: "calc(0.42 * (580rem / 0.65) / 16)", + right: "22vw", + width: "calc(48rem / 16)", + height: "calc(48rem / 16)", + }, + ], + triangle: [ + { + top: "calc(0.05 * (580rem / 0.65) / 16)", + left: "20vw", + width: "calc(68rem / 16)", + height: "calc(68rem / 16)", + transform: "rotate(18deg)", + filter: "var(--teal)", + opacity: "30%", + }, + { + top: "calc(0.02 * (580rem / 0.65) / 16)", + right: "40vw", + width: "calc(68rem / 16)", + height: "calc(68rem / 16)", + transform: "rotate(-26deg)", + }, + ], + waves: [ + { + top: "calc(0.5 * (580rem / 0.65) / 16)", + left: "24vw", + width: "calc(116rem / 16)", + height: "calc(58rem / 16)", + filter: "var(--teal)", + }, + { + top: "calc(0.1 * (580rem / 0.65) / 16)", + right: "18vw", + width: "calc(102rem / 16)", + height: "calc(50rem / 16)", + filter: "var(--teal)", + }, + ], + }; + + const mediumDesktopConfig = { + dots: [{ ...bigDesktopConfig["dots"][0], top: "6vh" }], + hash: [ + { ...bigDesktopConfig["hash"][0], top: "32vh" }, + { ...bigDesktopConfig["hash"][1], top: "25vh" }, + ], + plus: [{ ...bigDesktopConfig["plus"][0], top: "42vh" }], + triangle: [ + { ...bigDesktopConfig["triangle"][0], top: "5vh" }, + { ...bigDesktopConfig["triangle"][1], top: "2vh" }, + ], + waves: [ + { ...bigDesktopConfig["waves"][0], top: "50vh" }, + { ...bigDesktopConfig["waves"][1], top: "10vh" }, + ], + }; + + const smallDesktopConfig = { + dots: [ + { + ...bigDesktopConfig["dots"][0], + top: "calc(0.06 * (420rem / 0.65) / 16)", + }, + ], + hash: [ + { + ...bigDesktopConfig["hash"][0], + top: "calc(0.32 * (420rem / 0.65) / 16)", + }, + { + ...bigDesktopConfig["hash"][1], + top: "calc(0.25 * (420rem / 0.65) / 16)", + }, + ], + plus: [ + { + ...bigDesktopConfig["plus"][0], + top: "calc(0.42 * (420rem / 0.65) / 16)", + }, + ], + triangle: [ + { + ...bigDesktopConfig["triangle"][0], + top: "calc(0.05 * (420rem / 0.65) / 16)", + }, + { + ...bigDesktopConfig["triangle"][1], + top: "calc(0.02 * (420rem / 0.65) / 16)", + }, + ], + waves: [ + { + ...bigDesktopConfig["waves"][0], + top: "calc(0.5 * (420rem / 0.65) / 16)", + }, + { + ...bigDesktopConfig["waves"][1], + top: "calc(0.1 * (420rem / 0.65) / 16)", + }, + ], + }; + + const mobileConfig = { + dots: [ + { + top: "0", + right: "80vw", + width: "calc(168rem / 16)", + height: "calc(152rem / 16)", + }, + ], + hash: [ + { + top: "calc(190rem / 16)", + right: "87vw", + width: "calc(60rem / 16)", + height: "calc(60rem / 16)", + }, + ], + triangle: [ + { + top: "calc(266rem / 16)", + right: "78vw", + width: "calc(45rem / 16)", + height: "calc(45rem / 16)", + transform: "rotate(-26deg)", + }, + { + top: "calc(4rem / 16)", + right: "3vw", + width: "calc(45rem / 16)", + height: "calc(45rem / 16)", + transform: "rotate(16deg)", + }, + ], + waves: [ + { + top: "calc(168rem / 16)", + left: "86vw", + width: "calc(102rem / 16)", + height: "calc(50rem / 16)", + }, + ], + }; + + if (window.innerWidth <= 768) { + return mobileConfig; + } else if (window.innerHeight <= 420 / 0.65) { + return smallDesktopConfig; + } else if (window.innerHeight <= 580 / 0.65) { + return mediumDesktopConfig; + } + return bigDesktopConfig; +}) as GetShapesConfig; diff --git a/pages/news/[year]/[term].tsx b/pages/news/[year]/[term].tsx index f6e33f36..203165d1 100644 --- a/pages/news/[year]/[term].tsx +++ b/pages/news/[year]/[term].tsx @@ -5,6 +5,11 @@ import { MDXRemote } from "next-mdx-remote"; import React from "react"; import { NewsCard } from "@/components/NewsCard"; +import { + ShapesConfig, + defaultGetShapesConfig, + GetShapesConfig, +} from "@/components/ShapesBackground"; import { getNewsBySlug, getNewsByTerm, @@ -44,6 +49,12 @@ export default function TermNews({ year, term, news }: Props) { ); } +TermNews.getShapesConfig = ((width, height) => { + return window.innerWidth <= 768 + ? ({} as ShapesConfig) + : defaultGetShapesConfig(width, height); +}) as GetShapesConfig; + export const getStaticProps: GetStaticProps = async ( context ) => { diff --git a/pages/news/archive.tsx b/pages/news/archive.tsx index 0ae61722..9857618a 100644 --- a/pages/news/archive.tsx +++ b/pages/news/archive.tsx @@ -3,6 +3,11 @@ import { GetStaticProps } from "next"; import React from "react"; import { Link } from "@/components/Link"; +import { + ShapesConfig, + GetShapesConfig, + defaultGetShapesConfig, +} from "@/components/ShapesBackground"; import styles from "./archive.module.css"; @@ -32,6 +37,12 @@ export default function NewsArchive({ items }: Props) { ); } +NewsArchive.getShapesConfig = ((width, height) => { + return window.innerWidth <= 768 + ? ({} as ShapesConfig) + : defaultGetShapesConfig(width, height); +}) as GetShapesConfig; + export const getStaticProps: GetStaticProps = async () => { const years = (await getNewsYears()).reverse(); const yearsWithTerms = await Promise.all( diff --git a/public/images/shapes/asterisk.svg b/public/images/shapes/asterisk.svg new file mode 100644 index 00000000..ac7878e7 --- /dev/null +++ b/public/images/shapes/asterisk.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/shapes/circle.svg b/public/images/shapes/circle.svg new file mode 100644 index 00000000..f8ca4eaa --- /dev/null +++ b/public/images/shapes/circle.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/shapes/cross.svg b/public/images/shapes/cross.svg new file mode 100644 index 00000000..6e4955ae --- /dev/null +++ b/public/images/shapes/cross.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/shapes/dots.svg b/public/images/shapes/dots.svg new file mode 100644 index 00000000..dc3aad72 --- /dev/null +++ b/public/images/shapes/dots.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/shapes/hash.svg b/public/images/shapes/hash.svg new file mode 100644 index 00000000..ea67f08c --- /dev/null +++ b/public/images/shapes/hash.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/shapes/plus.svg b/public/images/shapes/plus.svg new file mode 100644 index 00000000..ff684ed4 --- /dev/null +++ b/public/images/shapes/plus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/shapes/ring.svg b/public/images/shapes/ring.svg new file mode 100644 index 00000000..a1bc7f05 --- /dev/null +++ b/public/images/shapes/ring.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/shapes/triangle.svg b/public/images/shapes/triangle.svg new file mode 100644 index 00000000..93a054aa --- /dev/null +++ b/public/images/shapes/triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/shapes/triangleBig.svg b/public/images/shapes/triangleBig.svg new file mode 100644 index 00000000..e7eb21f8 --- /dev/null +++ b/public/images/shapes/triangleBig.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/shapes/waves.svg b/public/images/shapes/waves.svg new file mode 100644 index 00000000..558b4a4f --- /dev/null +++ b/public/images/shapes/waves.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/shapes/wavesBig.svg b/public/images/shapes/wavesBig.svg new file mode 100644 index 00000000..a71a5d3e --- /dev/null +++ b/public/images/shapes/wavesBig.svg @@ -0,0 +1,4 @@ + + + +