www-new/docs/pages.md

6.0 KiB

Pages

All pages are a separate React component in our repository, under the pages folder. This is a special directory used by Next.js which maps a React component exported from this directory to a page on a url.

The React components exported by these files are wrapped by the App component. This lets us reuse code in between pages which makes it a good place to render the navbar, footer, background shapes, and the general CSS layout of a page.

Title

We use a custom Title component to set the title on our pages. This is a very simple component and is just a wrapper around the Next.js Head component. It also automatically prefixes each title with "CSC - University of Waterloo". Look at the code for more details.

Example 1

import { Title } from "@/components/Title"

function FooPage() {
  return (
    <>
      <Title>Title in the tab</Title>
      <div>Content of the page</div>
    </>
  )
}

// The page's content will be "Content of the page"
// The title of the page (as shown at the top of the browser in the tab) is "CSC - University of Waterloo - Title in the tab"

Example 2

You can also pass in an array of strings, and the Title component will automatically join them with " - ".

import { Title } from "@/components/Title"

function FooPage() {
  return (
    <>
      <Title>{["Foo", "Bar", "Baz"]}</Title>
      <div>Content of the page</div>
    </>
  )
}

// The page's content will be "Content of the page"
// The title of the page (as shown at the top of the browser in the tab) is "CSC - University of Waterloo - Foo - Bar - Baz"

Layout

Most pages are wrapped with the DefaultLayout component which limits the page width and adds the necessary margins and paddings. However, some pages need to override these default styles to accomodate for their specific design. For example:

  • The home page is wider than all the other pages.
  • The about us needs the entire screen width to properly render the bubbles.
bubble on about page
The bubble component on the About us page

We have an opt-in model for using a custom layout for pages. This is enabled by the static Layout function on a React component for a page.

Example

function PageXYZ() {
  return <p>I am a page</p>
}

// If we want this page to use a custom layout, we can add a static `Layout` function to it.
PageXYZ.Layout = function PageXYZLayout(props: { children: React.ReactNode }) {
  return <div className={styles.customLayoutStyles}>{props.children}</div>;
}

It is extremely important to return props.children as part of the JSX because this contains the entire page. If you fail to return it, the page will not show up.

Note that this functionality is not a part of Next.js. We take advantage of static properties in the _app.tsx file to implement this.

Shapes Background

Just like the static Layout function, we use another static function on React pages called getShapesConfig. Most pages use the default config which positions shapes randomly on the page. Some pages require a little artistic nudge or even hardcoding the shapes to appear in certain locations.

Pages like the home page can use the getShapesConfig function to customize what shapes they want the page to render.

Example 1

import { GetShapesConfig } from "@/components/ShapesBackground";

PageXYZ.getShapesConfig = (() => {
  // I ONLY LIKE DOTS AND WAVES!!!!
  return {
    dots: [
      {
        // These map to CSS properties. You don't have to use all of them. Use the ones that you want.
        top: "calc(0.06 * (580rem / 0.65) / 16)",
        right: "90vw",
        width: "calc(168rem / 16)",
        height: "calc(204rem / 16)",
        filter: "var(--teal)",
        opacity: "25%",
      }
    ],
    waves: [
      {
        top: "calc(0.5 * (580rem / 0.65) / 16)",
        left: "24vw",
        width: "calc(116rem / 16)",
        height: "calc(58rem / 16)",
        filter: "var(--teal)",
      },
    ]
  }
}) as GetShapesConfig

Note that background shapes are not rendered into html files during build time because it is impossible to know the window dimensions. This means that you can safely use window.innerWidth and window.innerHeight as well use the width and height of the shapes container inside the getShapesConfig function to change the shapes based on size of the screen.

Example 2

import { GetShapesConfig } from "@/components/ShapesBackground";

PageXYZ.getShapesConfig = ((containerWidth, containerHeight) => {
  // I like dots on desktops
  if (window.innerWidth >= 768) {
    return {
      dots: [
        {
          // These map to CSS properties. You don't have to use all of them. Use the ones that you want.
          top: "calc(0.06 * (580rem / 0.65) / 16)",
          right: "90vw",
          width: "calc(168rem / 16)",
          height: "calc(204rem / 16)",
          filter: "var(--teal)",
          opacity: "25%",
        }
      ],
    }
  }
  // but waves on phones and tables
  else {
    return {
      waves: [
        {
          top: "calc(0.5 * (580rem / 0.65) / 16)",
          left: "24vw",
          width: "calc(116rem / 16)",
          height: "calc(58rem / 16)",
          filter: "var(--teal)",
        },
      ]
    }
  }
}) as GetShapesConfig

As with the Layout function, this is not a part of Next.js. We take advantage of static properties in the _app.tsx file to implement this.