diff --git a/docs/sftt/frontend-guides/sftt-responsive-styling.md b/docs/sftt/frontend-guides/sftt-responsive-styling.md
new file mode 100644
index 0000000..2fe6962
--- /dev/null
+++ b/docs/sftt/frontend-guides/sftt-responsive-styling.md
@@ -0,0 +1,569 @@
+# Responsive Styling in React
+
+## The Basics
+How do we change our site to look good on all devices and screen sizes? There are three main
+techniques:
+
+1. Use flex and relative dimensions
+2. Rendering different content for different screen sizes
+3. Changing the style of content based on the screen size
+
+For techniques 2-3, we need to know the size of the user's screen. In our application,
+we use the screen's *width* to determine if the user is on a mobile device, tablet, narrow desktop,
+or desktop. These options are represented by the `WindowTypes` enum:
+```ts
+export enum WindowTypes {
+ Mobile = 'MOBILE',
+ Tablet = 'TABLET',
+ NarrowDesktop = 'NARROW',
+ Desktop = 'DESKTOP',
+}
+```
+
+and by our constant breakpoints:
+```ts
+export const BREAKPOINT_DESKTOP = 1300;
+export const BREAKPOINT_TABLET = 1025;
+export const BREAKPOINT_MOBILE = 680;
+```
+where window types are defined as follows:
+```ts
+const windowType: WindowTypes =
+ width < BREAKPOINT_MOBILE
+ ? WindowTypes.Mobile
+ : width < BREAKPOINT_TABLET
+ ? WindowTypes.Tablet
+ : width < BREAKPOINT_DESKTOP
+ ? WindowTypes.NarrowDesktop
+ : WindowTypes.Desktop;
+```
+
+## 1. Use flex and relative dimensions
+
+### Flex
+The CSS `display: flex` property will be your best friend. A flex container alters its content's
+height, width, and/or position to fit the available space. I could explain it, but
+[this guide](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) is pretty much all you'll
+need and something I 100% recommend always having on hand.
+
+I recommend using flex over other layout solutions like AntD's
+[grid](https://ant.design/components/grid/) because it's more flexible and a lot simpler in the
+code, but be aware other solutions exist if flex isn't working for what you're working on.
+
+### Relative Dimensions
+Use standard dimensions like `px` whenever it looks reasonable to keep things simple, but relative
+dimensions can help you scale content to look appropriate for the screen. In particular, be aware
+that you can use:
+
+| Unit | Description |
+|--------|------------------------------------------------------------------------------|
+| `vw` | 1`vw` = 1% of the browser window's width |
+| `vh` | 1`vh` = 1% of the browser window's height |
+| `vmin` | 1`vmin` = 1% of the browser window's smaller dimension (of width and height) |
+| `vmax` | 1`vmax` = 1% of the browser window's larger dimension (of width and height) |
+| `%` | Percentage of the parent element's respective dimension |
+
+## 2. Rendering different content for different screen sizes: `useWindowDimensions`
+
+To demonstrate how to render different content for different window types, let's build a component
+that renders what window type the user is on. We'll show a `h1` on desktop, `h2` on narrow desktop,
+`h3` on tablet, and `h4` on mobile.
+
+First, we can get the user's window type with the `useWindowDimensions`
+[hook](https://reactjs.org/docs/hooks-overview.html), located under `src/components/windowDimensions`.
+To use the hook, simply add the following line to your component:
+
+```ts
+const { windowType } = useWindowDimensions();
+```
+
+!!! note
+
+ The `useWindowDimensions` hook also gives us access to the window's exact width and height, but
+ in most cases the window type will be sufficient.
+ ```ts
+ const { width, height, windowType } = useWindowDimensions();
+ ```
+
+Second, we can change what to render in our React component with a switch statement:
+```html
+{(() => {
+ switch (windowType) {
+ case WindowTypes.Mobile:
+ return
You're on mobile!
+ case WindowTypes.Tablet:
+ return You're on tablet!
+ case WindowTypes.NarrowDesktop:
+ return You're on narrow desktop!
+ case WindowTypes.Desktop:
+ return You're on desktop!
+ }
+})()}
+```
+
+so our whole component would look something like:
+```html
+const WebPage: React.FC = () => {
+ // get the window type
+ const { windowType } = useWindowDimensions();
+
+ return (
+ <>
+ {/* content that renders on all screens */}
+ Welcome to the site that tells you what window type you're on!
+
+ {/* content that renders based on window type */}
+ {(() => {
+ switch (windowType) {
+ case WindowTypes.Mobile:
+ return You're on mobile!
+ case WindowTypes.Tablet:
+ return You're on tablet!
+ case WindowTypes.NarrowDesktop:
+ return You're on narrow desktop!
+ case WindowTypes.Desktop:
+ return You're on desktop!
+ }
+ })()}
+ >
+ );
+}
+```
+
+!!! note
+
+ Switch statements are just one way to *conditionally render* content in React based on `windowType`.
+ See [**here**](https://reactjs.org/docs/conditional-rendering.html) for other ways that will allow
+ you to do different things, such as only showing a component on desktop.
+
+This was a pretty basic example, but in general we'll do this sort of responsive rendering to show
+different content or use different layouts for different window types.
+
+## 3. Changing the style of content based on the screen size: CSS `@media` queries
+
+When we're just changing the *style* of components instead of what components are rendering, we
+use the CSS `@media` rule. In general, always try to do this over using `useWindowDimensions`
+(we'll explain why later).
+
+To demonstrate, let's build a page that renders small text (`10px`) on mobile and medium text
+(`16px`) on everything else.
+
+First, let's build our page for everything except mobile.
+```js
+const StyledParagraph = styled.p`
+ font-size: 16px;
+`;
+
+const WebPage: React.FC = () => {
+ return (
+ <>
+ Welcome to our amazing webpage!
+ >
+ );
+}
+```
+
+!!! note
+
+ We're using [`styled-components`](https://styled-components.com/) in this example and in our
+ project, but `@media` rules work whenever you're using CSS.
+
+Now, to have our `StyledParagraph` be `10px` on mobile, we just have to add an `@media` rule in the
+CSS of our styled component:
+```js
+const StyledParagraph = styled.p`
+ font-size: 16px;
+
+ /* on any devices with a width less than BREAKPOINT_MOBILE... */
+ @media (max-width: ${BREAKPOINT_MOBILE}px) {
+ /* ...apply the following styles */
+ font-size: 10px;
+ }
+`;
+
+const WebPage: React.FC = () => {
+ return (
+ <>
+ Welcome to our amazing webpage!
+ >
+ );
+}
+```
+
+And that's it! There's a lot more you can do with CSS media queries, which you can explore
+[**here**](https://www.w3schools.com/cssref/css3_pr_mediaquery.asp).
+
+### Sidebar: We prefer to use CSS Media Queries over `useWindowDimensions`
+
+It is possible to use our `useWindowDimensions` hook instead of CSS media queries to do what we
+just did above by using props in our styled component:
+```js
+interface StyledParagraphProps {
+ readonly isMobile: boolean;
+}
+
+const StyledParagraph = styled.p`
+ font-size: ${({ isMobile }: StyledParagraphProps) => (isMobile ? '10' : '16')}px;
+`;
+
+const WebPage: React.FC = () => {
+ // get the window type
+ const { windowType } = useWindowDimensions();
+ const isMobile = windowType === WindowTypes.Mobile;
+
+ return (
+ <>
+ Welcome to our amazing webpage!
+ >
+ );
+}
+```
+
+or
+
+```js
+interface StyledParagraphProps {
+ readonly fontSize: string;
+}
+
+const StyledParagraph = styled.p`
+ font-size: ${({ fontSize }: StyledParagraphProps) => fontSize};
+`;
+
+const WebPage: React.FC = () => {
+ // get the window type
+ const { windowType } = useWindowDimensions();
+ const fontSize = (windowType === WindowTypes.Mobile) ? '10px' : '16px';
+
+ return (
+ <>
+ Welcome to our amazing webpage!
+ >
+ );
+}
+```
+
+etc. However, we prefer to use media queries because passing props around can get pretty messy. From
+the examples above, we can see that it's a lot more readable and quicker to use media queries.
+Further, following React best practices of having
+[smart and dumb components](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0),
+we only want to use the `useWindowDimensions` hook in our smart components (in our project, our
+containers). This means that we'd have to pass down a `windowType` or `isMobile` or similar prop to
+every component that we want to have it, which is pretty ridiculous if a component four levels down
+is the only one that needs it. Just to drive home the point:
+
+```
+// ComponentFour.tsx
+
+interface StyledParagraphProps {
+ readonly isMobile: boolean;
+}
+
+const StyledParagraph = styled.p`
+ font-size: ${({ isMobile }: StyledParagraphProps) => (isMobile ? '10' : '16')}px;
+`;
+
+const ComponentFour: React.FC = ({ isMobile }) => {
+ return (
+ The thing that needs to be styled
+ );
+}
+
+// ComponentThree.tsx
+
+interface ComponentThreeProps {
+ readonly isMobile: boolean;
+}
+
+const ComponentThree: React.FC = ({ isMobile }) => {
+ return (
+ <>
+ Component 3 content
+
+ >
+ );
+}
+
+// ComponentTwo.tsx
+
+interface ComponentTwoProps {
+ readonly isMobile: boolean;
+}
+
+const ComponentTwo: React.FC = ({ isMobile }) => {
+ return (
+ <>
+ Component 2 content
+
+ >
+ );
+}
+
+// ComponentOne.tsx
+
+interface ComponentOneProps {
+ readonly isMobile: boolean;
+}
+
+const ComponentOne: React.FC = ({ isMobile }) => {
+ return (
+ <>
+ Component 1 content
+
+ >
+ );
+}
+
+// WebPage.tsx
+
+const WebPage: React.FC = () => {
+ // get the window type
+ const { windowType } = useWindowDimensions();
+ const isMobile = windowType === WindowTypes.Mobile;
+
+ return (
+ <>
+ Welcome to our amazing webpage!
+
+ >
+ );
+}
+```
+
+versus:
+
+```
+// ComponentFour.tsx
+
+const StyledParagraph = styled.p`
+ font-size: 16px;
+
+ @media (max-width: ${BREAKPOINT_MOBILE}px)
+ font-size: 10px;
+ }
+`;
+
+const ComponentFour: React.FC = () => {
+ return (
+ The thing that needs to be styled
+ );
+}
+
+// ComponentThree.tsx
+
+const ComponentThree: React.FC = () => {
+ return (
+ <>
+ Component 3 content
+
+ >
+ );
+}
+
+// ComponentTwo.tsx
+
+const ComponentTwo: React.FC = () => {
+ return (
+ <>
+ Component 2 content
+
+ >
+ );
+}
+
+// ComponentOne.tsx
+
+const ComponentOne: React.FC = () => {
+ return (
+ <>
+ Component 1 content
+
+ >
+ );
+}
+
+// WebPage.tsx
+
+const WebPage: React.FC = () => {
+ return (
+ <>
+ Welcome to our amazing webpage!
+
+ >
+ );
+}
+```
+
+which leaves space for the props that matter.
+
+## A more complicated example: Login Page
+
+So far our examples have been pretty basic. Our login container shows a more interesting way of
+using the techniques we've described so far. (You can play with resizing the page
+[**here**](https://map.treeboston.org/login)).
+
+On desktops, we show our standard design:
+
+
+
+We have less whitespace on narrow desktop:
+
+
+
+The blocks stack on tablet:
+
+
+
+And we have a much simpler design for mobile:
+
+
+
+Let's look at the code to see how it works (narrowing in on the code that matters for styling).
+First, for `WindowTypes.Desktop`, `WindowTypes.NarrowDesktop`, and `WindowTypes.Tablet`, we
+render the same blocks and have mostly the same styling, but we use `flex` to make the grey and
+green boxes appear reasonably on the screen. We also use relative dimensions:
+
+```js
+const InputGreetingContainer = styled.div`
+ width: 100vw; /* use relative dimensions to make the boxes as wide as the screen */
+
+ /* make the boxes as tall as the parent container (the screen minus the nav bar), or 575px
+ (whichever is taller). this way, if the screen is short, we can scroll down to see the rest
+ of the content instead of making the content look too short. */
+ min-height: 575px;
+ height: 100%;
+
+ display: flex; /* use flex to responsively position the boxes */
+
+ /* by using wrap, the green box will be below the grey on tablet,
+ ensuring the boxes never get too skinny */
+ flex-wrap: wrap;
+
+ /* center the boxes in the screen and parent container */
+ align-items: center;
+ justify-content: space-around;
+ align-content: center;
+
+ /* define the gap between the boxes */
+ gap: 20px;
+`;
+
+const Login: React.FC = () => {
+ return (
+
+ {/* grey box */}
+
+ {LOGIN_TITLE}
+
+
+ {ForgotPasswordFooter}
+
+
+ {/* green box */}
+
+
+ );
+}
+```
+
+We also add a media rule to `InputGreetingContainer` to make sure the boxes are big enough on
+tablets (since the paragraphs are not as wide, they'll need to have more lines, making them taller
+than on desktops).
+
+```js
+export const InputGreetingContainer = styled.div`
+ width: 100vw;
+ min-height: 575px;
+ height: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-around;
+ align-content: center;
+ gap: 20px;
+
+ /* make sure the boxes are tall enough to fit all content on tablets */
+ @media (max-width: ${BREAKPOINT_TABLET}px) {
+ min-height: 900px;
+ }
+`;
+```
+
+Finally, for mobile we'll use `useWindowDimensions` to not render the boxes at all:
+
+```js
+export const InputGreetingContainer = styled.div`
+ width: 100vw;
+ min-height: 575px;
+ height: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-around;
+ align-content: center;
+ gap: 20px;
+
+ /* make sure the boxes are tall enough to fit all content on tablets */
+ @media (max-width: ${BREAKPOINT_TABLET}px) {
+ min-height: 900px;
+ }
+`;
+
+const Login: React.FC = () => {
+ const { windowType } = useWindowDimensions();
+
+ return (
+ <>
+ {(() => {
+ switch (windowType) {
+ case WindowTypes.Mobile:
+ return (
+
+
+
+ {ForgotPasswordFooter}
+
+ );
+ case WindowTypes.Tablet:
+ case WindowTypes.NarrowDesktop:
+ case WindowTypes.Desktop:
+ return (
+
+
+
+ {LOGIN_TITLE}
+
+
+ {ForgotPasswordFooter}
+
+
+
+
+
+ );
+ }
+ })()}
+ >
+ );
+};
+```
+
+Congrats! You've made it to the end :tada:
diff --git a/docs/sftt/index.md b/docs/sftt/index.md
index dfadd61..0148b8b 100644
--- a/docs/sftt/index.md
+++ b/docs/sftt/index.md
@@ -4,8 +4,18 @@ In this section you will find all the developer documentation related to our Spe
## [API Specification](./sftt-api-spec)
-The full API specification for our backend. This page lists every backend route and describes what it does, the request body and the possible responses.
+The full API specification for our backend. This page lists every backend route and describes what
+it does, the request body and the possible responses.
## [(DEPRICATED) ArcGIS Map Documentation](./arcgis-architecture.md)
-A thorough description of our legacy ArcGIS map technology. This has since been replaced by the Google Maps API.
\ No newline at end of file
+A thorough description of our legacy ArcGIS map technology. This has since been replaced by the
+Google Maps API.
+
+## [Frontend Guides](./frontend-guides/sftt-frontend-walkthrough)
+
+A set of guides documenting best practices and how-tos for developing in our React frontend. Topics
+include:
+
+- [Routing](./frontend-guides/sftt-routing.md)
+- [Responsive Styling](./frontend-guides/sftt-responsive-styling.md)
diff --git a/mkdocs.yml b/mkdocs.yml
index 92da091..59e210b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -82,6 +82,7 @@ nav:
- Frontend Guides:
- Walkthrough: sftt/frontend-guides/sftt-frontend-walkthrough.md
- Routing: sftt/frontend-guides/sftt-routing.md
+ - Responsive Styling in React: sftt/frontend-guides/sftt-responsive-styling.md
- HATS:
- Home: hats/index.md
- Auth API Specification: hats/api-spec-auth.md