diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/Activities/Module01 Activity/index.html b/Activities/Module01 Activity/index.html new file mode 100644 index 00000000..df61c49d --- /dev/null +++ b/Activities/Module01 Activity/index.html @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +User Stories and Conditions of Satisfaction | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + User Stories and Conditions of Satisfaction + + +

+ +

+ + + Introduction + + +

+ + +

This activity will give you practice with using user stories to solicit and document user requirements, refining them into conditions of satisfaction, and assigning priorities.

+ +

Before you start, be sure to review the tutorial User Stories and Conditions of Satisfaction on the course website.

+ +

Choose one of the two scenarios (as required by your instructor):

+

+ + + Scenario 1 + + +

+ + +

Imagine that the State of Massachusetts is your client and you are the lead of a development team. You have already walked your client through the material in the tutorial, and together you have developed the following user story:

+ +
    +
  • As a resident of the State of Massachusetts, I would like to obtain my mail-in ballot so that I can cast my vote by mail.
  • +
+

+ + + Requirements for this activity + + +

+ + +
    +
  1. Write down at least 3 questions that you should ask the client before beginning to write conditions of satisfaction for this user story.
  2. +
  3. Write at least 3 conditions of satisfaction with appropriate priority
  4. +
  5. Then write 3 bad examples of conditions of satisfaction, and briefly say why they are bad.
  6. +
+ +

When you are done, submit your work as required by your instructor. This may vary from section to section.

+

+ + + Scenario 2 + + +

+ + +

Imagine that you are the owner of a restaurant, and your consultant has convinced you that you need to have a web site. You and they have walked through the material in Module 01 and the Tutorial on User Stories. Your consultant is also a fan of “systems thinking”, by which they mean looking at a problem from many different points of view.

+

+ + + Requirements for this activity + + +

+ + +
    +
  1. Identify at least 3 different roles representing different points of view
  2. +
  3. +

    Choose one of those roles and write down at least 3 different user stories for that participant. These should be of the form

    + +

    As a <role> I want to <some property of the system> so that I can <receive some benefit>

    +
  4. +
  5. For one of those user stories, write 3 conditions of satisfaction with appropriate priority.
  6. +
+ +

When you are done, submit your work as required by your instructor. This may vary from section to section.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/Activities/Module02 Activity/index.html b/Activities/Module02 Activity/index.html new file mode 100644 index 00000000..c49a681a --- /dev/null +++ b/Activities/Module02 Activity/index.html @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Test-Driven Development | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Test-Driven Development + + +

+ + +

This activity is intended to supplement the CS4530 lecture on test-driven development.

+

+ + + Steps + + +

+ + +
    +
  1. Download and unpack the starter Code
  2. +
  3. Write down the testable behaviors for the satisfaction condition: “Add a new grade for an existing student”
  4. +
  5. Identify at least two exceptional conditions or design decisions associated with these testable behaviors
  6. +
  7. Write Jest tests for your testable behaviors
  8. +
  9. Implement a method addGrade that passes your tests.
  10. +
+ +

When you are done, submit your work as required by your instructor. This may vary from section to section.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/Activities/Module03 Activity/index.html b/Activities/Module03 Activity/index.html new file mode 100644 index 00000000..ea1399ff --- /dev/null +++ b/Activities/Module03 Activity/index.html @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Test Adequacy | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Test Adequacy Activity + + +

+ +

We will gain experience improving test suites using two adequacy criteria: line coverage and mutation coverage. The instructions reference line numbers in transcriptManager.ts. Do not change that file while you are following along, or else you may find that the line referenced do not match what you see.

+ +

As usual, download the starter code and run npm install.

+ +

Then, follow the instructions given in the README.md file

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/Activities/Module04 Activity/index.html b/Activities/Module04 Activity/index.html new file mode 100644 index 00000000..1e5f2e4c --- /dev/null +++ b/Activities/Module04 Activity/index.html @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Traffic Light Activity Handout | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Overview + + +

+ +

In this activity, you will do a simple example to help you recognize failure of the five code-level design principles

+

+ + + Step 0: Getting started + + +

+ +

Start by downloading the (Starter Code). +Run npm install to download the dependencies for this project, and then open it in your IDE of choice. +Run npm run test to run the tests of the existing code (or run the script in VSC). You will see that the tests pass.

+

+ + + The Problem + + +

+ + +

Your team is writing a simulator for a traffic light in a large city. Whitley, an intern in your group, has written the code in TrafficLight.ts, and the tests in TrafficLight.test.ts

+ +

Your team leader, Adrian, has asked you to critique Whitley’s code based on the Five Code-Level Design Principles. Adrian says that he has already received some feedback from the client, who complained that not all traffic lights change when they are expected (they expect all lights to change at the same time, and for the red to be 20, yellow 5 and green 15 seconds). Moreover, the lights in different intersections are not synchronized, so that cars have to stop at every intersection.

+

+ + + Part 1: Critique Whitley’s code. + + +

+ + +

In a text file, list several ways in which Whitley’s code violates the 5 Principles.

+

+ + + Part 2: Improve Whitley’s code + + +

+ + +

Write a new file, called betterTrafficLight.ts, which Principles, and addresses some of the problems raised by the customer. Test your code by changing TrafficLight.test.ts to import from betterTrafficLight.ts

+

+ + + Part 3: Critique your own code + + +

+ + +

Now look at your own code. Based on your experience with traffic lights, list 3 assumptions that your code makes about the behavior of traffic lights in a large city. In what ways is the client likely to be unhappy with the assumptions you made? (Stretch goal: how could you redesign your code to make it more flexible?)

+

+ + + Submit your work + + +

+ + +

Instructions for submitting this activity will appear in Canvas.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/Activities/Module05 Activity/index.html b/Activities/Module05 Activity/index.html new file mode 100644 index 00000000..6e762a1c --- /dev/null +++ b/Activities/Module05 Activity/index.html @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Weather Station Observer Pattern Activity Handout | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+ +

This project will give you practice in coding using the observer design pattern.

+ +

Start by downloading the +starter code.

+

+ + + Change Log + + +

+ +
    +
  • 2024-01-23: Migrate ts-node implementation to tsx for node 18.18 compatibility
  • +
+

+ + + Instructions to install Node-Version + + +

+ +
    +
  • nvm install 18.18
  • +
  • nvm use 18.18
  • +
+

+ + + Instructions + + +

+ + +

To run it on your computer, run npm install to fetch the dependencies for the project, and then run npm run demo, which should produce the output like the following:

+
Current conditions: 80F degrees and 65% humidity
+Avg/max/min temperature = 80/80/0
+Heat Index: 82.95535063710001
+Forecast: Improving weather on the way!
+Current conditions: 82F degrees and 70% humidity
+Avg/max/min temperature = 81/82/0
+Heat Index: 86.90123306385205
+Forecast: Watch out for cooler, rainy weather
+Current conditions: 78F degrees and 90% humidity
+Avg/max/min temperature = 80/82/0
+Heat Index: 83.64967139559604
+Forecast: More of the same
+
+ +

There is a lot to be improved from this design. Modify this code so that it uses the observer pattern, with each of the various display classes as +the observers, and the WeatherData as the subject object.

+ +

A high-level sketch of this design is:

+
    +
  1. Create a WeatherDataObserver interface, which defines your update method
  2. +
  3. In WeatherData, create an observers array in WeatherData along with methods to register and de-register observers. Add code to notify the observers of updates when the weather data updates.
  4. +
  5. Modify each of CurrentConditionsDisplay, ForecastDisplay, HeatIndexDisplay and StatisticsDisplay to be implementors of the new observer interface. These display classes should display their information whenever the weather data is updated.
  6. +
  7. Modify WeatherStation, so that it creates the XXXDisplays, and subscribes them to the WeatherData
  8. +
  9. Update WeatherData.measurementsChanged to notify its observers of the update
  10. +
  11. Compare the output of your new program to the output you got by running the original version. Are the lines printed in the same order? Why or why not?
  12. +
  13. Can you modify the code so that the various XXXDisplay classes are all implementations of the same interface? Why might or might not this be a good idea?
  14. +
+ +

When you are done, run npm run zip to create a zip archive with your code.

+ +

This activity is based on the running example in Chapter 2 of “Head First Design Patterns, 2nd Edition” by Robson and Freeman.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/Activities/Module06 Activity/index.html b/Activities/Module06 Activity/index.html new file mode 100644 index 00000000..0ef80f1a --- /dev/null +++ b/Activities/Module06 Activity/index.html @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Async Activity | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Simple Activity using async/await + + +

+ + +

Learning Objectives for this activity:

+
    +
  • Practice applying asynchronous programming concepts: promises, async/await
  • +
  • Experiment with applying different ordering constraints in asynchronous code
  • +
+

+ + + Overview + + +

+ +

In this activity, you will experiment with asynchronous programming constructs in TypeScript.

+

+ + + Getting started + + +

+ +

Download starter Code +Run npm install to download the dependencies for this project, and then open it in your IDE of choice. +Run npm run client to run the client as-is, the output should be something like:

+ +
Creating a student
+Import grades completed, and returned:
+[
+  {
+    "student": {
+      "studentID": 17,
+      "studentName": "test student"
+    },
+    "grades": [
+      {
+        "course": "demo course",
+        "grade": 100
+      }
+    ]
+  }
+]
+
+
+

+ + + Stringing together many async calls: bulk importing grades + + +

+ +

Your task is to write a new, async function, importGrades, which takes in input of the type ImportTranscript[]. +importGrades should create a student record for each ImportTranscript, and then post the grades for each of those students. +After posting the grades, it should fetch the transcripts for each student and return an array of transcripts.

+ +

You should implement importGrades in the file examples.ts - note that there is already a function stub there. +As you get started, examine the transcript server client in client.ts, and take note of the API calls that are available to you.

+ +

Here is the type definition for ImportTranscript and its dependencies:

+
type ImportTranscript = {
+  studentName: string;
+  grades: CourseGrade[];
+};
+type CourseGrade = { course: Course, grade: number };
+type Course = string;
+
+ +

Example input:

+
[
+    {
+        studentName: "Avery",
+        grades: [{course: "Software Engineering", grade: 100}, {course: "Chemistry", grade: 70}],
+    },
+    {
+        studentName: "Ripley",
+        grades: [{course: "Underwater Basket Weaving", grade: 100}, {course: "Kayaking", grade: 90} ]
+    }
+]
+
+ +

Implement this three ways:

+
    +
  1. Insert a student, insert each of their grades (in order), then insert the next student, then their grades, etc. until all students are inserted, then fetch transcripts
  2. +
  3. Insert a student, then insert each of their grades (in order), then fetch their transcript. Do this set of operations asynchronously (concurrently) for all students
  4. +
  5. Insert a student, then insert each of their grades asynchronously (concurrently). After all students have all of their grades submitted, fetch all fo the transcripts asynchronously (concurrently)
  6. +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/Activities/Module08 Activity/index.html b/Activities/Module08 Activity/index.html new file mode 100644 index 00000000..b2438abf --- /dev/null +++ b/Activities/Module08 Activity/index.html @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +React | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + React Activity - Todo Tracker + + +

+ + +

This activity is designed to give you practice with React. You will enhance the ToDo app that was discussed in lecture.

+

+ + + Steps + + +

+ + +

To get started, download the starter Code. Run the command npm install, and then npm run dev. The development server should start, and If you open your browser it will start running the app in app/page.tsx If your browser does not open up in the todo app, modify app/page.tsx to point to ./Apps/ToDoApp

+ +

In the ToDoApp, make the following three enhancements:

+ +
    +
  1. Currently the “priority” field will accept any value. Modify it so that the priority must be a number. (Hint: NumberInput is your friend)
  2. +
  3. Add a button that will sort the todo items by priority, lowest number first
  4. +
  5. Add an entry field that will take a number and delete all the todo items with priorities greater than that number. (We are assuming that priority 1 means the thing that has to be done first.)
  6. +
+ +

When you are done, submit your work as required by your instructor. This may vary from section to section.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/Activities/Module11 REST Activity/index.html b/Activities/Module11 REST Activity/index.html new file mode 100644 index 00000000..7f38bff9 --- /dev/null +++ b/Activities/Module11 REST Activity/index.html @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Working with REST / OpenAPI | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + REST Transcript Activity + + +

+ + +

In this activity, you will use TSOA to build a REST client for our transcript database.

+ +

Requirement: node -version = @16 0r @17

+ +

As usual, download the starter code and run npm install.

+ +

Then, run npm start to generate the OpenAPI specification, server boilerplate, and start the development server. +This command will automatically reload the server as you change files in this project. +To stop the server, press control-C in the terminal.

+ +

Once you see the message “Listening on 8081”, you can access this URL in your browser: +http://localhost:8081/docs/#/transcript

+ +

You should now see a “Swagger” transcript-server-openapi documentation page, with a few API endpoints defined. Expand the “GET /transcripts” endpoint, click “Try it out”, and then “Execute”. Now, the field “Response Body” should have text in it like:

+
[
+  {
+    "student": {
+      "studentID": 1,
+      "studentName": "avery"
+    },
+    "grades": [
+      {
+        "course": "DemoClass",
+        "grade": 100
+      },
+      {
+        "course": "DemoClass2",
+        "grade": 100
+      }
+    ]
+  },
+
+ +

This demonstrates that this endpoint of your REST API is functional. In the rest of the activity, you will implement the remainder of the routes. Details are in the README page of the starter code.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/Activities/assets/continuous_dev/gha-secrets-twilio.png b/Activities/assets/continuous_dev/gha-secrets-twilio.png new file mode 100644 index 00000000..c5e40168 Binary files /dev/null and b/Activities/assets/continuous_dev/gha-secrets-twilio.png differ diff --git a/Activities/assets/continuous_dev/heroku-account-settings-menu.png b/Activities/assets/continuous_dev/heroku-account-settings-menu.png new file mode 100644 index 00000000..386abbcd Binary files /dev/null and b/Activities/assets/continuous_dev/heroku-account-settings-menu.png differ diff --git a/Activities/assets/continuous_dev/heroku-settings-expanded.png b/Activities/assets/continuous_dev/heroku-settings-expanded.png new file mode 100644 index 00000000..b5893b93 Binary files /dev/null and b/Activities/assets/continuous_dev/heroku-settings-expanded.png differ diff --git a/Activities/assets/continuous_dev/heroku-settings.png b/Activities/assets/continuous_dev/heroku-settings.png new file mode 100644 index 00000000..2da7261f Binary files /dev/null and b/Activities/assets/continuous_dev/heroku-settings.png differ diff --git a/Activities/assets/continuous_dev/netlify-build-settings.png b/Activities/assets/continuous_dev/netlify-build-settings.png new file mode 100644 index 00000000..86d9c148 Binary files /dev/null and b/Activities/assets/continuous_dev/netlify-build-settings.png differ diff --git a/Activities/assets/continuous_dev/netlify-done.png b/Activities/assets/continuous_dev/netlify-done.png new file mode 100644 index 00000000..d821eecc Binary files /dev/null and b/Activities/assets/continuous_dev/netlify-done.png differ diff --git a/Activities/git-placeholder.txt b/Activities/git-placeholder.txt new file mode 100644 index 00000000..2d6401c9 --- /dev/null +++ b/Activities/git-placeholder.txt @@ -0,0 +1 @@ +Placeholder file to force git to create a folder here. \ No newline at end of file diff --git a/Activities/module02-tdd-transcript-activity.zip b/Activities/module02-tdd-transcript-activity.zip new file mode 100644 index 00000000..71aecbf3 Binary files /dev/null and b/Activities/module02-tdd-transcript-activity.zip differ diff --git a/Activities/module03-test-adequacy.zip b/Activities/module03-test-adequacy.zip new file mode 100644 index 00000000..3579eb46 Binary files /dev/null and b/Activities/module03-test-adequacy.zip differ diff --git a/Activities/module04-traffic-light-activity.zip b/Activities/module04-traffic-light-activity.zip new file mode 100644 index 00000000..d912e95e Binary files /dev/null and b/Activities/module04-traffic-light-activity.zip differ diff --git a/Activities/module05-design-pattern-activity-weatherstation.zip b/Activities/module05-design-pattern-activity-weatherstation.zip new file mode 100644 index 00000000..42da8a9b Binary files /dev/null and b/Activities/module05-design-pattern-activity-weatherstation.zip differ diff --git a/Activities/module06-async_activity.zip b/Activities/module06-async_activity.zip new file mode 100644 index 00000000..53848a30 Binary files /dev/null and b/Activities/module06-async_activity.zip differ diff --git a/Activities/module08-react-activity.zip b/Activities/module08-react-activity.zip new file mode 100644 index 00000000..1acc43b6 Binary files /dev/null and b/Activities/module08-react-activity.zip differ diff --git a/Activities/module11-rest-transcript-activity.zip b/Activities/module11-rest-transcript-activity.zip new file mode 100644 index 00000000..94b5ee59 Binary files /dev/null and b/Activities/module11-rest-transcript-activity.zip differ diff --git a/Activities/week1-user-stories-solutions-Spring 2024.txt b/Activities/week1-user-stories-solutions-Spring 2024.txt new file mode 100644 index 00000000..c7a88fc7 --- /dev/null +++ b/Activities/week1-user-stories-solutions-Spring 2024.txt @@ -0,0 +1,43 @@ +Roles: + +Owner/Manager +Prospective Customer +Chef + + +* As Owner/Manager I want + +to be able to add and delete menu items so I can change the menu easily (Essential) + +to be able to change the price of a menu item so I can respond to changing +supply costs. (Essential) + +to have pictures of my dises on the web site, so customers will see how good +our food looks (Desirable) + + +* As a Prospective Customer, I want + +the web site to show what is on the menu today, so I can see if they have +what I want (Essential) + +to have nice pictures of the restaurant, so I can get an idea of whether I +want to go there. (Essential) + +to be easy to navigate to different portions of the menu, so I can easily +distinguish appetizers/main dishes/desserts, etc. (Desirable) + +to have possible substitutions listed, so I can know whether a dish can be +modified to meet my dietary needs. (Extension) + +* As a chef, I want + +the web site to keep track of how many of each dish was ordered, so I can +know how much of each ingredient to buy. (Essential? Desirable?) + +the web site to show customers what substitutions are possible, so I can keep track of +what variations I need to prepare for. (Extension) + + + + diff --git a/Assignments/git-fodder.txt b/Assignments/git-fodder.txt new file mode 100644 index 00000000..7b57bd29 --- /dev/null +++ b/Assignments/git-fodder.txt @@ -0,0 +1 @@ +some text diff --git a/Assignments/ip1/ip1-handout.zip b/Assignments/ip1/ip1-handout.zip new file mode 100644 index 00000000..cd7c94cb Binary files /dev/null and b/Assignments/ip1/ip1-handout.zip differ diff --git a/Assignments/ip1/npm-warnings.jpg b/Assignments/ip1/npm-warnings.jpg new file mode 100644 index 00000000..cb73aca9 Binary files /dev/null and b/Assignments/ip1/npm-warnings.jpg differ diff --git a/Assignments/ip2/GamesArea.test.tsx b/Assignments/ip2/GamesArea.test.tsx new file mode 100644 index 00000000..430e29e0 --- /dev/null +++ b/Assignments/ip2/GamesArea.test.tsx @@ -0,0 +1,373 @@ +import { ChakraProvider } from '@chakra-ui/react'; +import { render, screen } from '@testing-library/react'; +import { mock, mockReset } from 'jest-mock-extended'; +import { nanoid } from 'nanoid'; +import { act } from 'react-dom/test-utils'; +import GameAreaController, { + GameEventTypes, +} from '../../../classes/interactable/GameAreaController'; +import PlayerController from '../../../classes/PlayerController'; +import TownController, * as TownControllerHooks from '../../../classes/TownController'; +import TownControllerContext from '../../../contexts/TownControllerContext'; +import { randomLocation } from '../../../TestUtils'; +import { GameArea, GameResult, GameState, InteractableType } from '../../../types/CoveyTownSocket'; +import * as ChatChannel from './ChatChannel'; +import * as ConnectFourArea from './ConnectFour/ConnectFourArea'; +import PhaserGameArea from './GameArea'; +import GamesArea, { INVALID_GAME_AREA_TYPE_MESSAGE } from './GamesArea'; +import * as Leaderboard from './Leaderboard'; +import * as TicTacToeArea from './TicTacToe/TicTacToeArea'; +import React from 'react'; + +const mockToast = jest.fn(); +jest.mock('@chakra-ui/react', () => { + const ui = jest.requireActual('@chakra-ui/react'); + const mockUseToast = () => mockToast; + return { + ...ui, + useToast: mockUseToast, + }; +}); +const mockGameArea = mock({ + id: nanoid(), +}); +mockGameArea.name = 'TicTacToe'; +mockGameArea.getData.mockReturnValue('TicTacToe'); +jest.spyOn(TownControllerHooks, 'useInteractable').mockReturnValue(mockGameArea); + +const useInteractableAreaControllerSpy = jest.spyOn( + TownControllerHooks, + 'useInteractableAreaController', +); + +const CHAT_CHANNEL_TEST_ID = 'chatWindow'; +const chatChannelSpy = jest.spyOn(ChatChannel, 'default'); +chatChannelSpy.mockReturnValue(
); + +const TIC_TAC_TOE_AREA_TEST_ID = 'ticTacToeArea'; +const ticTacToeAreaSpy = jest.spyOn(TicTacToeArea, 'default'); +ticTacToeAreaSpy.mockReturnValue(
); + +const CONNECT_FOUR_AREA_TEST_ID = 'connectFourArea'; +const connectFourAreaSpy = jest.spyOn(ConnectFourArea, 'default'); +connectFourAreaSpy.mockReturnValue(
); + +const leaderboardComponentSpy = jest.spyOn(Leaderboard, 'default'); +leaderboardComponentSpy.mockReturnValue(
); + +class MockGameAreaController extends GameAreaController { + private _type: InteractableType = 'TicTacToeArea'; + + private _mockID: string; + + public constructor() { + const id = nanoid(); + super(id, mock>(), mock()); + this._mockID = id; + } + + public get id() { + return this._mockID; + } + + public set id(newID: string) { + this._mockID = newID; + } + + public set type(type: InteractableType) { + this._type = type; + } + + toInteractableAreaModel(): GameArea { + if (!this._type) throw new Error('Type not set'); + const ret = mock>(); + ret.type = this._type; + return ret; + } + + mockHistory: GameResult[] = []; + + mockObservers: PlayerController[] = []; + + get observers(): PlayerController[] { + return this.mockObservers; + } + + get history(): GameResult[] { + return this.mockHistory; + } + + public isActive(): boolean { + return true; + } +} +describe('GamesArea', () => { + // Spy on console.error and intercept react key warnings to fail test + let consoleErrorSpy: jest.SpyInstance; + beforeAll(() => { + // Spy on console.error and intercept react key warnings to fail test + consoleErrorSpy = jest.spyOn(global.console, 'error'); + consoleErrorSpy.mockImplementation((message?, ...optionalParams) => { + const stringMessage = message as string; + if (stringMessage.includes && stringMessage.includes('children with the same key,')) { + throw new Error(stringMessage.replace('%s', optionalParams[0])); + } else if (stringMessage.includes && stringMessage.includes('warning-keys')) { + throw new Error(stringMessage.replace('%s', optionalParams[0])); + } + // eslint-disable-next-line no-console -- we are wrapping the console with a spy to find react warnings + console.warn(message, ...optionalParams); + }); + }); + afterAll(() => { + consoleErrorSpy.mockRestore(); + }); + let ourPlayer: PlayerController; + const townController = mock(); + Object.defineProperty(townController, 'ourPlayer', { get: () => ourPlayer }); + let gameAreaController = new MockGameAreaController(); + function setGameAreaControllerID(id: string) { + gameAreaController.id = id; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + mockGameArea.id = id; + } + + beforeEach(() => { + ourPlayer = new PlayerController('player x', 'player x', randomLocation()); + mockReset(townController); + useInteractableAreaControllerSpy.mockReturnValue(gameAreaController); + setGameAreaControllerID(nanoid()); + leaderboardComponentSpy.mockClear(); + mockToast.mockClear(); + chatChannelSpy.mockClear(); + }); + function renderGamesArea() { + return render( + + + + + , + ); + } + + describe('[T2.4] Rendering the correct game', () => { + test('If the interactableID is for a ConnectFour game, the ConnectFourGameArea should be rendered', () => { + gameAreaController.type = 'ConnectFourArea'; + renderGamesArea(); + expect(screen.getByTestId(CONNECT_FOUR_AREA_TEST_ID)).toBeInTheDocument(); + }); + test('If the interactableID is for a TicTacToe game, the TicTacToeGameArea should be rendered', () => { + gameAreaController.type = 'TicTacToeArea'; + renderGamesArea(); + expect(screen.getByTestId(TIC_TAC_TOE_AREA_TEST_ID)).toBeInTheDocument(); + }); + test('If the interactableID is NOT for a ConnectFour or TicTacToe game, an error should be displayed', () => { + gameAreaController.type = 'ViewingArea'; //Not a game! + renderGamesArea(); + + expect(screen.queryByTestId(CONNECT_FOUR_AREA_TEST_ID)).toBeNull(); + expect(screen.queryByTestId(TIC_TAC_TOE_AREA_TEST_ID)).toBeNull(); + + expect(screen.getByText(INVALID_GAME_AREA_TYPE_MESSAGE)).toBeInTheDocument(); + }); + }); + describe('[T2.2] Leaderboard', () => { + it('Renders the leaderboard with the history when the component is mounted', () => { + gameAreaController.mockHistory = [ + { + gameID: nanoid(), + scores: { + [nanoid()]: 1, + [nanoid()]: 0, + }, + }, + ]; + renderGamesArea(); + expect(leaderboardComponentSpy).toHaveBeenCalledWith( + { + results: gameAreaController.mockHistory, + }, + {}, + ); + }); + it('Renders the leaderboard with the history when the game is updated', () => { + gameAreaController.mockHistory = [ + { + gameID: nanoid(), + scores: { + [nanoid()]: 1, + [nanoid()]: 0, + }, + }, + ]; + renderGamesArea(); + expect(leaderboardComponentSpy).toHaveBeenCalledWith( + { + results: gameAreaController.mockHistory, + }, + {}, + ); + + gameAreaController.mockHistory = [ + { + gameID: nanoid(), + scores: { + [nanoid()]: 1, + [nanoid()]: 1, + }, + }, + ]; + act(() => { + gameAreaController.emit('gameUpdated'); + }); + expect(leaderboardComponentSpy).toHaveBeenCalledWith( + { + results: gameAreaController.mockHistory, + }, + {}, + ); + }); + }); + describe('[T2.3] List of observers', () => { + beforeEach(() => { + gameAreaController.mockObservers = [ + new PlayerController('player 1', 'player 1', randomLocation()), + new PlayerController('player 2', 'player 2', randomLocation()), + new PlayerController('player 3', 'player 3', randomLocation()), + ]; + }); + it('Displays the correct observers when the component is mounted', () => { + renderGamesArea(); + const observerList = screen.getByLabelText('list of observers in the game'); + const observerItems = observerList.querySelectorAll('li'); + expect(observerItems).toHaveLength(gameAreaController.mockObservers.length); + for (let i = 0; i < observerItems.length; i++) { + expect(observerItems[i]).toHaveTextContent(gameAreaController.mockObservers[i].userName); + } + }); + it('Displays the correct observers when the game is updated', () => { + renderGamesArea(); + act(() => { + gameAreaController.mockObservers = [ + new PlayerController('player 1', 'player 1', randomLocation()), + new PlayerController('player 2', 'player 2', randomLocation()), + new PlayerController('player 3', 'player 3', randomLocation()), + new PlayerController('player 4', 'player 4', randomLocation()), + ]; + gameAreaController.emit('gameUpdated'); + }); + const observerList = screen.getByLabelText('list of observers in the game'); + const observerItems = observerList.querySelectorAll('li'); + expect(observerItems).toHaveLength(gameAreaController.mockObservers.length); + for (let i = 0; i < observerItems.length; i++) { + expect(observerItems[i]).toHaveTextContent(gameAreaController.mockObservers[i].userName); + } + }); + }); + describe('[T2.1] Listeners', () => { + it('Registers exactly one listeners when mounted: for gameUpdated', () => { + const addListenerSpy = jest.spyOn(gameAreaController, 'addListener'); + addListenerSpy.mockClear(); + + renderGamesArea(); + expect(addListenerSpy).toBeCalledTimes(1); + expect(addListenerSpy).toHaveBeenCalledWith('gameUpdated', expect.any(Function)); + }); + it('Does not register listeners on every render', () => { + const removeListenerSpy = jest.spyOn(gameAreaController, 'removeListener'); + const addListenerSpy = jest.spyOn(gameAreaController, 'addListener'); + addListenerSpy.mockClear(); + removeListenerSpy.mockClear(); + const renderData = renderGamesArea(); + expect(addListenerSpy).toBeCalledTimes(1); + addListenerSpy.mockClear(); + + renderData.rerender( + + + + + , + ); + + expect(addListenerSpy).not.toBeCalled(); + expect(removeListenerSpy).not.toBeCalled(); + }); + it('Removes the listeners when the component is unmounted', () => { + const removeListenerSpy = jest.spyOn(gameAreaController, 'removeListener'); + const addListenerSpy = jest.spyOn(gameAreaController, 'addListener'); + addListenerSpy.mockClear(); + removeListenerSpy.mockClear(); + const renderData = renderGamesArea(); + expect(addListenerSpy).toBeCalledTimes(1); + const addedListeners = addListenerSpy.mock.calls; + const addedGameUpdateListener = addedListeners.find(call => call[0] === 'gameUpdated'); + expect(addedGameUpdateListener).toBeDefined(); + renderData.unmount(); + expect(removeListenerSpy).toBeCalledTimes(1); + const removedListeners = removeListenerSpy.mock.calls; + const removedGameUpdateListener = removedListeners.find(call => call[0] === 'gameUpdated'); + expect(removedGameUpdateListener).toEqual(addedGameUpdateListener); + }); + it('Creates new listeners if the gameAreaController changes', () => { + const removeListenerSpy = jest.spyOn(gameAreaController, 'removeListener'); + const addListenerSpy = jest.spyOn(gameAreaController, 'addListener'); + addListenerSpy.mockClear(); + removeListenerSpy.mockClear(); + const renderData = renderGamesArea(); + expect(addListenerSpy).toBeCalledTimes(1); + + gameAreaController = new MockGameAreaController(); + const removeListenerSpy2 = jest.spyOn(gameAreaController, 'removeListener'); + const addListenerSpy2 = jest.spyOn(gameAreaController, 'addListener'); + + useInteractableAreaControllerSpy.mockReturnValue(gameAreaController); + renderData.rerender( + + + + + , + ); + expect(removeListenerSpy).toBeCalledTimes(1); + + expect(addListenerSpy2).toBeCalledTimes(1); + expect(removeListenerSpy2).not.toBeCalled(); + }); + }); + describe('[T2.5] Chat', () => { + it('Renders a ChatChannel with the interactableID', () => { + renderGamesArea(); + expect(chatChannelSpy).toHaveBeenCalledWith( + { + interactableID: gameAreaController.id, + }, + {}, + ); + }); + it('Re-renders the ChatChannel when the interactableID changes', () => { + const renderData = renderGamesArea(); + expect(chatChannelSpy).toHaveBeenCalledWith( + { + interactableID: gameAreaController.id, + }, + {}, + ); + setGameAreaControllerID(nanoid()); + renderData.rerender( + + + + + , + ); + expect(chatChannelSpy).toHaveBeenCalledWith( + { + interactableID: gameAreaController.id, + }, + {}, + ); + }); + }); +}); diff --git a/Assignments/ip2/ip2-handout.zip b/Assignments/ip2/ip2-handout.zip new file mode 100644 index 00000000..55de3324 Binary files /dev/null and b/Assignments/ip2/ip2-handout.zip differ diff --git a/Examples/conversation-areas-poster.pdf b/Examples/conversation-areas-poster.pdf new file mode 100644 index 00000000..812dbdc3 Binary files /dev/null and b/Examples/conversation-areas-poster.pdf differ diff --git a/Examples/fall-2023-react-examples.zip b/Examples/fall-2023-react-examples.zip new file mode 100644 index 00000000..24aac47b Binary files /dev/null and b/Examples/fall-2023-react-examples.zip differ diff --git a/Examples/git-placeholder.txt b/Examples/git-placeholder.txt new file mode 100644 index 00000000..2d6401c9 --- /dev/null +++ b/Examples/git-placeholder.txt @@ -0,0 +1 @@ +Placeholder file to force git to create a folder here. \ No newline at end of file diff --git a/Examples/module-05-design-patterns-examples.zip b/Examples/module-05-design-patterns-examples.zip new file mode 100644 index 00000000..9f95b446 Binary files /dev/null and b/Examples/module-05-design-patterns-examples.zip differ diff --git a/Examples/module-06-async-examples.zip b/Examples/module-06-async-examples.zip new file mode 100644 index 00000000..d0428d01 Binary files /dev/null and b/Examples/module-06-async-examples.zip differ diff --git a/Examples/module-08-react-examples.zip b/Examples/module-08-react-examples.zip new file mode 100644 index 00000000..f6012149 Binary files /dev/null and b/Examples/module-08-react-examples.zip differ diff --git a/Examples/module-09-react-hooks-examples.zip b/Examples/module-09-react-hooks-examples.zip new file mode 100644 index 00000000..d3f843d1 Binary files /dev/null and b/Examples/module-09-react-hooks-examples.zip differ diff --git a/Examples/module-12-testing-examples (Mitch's version).zip b/Examples/module-12-testing-examples (Mitch's version).zip new file mode 100644 index 00000000..790ba87f Binary files /dev/null and b/Examples/module-12-testing-examples (Mitch's version).zip differ diff --git a/Examples/module-12-testing-examples.zip b/Examples/module-12-testing-examples.zip new file mode 100644 index 00000000..790ba87f Binary files /dev/null and b/Examples/module-12-testing-examples.zip differ diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..620a3690 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +index.html: + bundle exec jekyll serve diff --git a/Slides/Module 01.1 Course Introduction.pdf b/Slides/Module 01.1 Course Introduction.pdf new file mode 100644 index 00000000..b9f8a961 Binary files /dev/null and b/Slides/Module 01.1 Course Introduction.pdf differ diff --git a/Slides/Module 01.1 Course Introduction.pptx b/Slides/Module 01.1 Course Introduction.pptx new file mode 100644 index 00000000..1abdc642 Binary files /dev/null and b/Slides/Module 01.1 Course Introduction.pptx differ diff --git a/Slides/Module 01.2 Requirements Gathering.pdf b/Slides/Module 01.2 Requirements Gathering.pdf new file mode 100644 index 00000000..cc7dfd0c Binary files /dev/null and b/Slides/Module 01.2 Requirements Gathering.pdf differ diff --git a/Slides/Module 01.2 Requirements Gathering.pptx b/Slides/Module 01.2 Requirements Gathering.pptx new file mode 100644 index 00000000..809f3398 Binary files /dev/null and b/Slides/Module 01.2 Requirements Gathering.pptx differ diff --git a/Slides/Module 02 From Requirements to Code.pdf b/Slides/Module 02 From Requirements to Code.pdf new file mode 100644 index 00000000..8b6ab515 Binary files /dev/null and b/Slides/Module 02 From Requirements to Code.pdf differ diff --git a/Slides/Module 02 From Requirements to Code.pptx b/Slides/Module 02 From Requirements to Code.pptx new file mode 100644 index 00000000..5eaa26b0 Binary files /dev/null and b/Slides/Module 02 From Requirements to Code.pptx differ diff --git a/Slides/Module 03 Test Adequacy.pdf b/Slides/Module 03 Test Adequacy.pdf new file mode 100644 index 00000000..2c8ea966 Binary files /dev/null and b/Slides/Module 03 Test Adequacy.pdf differ diff --git a/Slides/Module 03 Test Adequacy.pptx b/Slides/Module 03 Test Adequacy.pptx new file mode 100644 index 00000000..b4fb277b Binary files /dev/null and b/Slides/Module 03 Test Adequacy.pptx differ diff --git a/Slides/Module 04 Code Level Design.pdf b/Slides/Module 04 Code Level Design.pdf new file mode 100644 index 00000000..6337acd6 Binary files /dev/null and b/Slides/Module 04 Code Level Design.pdf differ diff --git a/Slides/Module 04 Code Level Design.pptx b/Slides/Module 04 Code Level Design.pptx new file mode 100644 index 00000000..ac727392 Binary files /dev/null and b/Slides/Module 04 Code Level Design.pptx differ diff --git a/Slides/Module 05 Interaction-Level Design Patterns.pdf b/Slides/Module 05 Interaction-Level Design Patterns.pdf new file mode 100644 index 00000000..ec8020b8 Binary files /dev/null and b/Slides/Module 05 Interaction-Level Design Patterns.pdf differ diff --git a/Slides/Module 05 Interaction-Level Design Patterns.pptx b/Slides/Module 05 Interaction-Level Design Patterns.pptx new file mode 100644 index 00000000..54951908 Binary files /dev/null and b/Slides/Module 05 Interaction-Level Design Patterns.pptx differ diff --git a/Slides/Module 06 Concurrency Patterns in Typescript.pdf b/Slides/Module 06 Concurrency Patterns in Typescript.pdf new file mode 100644 index 00000000..92c15366 Binary files /dev/null and b/Slides/Module 06 Concurrency Patterns in Typescript.pdf differ diff --git a/Slides/Module 06 Concurrency Patterns in Typescript.pptx b/Slides/Module 06 Concurrency Patterns in Typescript.pptx new file mode 100644 index 00000000..2bf3d7aa Binary files /dev/null and b/Slides/Module 06 Concurrency Patterns in Typescript.pptx differ diff --git a/Slides/Module 07.1 Software Process.pdf b/Slides/Module 07.1 Software Process.pdf new file mode 100644 index 00000000..2807c7cf Binary files /dev/null and b/Slides/Module 07.1 Software Process.pdf differ diff --git a/Slides/Module 07.1 Software Process.pptx b/Slides/Module 07.1 Software Process.pptx new file mode 100644 index 00000000..7bb60002 Binary files /dev/null and b/Slides/Module 07.1 Software Process.pptx differ diff --git a/Slides/Module 07.2 Agile Planning and Estimation.pdf b/Slides/Module 07.2 Agile Planning and Estimation.pdf new file mode 100644 index 00000000..63d38181 Binary files /dev/null and b/Slides/Module 07.2 Agile Planning and Estimation.pdf differ diff --git a/Slides/Module 07.2 Agile Planning and Estimation.pptx b/Slides/Module 07.2 Agile Planning and Estimation.pptx new file mode 100644 index 00000000..7554c5bb Binary files /dev/null and b/Slides/Module 07.2 Agile Planning and Estimation.pptx differ diff --git a/Slides/Module 07.3 Teams.pdf b/Slides/Module 07.3 Teams.pdf new file mode 100644 index 00000000..b693dfa5 Binary files /dev/null and b/Slides/Module 07.3 Teams.pdf differ diff --git a/Slides/Module 07.3 Teams.pptx b/Slides/Module 07.3 Teams.pptx new file mode 100644 index 00000000..d540a832 Binary files /dev/null and b/Slides/Module 07.3 Teams.pptx differ diff --git a/Slides/Module 08 React Basics.pdf b/Slides/Module 08 React Basics.pdf new file mode 100644 index 00000000..d4f11fb2 Binary files /dev/null and b/Slides/Module 08 React Basics.pdf differ diff --git a/Slides/Module 08 React Basics.pptx b/Slides/Module 08 React Basics.pptx new file mode 100644 index 00000000..7ad2c0c7 Binary files /dev/null and b/Slides/Module 08 React Basics.pptx differ diff --git a/Slides/Module 09 React Hooks.pdf b/Slides/Module 09 React Hooks.pdf new file mode 100644 index 00000000..54c1d789 Binary files /dev/null and b/Slides/Module 09 React Hooks.pdf differ diff --git a/Slides/Module 09 React Hooks.pptx b/Slides/Module 09 React Hooks.pptx new file mode 100644 index 00000000..24988dc1 Binary files /dev/null and b/Slides/Module 09 React Hooks.pptx differ diff --git a/Slides/Module 10.1 Distributed Systems Goals and Challenges.pdf b/Slides/Module 10.1 Distributed Systems Goals and Challenges.pdf new file mode 100644 index 00000000..fdd00b02 Binary files /dev/null and b/Slides/Module 10.1 Distributed Systems Goals and Challenges.pdf differ diff --git a/Slides/Module 10.1 Distributed Systems Goals and Challenges.pptx b/Slides/Module 10.1 Distributed Systems Goals and Challenges.pptx new file mode 100644 index 00000000..1e497aa5 Binary files /dev/null and b/Slides/Module 10.1 Distributed Systems Goals and Challenges.pptx differ diff --git a/Slides/Module 10.2 Distributing Data.pdf b/Slides/Module 10.2 Distributing Data.pdf new file mode 100644 index 00000000..5b2bcff9 Binary files /dev/null and b/Slides/Module 10.2 Distributing Data.pdf differ diff --git a/Slides/Module 10.2 Distributing Data.pptx b/Slides/Module 10.2 Distributing Data.pptx new file mode 100644 index 00000000..8527cee4 Binary files /dev/null and b/Slides/Module 10.2 Distributing Data.pptx differ diff --git a/Slides/Module 11.1 Distributing Processing (Spring 24; Mitch's Version).pdf b/Slides/Module 11.1 Distributing Processing (Spring 24; Mitch's Version).pdf new file mode 100644 index 00000000..2dab22ef Binary files /dev/null and b/Slides/Module 11.1 Distributing Processing (Spring 24; Mitch's Version).pdf differ diff --git a/Slides/Module 11.1 Distributing Processing (Spring 24; Mitch's Version).pptx b/Slides/Module 11.1 Distributing Processing (Spring 24; Mitch's Version).pptx new file mode 100644 index 00000000..fe7f830b Binary files /dev/null and b/Slides/Module 11.1 Distributing Processing (Spring 24; Mitch's Version).pptx differ diff --git a/Slides/Module 11.1 Distributing Processing.pdf b/Slides/Module 11.1 Distributing Processing.pdf new file mode 100644 index 00000000..40238508 Binary files /dev/null and b/Slides/Module 11.1 Distributing Processing.pdf differ diff --git a/Slides/Module 11.1 Distributing Processing.pptx b/Slides/Module 11.1 Distributing Processing.pptx new file mode 100644 index 00000000..fe7f830b Binary files /dev/null and b/Slides/Module 11.1 Distributing Processing.pptx differ diff --git a/Slides/Module 11.2 Case Studies.pdf b/Slides/Module 11.2 Case Studies.pdf new file mode 100644 index 00000000..29d626d4 Binary files /dev/null and b/Slides/Module 11.2 Case Studies.pdf differ diff --git a/Slides/Module 11.2 Case Studies.pptx b/Slides/Module 11.2 Case Studies.pptx new file mode 100644 index 00000000..e29b6ab3 Binary files /dev/null and b/Slides/Module 11.2 Case Studies.pptx differ diff --git a/Slides/Module 11.3 Communication Patterns.pdf b/Slides/Module 11.3 Communication Patterns.pdf new file mode 100644 index 00000000..18f2c5fd Binary files /dev/null and b/Slides/Module 11.3 Communication Patterns.pdf differ diff --git a/Slides/Module 11.3 Communication Patterns.pptx b/Slides/Module 11.3 Communication Patterns.pptx new file mode 100644 index 00000000..38ac625b Binary files /dev/null and b/Slides/Module 11.3 Communication Patterns.pptx differ diff --git a/Slides/Module 12 Testing Larger Things (Mitch's Version Spring 24).pdf b/Slides/Module 12 Testing Larger Things (Mitch's Version Spring 24).pdf new file mode 100644 index 00000000..201dfac3 Binary files /dev/null and b/Slides/Module 12 Testing Larger Things (Mitch's Version Spring 24).pdf differ diff --git a/Slides/Module 12 Testing Larger Things (Mitch's Version Spring 24).pptx b/Slides/Module 12 Testing Larger Things (Mitch's Version Spring 24).pptx new file mode 100644 index 00000000..47502b36 Binary files /dev/null and b/Slides/Module 12 Testing Larger Things (Mitch's Version Spring 24).pptx differ diff --git a/Slides/Module 12 Testing Larger Things.pdf b/Slides/Module 12 Testing Larger Things.pdf new file mode 100644 index 00000000..85b5b138 Binary files /dev/null and b/Slides/Module 12 Testing Larger Things.pdf differ diff --git a/Slides/Module 12 Testing Larger Things.pptx b/Slides/Module 12 Testing Larger Things.pptx new file mode 100644 index 00000000..877c471a Binary files /dev/null and b/Slides/Module 12 Testing Larger Things.pptx differ diff --git a/Slides/Module 13 Principles and Tech for Cloud.pdf b/Slides/Module 13 Principles and Tech for Cloud.pdf new file mode 100644 index 00000000..541e30d9 Binary files /dev/null and b/Slides/Module 13 Principles and Tech for Cloud.pdf differ diff --git a/Slides/Module 13 Principles and Tech for Cloud.pptx b/Slides/Module 13 Principles and Tech for Cloud.pptx new file mode 100644 index 00000000..fe9281d9 Binary files /dev/null and b/Slides/Module 13 Principles and Tech for Cloud.pptx differ diff --git a/Slides/Module 14 Continuous Development.pdf b/Slides/Module 14 Continuous Development.pdf new file mode 100644 index 00000000..e93774c1 Binary files /dev/null and b/Slides/Module 14 Continuous Development.pdf differ diff --git a/Slides/Module 14 Continuous Development.pptx b/Slides/Module 14 Continuous Development.pptx new file mode 100644 index 00000000..23b4e845 Binary files /dev/null and b/Slides/Module 14 Continuous Development.pptx differ diff --git a/Slides/git-placeholder.txt b/Slides/git-placeholder.txt new file mode 100644 index 00000000..2d6401c9 --- /dev/null +++ b/Slides/git-placeholder.txt @@ -0,0 +1 @@ +Placeholder file to force git to create a folder here. \ No newline at end of file diff --git a/activities/continuous-development.html b/activities/continuous-development.html new file mode 100644 index 00000000..17131ea3 --- /dev/null +++ b/activities/continuous-development.html @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +CI/CD for Covey.Town | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Continuous Development Pipelines for Covey.Town + + +

+ + +

In this activity, you will configure a continuous development pipeline using GitHub Actions, Heroku and Render.com. Our pipeline will use GitHub Actions to build and test your Covey.Town fork, and use Render.com to deploy the application.

+

+ + + Pre-requisites + + +

+ +

There are two pre-requisites for this activity:

+
    +
  1. Everyone in your team must have a GitHub.com account
  2. +
  3. At least one member of your team must have the GitHub Student Developer Pack - but we would suggest that you all have it, as in addition to providing Heroku credits, this package also provides free access to GitHub CoPilot. Student report success uploading a PDF of their enrollment snapshot/transcript to get the pack - a student ID doesn’t satisfy GitHub’s requirements (there must be a date).
  4. +
+ +

We have carefully crafted this activity around services that provide free trials. One limitation of note is that Heroku’s free trial appears to be a “once per lifetime” offer - if you have used a Heroku free trial in the past few years (since they switched from “free for everyone” to “only free for students”), you won’t be able to claim another free trial. However, only one member of your team needs to set up Heroku - so we hope that at least one of you will be able to use it. If you run into issues surrounding free trials etc., please reach out to the course staff.

+

+ + + Creating a GitHub Repository + + +

+ +

Your team’s development must take place within a private GitHub repository in our GitHub Classroom. To create your repository, each member of your team should follow these instructions:

+
    +
  1. Sign in to GitHub.com, and then use our invitation to create a repository with the covey.town codebase. You will need to click the “authorize” button next to neu-cs4530 and go through the NEU single sign on. Then you will see a list of groups. Check to see if one of your groupmates has created a group already - if so, select it to join it. Otherwise, create a team with the exact naming scheme “S24 Group XYY”, where X is your section number and YY is your group number. (Updated 2/28 to suggest prefixing with ‘S24’ to avoid collisions with other semesters’ groups).
  2. +
  3. Wait for an email to arrive from GitHub, inviting you to join the repository. This email will be sent to the email address that you used to sign up for GitHub. Click the link to join the repository.
  4. +
  5. The repository should now be created.
  6. +
  7. Clone the repository to your computer. Each of your team members should also follow these steps to join your group and clone the repository to their computer.
  8. +
+ +

This repository will be private, and visible only to your team and the course staff. After the semester ends, you are welcome to make it public - you have complete administrative control of the repository.

+ +

If you run into the error “refusing to allow an OAuth App to create or update workflow” when trying to push to GitHub, the fix is to update your saved authentication credentials for GitHub. For instance, you can follow these instructions to update your credentials in the MacOS Keychain. If all else fails, you can connect to GitHub with SSH instead of HTTPS, which will also solve this problem. This error seems to only occur when pushing a change to the GitHub Actions configuration file, so you could also side-step the problem by having a team mate push this change to GitHub instead (who may not run into this issue).

+

+ + + Creating a Twilio account and recording the credentials + + +

+ + +

You’ll need a Twilio account for your local development environment, and for deployment. You can share a single Twilio account with all of the members in your team (sharing the .env file), but please be careful to avoid posting the contents of your .env file publicly anywhere - someone else might find your credentials and run out all of the free minutes on your account. To set up GitHub Actions and Heroku, we’ll need the values from your .env file - so be sure to get them working before proceeding.

+ +

If you have not configured a Twilio account yet, follow these instructions to set one up. If you have done this before but forgot the API key or secret, you can simply follow the instructions to create a new API key.

+ +
    +
  1. Go to Twilio’s website and sign up for an account. You do not need to provide a credit card to create a trial account, but will need to provide a cell phone number. + a. When you create your account, to the question to “What do you plan to build…” answer “Other”, to “Which Twilio product are you here to use” answer “Video”, to “How do you want to build with Twilio?” select “With code”, to “Preferred Language” select JavaScript, and to “Would you like Twilio to host your code?” select “No”. (It is OK to select other answers, but selecting these will bring you directly to the “programmable video” part of Twilio’s admin console.
  2. +
  3. Create an API Key: Select the “Account” menu from the top toolbar, and then select “API keys & tokens”. If prompted, re-enter your password.
  4. +
  5. Click “Create API key”, the big blue button at the top right. Enter any descriptive name that you would like for the “friendly name”, and use the other default settings (US region, “standard” key type). Save the “SID” (this will be referenced later as the TWILIO_API_KEY_SID) and the “Secret” (this will be referenced later as the TWILIO_API_KEY_SECRET). Once you are sure that you have copy/pasted these for safe keeping, tick the “Got it!…” checkbox and click “Done”.
  6. +
  7. You should now be back to the “API keys & tokens” page. Take note of the “Live Credentials” in the box lower on this page. The “Account SID” and “Auth token” will be necessary for step 5.
  8. +
  9. Create a .env file in the townService directory, setting the values as follows:
  10. +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Config ValueDescription
TWILIO_ACCOUNT_SIDVisible on your twilio account dashboard. Starts with AC…
TWILIO_API_KEY_SIDThe SID of the new API key you created. Starts with SK…
TWILIO_API_KEY_SECRETThe secret for the API key you created.
TWILIO_API_AUTH_TOKENVisible on your twilio account dashboard.
+ +

You should now be able to start the backend and fronted in your local development environment and have functioning video chat.

+

+ + + Set up Heroku for the backend towns service + + +

+ +

We will configure Heroku to automatically deploy the backend townsService to Heroku on every push to the main branch. This way, you can have a publicly available version of your backend service, which will automatically update with any changes that you push to GitHub. +Please note that there are other PaaS providers that you could use for this step (e.g. Vercel, Render.com, Railway) - but we have found that only Heroku’s free product provides sufficient resources to actually run the backend service.

+ +
    +
  1. If you have not already, enroll for the Student Github developer pack and choose “Get student benefits”. Follow the steps as asked after doing this. It generally takes upto 2 business days or more to get the enrollment confirmation from Github.
  2. +
  3. Go to Heroku.com and create an account, or sign in if you already have one using the email id same as the one used for Github account.
  4. +
  5. Enroll in the Heroku for students offer, if you have not already. This would require you to have already enrolled in the Github student developer pack. You will need to complete 3 steps which involves adding a credit card for receiving the platform credits which then can be used for creating apps.
  6. +
  7. After receiving the confirmation of the enrollment in you mail box, the platform credits will be reflected in your billing section in your Heroku account in a few hours. Do not create an app before this.
  8. +
  9. After signing in, create a new app. Choose a name for your app that is somewhat descriptive - it will become part of the URL that you’ll use to access the service. Click “Create app” (no need to go through “Add to pipeline…”).
  10. +
  11. After creating your app on Heroku, open the app’s settings page, revealing a page that looks like this: +Heroku App Settings
  12. +
  13. Click “Reveal Config Vars”, and enter the 4 twilio configuration variables from your .env file (the same 4 that you set up on GitHub Actions). Heroku’s “Config Vars” are the way that we tell Heroku what variables to use for .env without having to commit that file into the (publicly viewable) repository. Your configuration settings on Heroku should look like this now: +Heroku App Settings
  14. +
  15. Before navigating away from this settings page, scroll down to “Domains”, and take note of the address that Heroku has provided for your app. This should say something like “Your app can be found at https://covey-town-sp24-a800bfec4826.herokuapp.com/”.
  16. +
  17. Click on the “Deploy” tab (in the same ribbon where “Settings” was), and click on “GitHub” next to “Deployment Method” - sign in if necessary
  18. +
  19. Search for your repository in the “Connect to GitHub” section, and click “Connect” next to it. You may need to authorize Heroku to access your GitHub account. Once you have connected your repository, you can click “Enable Automatic Deploys” to have Heroku automatically deploy your app every time you push to GitHub. We suggest ticking the box “Wait for CI to pass before deploy” - this will ensure that your app is only deployed if your tests pass.
  20. +
  21. Lastly, scroll down to the button that says “Deploy Branch” and click it. Watch the logs to see if your deployment is successful. If it is not, you may need to troubleshoot the logs to see what went wrong.
  22. +
  23. To confirm that your service is successfully deployed (after the build log shows that it completed successfully), try to visit it in your browser. Use the URL that you noted in step 8 (“Your app can be found at https://covey-town-sp24-a800bfec4826.herokuapp.com/”). Append towns to the URL, and visit it in your browser (e.g. https://covey-town-sp24-a800bfec4826.herokuapp.com/towns). After a short delay, you should see the response [].
  24. +
+

+ + + Set up Render.com for the frontend application + + +

+ +

The last step to our continuous development pipeline will be to automatically deploy our frontend to Render.com. Render.com will create an optimized production build of your frontend (by running npm run build) and host it in their globally-distributed content delivery network.

+ +
    +
  1. Go to Render.com and create an account that is linked to the same GitHub account that you used to create your fork of the starter code.
  2. +
  3. After signing in, create a new app by clicking the “new” button. Select “Static Site” as the type of the app.
  4. +
  5. Connect Render.com to your GitHub account, and give it permission to access your repositories.
  6. +
  7. Use the “Connect a repository” picker to select your fork of the starter code. Click next.
  8. +
  9. Configure the site: +
      +
    • Choose a name for your app - this will become part of the URL that you’ll use to access the application. You can use the default, or change it
    • +
    • Leave branch at main, and root directory blank
    • +
    • For build command, enter npm install && cd frontend && npm install && npm run prestart && npm run build && npm run export
    • +
    • For publish directory, enter frontend/out
    • +
    • In “Environment Variables” add a variable with the key NEXT_PUBLIC_TOWNS_SERVICE_URL, setting the value to be the URL of your townService (e.g. https://covey-town-sp24-a800bfec4826.herokuapp.com)
    • +
    • Click “Create static site” to create the site.
    • +
    +
  10. +
  11. Render.com will take several minutes to build your site. Once it is deployed, visit the site and confirm that it’s working. If you see an error in the JavaScript log like process is not defined, this indicates that the site was not built with the NEXT_PUBLIC_TOWNS_SERVICE_URL variable set - confirm that it is correctly set, and then rebuild the site on Render.com.
  12. +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/announcements/index.html b/announcements/index.html new file mode 100644 index 00000000..be56ca51 --- /dev/null +++ b/announcements/index.html @@ -0,0 +1,385 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Announcements | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Announcements + + +

+ + +
+

+ + + Team Repository Setup Now Available + + +

+ + + + Feb 26 + · + + + 0 min read + +
+

We have posted the instructions for setting up your team repository. You can find them in the start of the CI/CD Pipeline Activity. Please note that setting up the deployment to Heroku requires creating an account and waiting for it to be manually validated by Heroku. We recommend that you start this process as soon as possible to avoid any delays.

+ +
+
+ +
+

+ + + Software Development Process + + +

+ + + + Feb 14 + · + + + 1 min read + +
+

As part of software development process, each team is expected to use a structured development process, including pull requests, code reviews, etc. Each team will also submit sprint reports, individual surveys, and have weekly meetings with TA Mentor. The development process started last week (as you developed your project plan) and the required “process-related” submissions start this week.

+ +

Here are important submissions to remember for week 6, 8, 10-14:

+
    +
  • Every week all members of a team are expected to meet with their mentor for a regularly scheduled meeting. Weekly Canvas assignments will help track your attendance for these meetings.
  • +
  • Every week each student is expected to complete one (Confidential, Peer Evaluation) Survey. The link can be found under the Canvas assignment which will help track if you completed the survey or not.
  • +
  • At the end of each sprint, each team will submit a sprint report (and retrospective) through a Canvas assignment.
  • +
  • TAs will also review your github commits, PRs, code reviews regularly and will keep an eye on appropriate division of labor within the project team. They will also provide you a weekly assessment of your work.
  • +
+ +

These assignments count towards “project process” part of the project which is worth 20% of the overall project grade.

+ +
+
+ +
+

+ + + Individual Project Deliverable 2 is Now Available + IP1 Regrade Requests + Project Teams Announced + + +

+ + + + Feb 6 + · + + + 1 min read + +
+

The second and (final!) individual project deliverable is now released, and will be due on Wednesday, Feb 21 at 11am ET. We strongly encourage you to read through the assignment soon, so that you can begin to formulate a plan of attack to complete this assignment.

+ +

To request a regrade on IP1, please fill out a request form. The link is available on Notes and Videos page on Canvas

+ +

Project Team Assignments are now posted on piazza. Please coordinate with your assigned TA Mentors to schedule a kick-off meeting. The first team deliverable will be the preliminary project plan due on Feb 14th

+ + +
+
+ +
+

+ + + Team Formation Survey is Now Available + + +

+ + + + Jan 23 + · + + + 0 min read + +
+

Please complete this Team Formation Survey to help us organize you into a team for the term project. EVERY STUDENT must fill this form out by 11am on Wednesday 1/31/2024, or risk being placed in a random team.

+ + +
+
+ +
+

+ + + Welcome to CS4530 - Spring 2024 + + +

+ + + + Jan 8 + · + + + 1 min read + +
+

On behalf of the entire instructional team (all 3 instructors and 15 TAs), welcome to the Spring 2024 edition of CS 4530! This website will contain all of the lectures, assignments and tutorials for the class. Our Canvas site contains your gradebook as well as single-sign-on links to Piazza (for discussion), instructions for submitting assignments and replicates much of the material from the class website.

+ +

In advance of the first week of class, we’ve shared the calendars for each section, the first two lectures, and tutorials on installing your IDE, Typescript, and unit testing with Jest. The schedule of office hours is also posted and we will start holding office hours from Monday Jan 8th.

+ +

The first individual project deliverable is now available. This deliverable is due by January 24th at 11am ET. Your work will be submitted on Autograder.io which can be accessed through neu.autograder.io. Please note the autograding script will impose a strict rate limit of 5 submissions per 24 hours. This limit exists to encourage you to start early on this assignment. Feel free to use piazza to ask questions and use office hours if you need help. We have 75 hours of scheduled TA office hours per week starting today.

+ +

We are excited to meet you all this week!

+ + +
+
+ +
+

+ + + Initial Website Launched + + +

+ + + + Dec 18 + · + + + 0 min read + +
+

Welcome to the course website for CS4530, Spring 2024! Over the coming weeks, we will be populating this website (and Canvas) with more information about the course.

+ + +
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/assets/css/just-the-docs-dark.css b/assets/css/just-the-docs-dark.css new file mode 100644 index 00000000..e758d18c --- /dev/null +++ b/assets/css/just-the-docs-dark.css @@ -0,0 +1,6936 @@ +@charset "UTF-8"; +.highlight, +pre.highlight { + background: #f9f9f9; + color: #383942; } + +.highlight pre { + background: #f9f9f9; } + +.highlight .hll { + background: #f9f9f9; } + +.highlight .c { + color: #9fa0a6; + font-style: italic; } + +.highlight .err { + color: #fff; + background-color: #e05151; } + +.highlight .k { + color: #a625a4; } + +.highlight .l { + color: #50a04f; } + +.highlight .n { + color: #383942; } + +.highlight .o { + color: #383942; } + +.highlight .p { + color: #383942; } + +.highlight .cm { + color: #9fa0a6; + font-style: italic; } + +.highlight .cp { + color: #9fa0a6; + font-style: italic; } + +.highlight .c1 { + color: #9fa0a6; + font-style: italic; } + +.highlight .cs { + color: #9fa0a6; + font-style: italic; } + +.highlight .ge { + font-style: italic; } + +.highlight .gs { + font-weight: 700; } + +.highlight .kc { + color: #a625a4; } + +.highlight .kd { + color: #a625a4; } + +.highlight .kn { + color: #a625a4; } + +.highlight .kp { + color: #a625a4; } + +.highlight .kr { + color: #a625a4; } + +.highlight .kt { + color: #a625a4; } + +.highlight .ld { + color: #50a04f; } + +.highlight .m { + color: #b66a00; } + +.highlight .s { + color: #50a04f; } + +.highlight .na { + color: #b66a00; } + +.highlight .nb { + color: #ca7601; } + +.highlight .nc { + color: #ca7601; } + +.highlight .no { + color: #ca7601; } + +.highlight .nd { + color: #ca7601; } + +.highlight .ni { + color: #ca7601; } + +.highlight .ne { + color: #ca7601; } + +.highlight .nf { + color: #383942; } + +.highlight .nl { + color: #ca7601; } + +.highlight .nn { + color: #383942; } + +.highlight .nx { + color: #383942; } + +.highlight .py { + color: #ca7601; } + +.highlight .nt { + color: #e35549; } + +.highlight .nv { + color: #ca7601; } + +.highlight .ow { + font-weight: 700; } + +.highlight .w { + color: #f8f8f2; } + +.highlight .mf { + color: #b66a00; } + +.highlight .mh { + color: #b66a00; } + +.highlight .mi { + color: #b66a00; } + +.highlight .mo { + color: #b66a00; } + +.highlight .sb { + color: #50a04f; } + +.highlight .sc { + color: #50a04f; } + +.highlight .sd { + color: #50a04f; } + +.highlight .s2 { + color: #50a04f; } + +.highlight .se { + color: #50a04f; } + +.highlight .sh { + color: #50a04f; } + +.highlight .si { + color: #50a04f; } + +.highlight .sx { + color: #50a04f; } + +.highlight .sr { + color: #0083bb; } + +.highlight .s1 { + color: #50a04f; } + +.highlight .ss { + color: #0083bb; } + +.highlight .bp { + color: #ca7601; } + +.highlight .vc { + color: #ca7601; } + +.highlight .vg { + color: #ca7601; } + +.highlight .vi { + color: #e35549; } + +.highlight .il { + color: #b66a00; } + +.highlight .gu { + color: #75715e; } + +.highlight .gd { + color: #e05151; } + +.highlight .gi { + color: #43d089; } + +.highlight .language-json .w + .s2 { + color: #e35549; } + +.highlight .language-json .kc { + color: #0083bb; } + +.highlight, +pre.highlight { + background: #31343f; + color: #dee2f7; } + +.highlight pre { + background: #31343f; } + +.highlight .hll { + background: #31343f; } + +.highlight .c { + color: #63677e; + font-style: italic; } + +.highlight .err { + color: #960050; + background-color: #1e0010; } + +.highlight .k { + color: #e19ef5; } + +.highlight .l { + color: #a3eea0; } + +.highlight .n { + color: #dee2f7; } + +.highlight .o { + color: #dee2f7; } + +.highlight .p { + color: #dee2f7; } + +.highlight .cm { + color: #63677e; + font-style: italic; } + +.highlight .cp { + color: #63677e; + font-style: italic; } + +.highlight .c1 { + color: #63677e; + font-style: italic; } + +.highlight .cs { + color: #63677e; + font-style: italic; } + +.highlight .ge { + font-style: italic; } + +.highlight .gs { + font-weight: 700; } + +.highlight .kc { + color: #e19ef5; } + +.highlight .kd { + color: #e19ef5; } + +.highlight .kn { + color: #e19ef5; } + +.highlight .kp { + color: #e19ef5; } + +.highlight .kr { + color: #e19ef5; } + +.highlight .kt { + color: #e19ef5; } + +.highlight .ld { + color: #a3eea0; } + +.highlight .m { + color: #eddc96; } + +.highlight .s { + color: #a3eea0; } + +.highlight .na { + color: #eddc96; } + +.highlight .nb { + color: #fdce68; } + +.highlight .nc { + color: #fdce68; } + +.highlight .no { + color: #fdce68; } + +.highlight .nd { + color: #fdce68; } + +.highlight .ni { + color: #fdce68; } + +.highlight .ne { + color: #fdce68; } + +.highlight .nf { + color: #dee2f7; } + +.highlight .nl { + color: #fdce68; } + +.highlight .nn { + color: #dee2f7; } + +.highlight .nx { + color: #dee2f7; } + +.highlight .py { + color: #fdce68; } + +.highlight .nt { + color: #f9867b; } + +.highlight .nv { + color: #fdce68; } + +.highlight .ow { + font-weight: 700; } + +.highlight .w { + color: #f8f8f2; } + +.highlight .mf { + color: #eddc96; } + +.highlight .mh { + color: #eddc96; } + +.highlight .mi { + color: #eddc96; } + +.highlight .mo { + color: #eddc96; } + +.highlight .sb { + color: #a3eea0; } + +.highlight .sc { + color: #a3eea0; } + +.highlight .sd { + color: #a3eea0; } + +.highlight .s2 { + color: #a3eea0; } + +.highlight .se { + color: #a3eea0; } + +.highlight .sh { + color: #a3eea0; } + +.highlight .si { + color: #a3eea0; } + +.highlight .sx { + color: #a3eea0; } + +.highlight .sr { + color: #7be2f9; } + +.highlight .s1 { + color: #a3eea0; } + +.highlight .ss { + color: #7be2f9; } + +.highlight .bp { + color: #fdce68; } + +.highlight .vc { + color: #fdce68; } + +.highlight .vg { + color: #fdce68; } + +.highlight .vi { + color: #f9867b; } + +.highlight .il { + color: #eddc96; } + +.highlight .gu { + color: #75715e; } + +.highlight .gd { + color: #f92672; } + +.highlight .gi { + color: #a6e22e; } + +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +/* Document + ========================================================================== */ +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ +html { + line-height: 1.15; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ } + +/* Sections + ========================================================================== */ +/** + * Remove the margin in all browsers. + */ +body { + margin: 0; } + +/** + * Render the `main` element consistently in IE. + */ +main { + display: block; } + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ +h1 { + font-size: 2em; + margin: 0.67em 0; } + +/* Grouping content + ========================================================================== */ +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ +hr { + box-sizing: content-box; + /* 1 */ + height: 0; + /* 1 */ + overflow: visible; + /* 2 */ } + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ +pre { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ } + +/* Text-level semantics + ========================================================================== */ +/** + * Remove the gray background on active links in IE 10. + */ +a { + background-color: transparent; } + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ +abbr[title] { + border-bottom: none; + /* 1 */ + text-decoration: underline; + /* 2 */ + text-decoration: underline dotted; + /* 2 */ } + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ +b, +strong { + font-weight: bolder; } + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ +code, +kbd, +samp { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ } + +/** + * Add the correct font size in all browsers. + */ +small { + font-size: 80%; } + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } + +sub { + bottom: -0.25em; } + +sup { + top: -0.5em; } + +/* Embedded content + ========================================================================== */ +/** + * Remove the border on images inside links in IE 10. + */ +img { + border-style: none; } + +/* Forms + ========================================================================== */ +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: 1.15; + /* 1 */ + margin: 0; + /* 2 */ } + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ +button, +input { + /* 1 */ + overflow: visible; } + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ +button, +select { + /* 1 */ + text-transform: none; } + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; } + +/** + * Remove the inner border and padding in Firefox. + */ +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; } + +/** + * Restore the focus styles unset by the previous rule. + */ +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; } + +/** + * Correct the padding in Firefox. + */ +fieldset { + padding: 0.35em 0.75em 0.625em; } + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ +legend { + box-sizing: border-box; + /* 1 */ + color: inherit; + /* 2 */ + display: table; + /* 1 */ + max-width: 100%; + /* 1 */ + padding: 0; + /* 3 */ + white-space: normal; + /* 1 */ } + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ +progress { + vertical-align: baseline; } + +/** + * Remove the default vertical scrollbar in IE 10+. + */ +textarea { + overflow: auto; } + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ } + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; } + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ +[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ } + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ } + +/* Interactive + ========================================================================== */ +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ +details { + display: block; } + +/* + * Add the correct display in all browsers. + */ +summary { + display: list-item; } + +/* Misc + ========================================================================== */ +/** + * Add the correct display in IE 10+. + */ +template { + display: none; } + +/** + * Add the correct display in IE 10. + */ +[hidden] { + display: none; } + +:root { + color-scheme: dark; } + +* { + box-sizing: border-box; } + +html { + font-size: 0.875rem !important; + scroll-behavior: smooth; } + @media (min-width: 31.25rem) { + html { + font-size: 1rem !important; } } + +body { + font-family: system-ui, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, sans-serif, "Segoe UI Emoji"; + font-size: inherit; + line-height: 1.4; + color: #e6e1e8; + background-color: #27262b; + overflow-wrap: break-word; } + +ol, +ul, +dl, +pre, +address, +blockquote, +table, +div, +hr, +form, +fieldset, +noscript .table-wrapper { + margin-top: 0; } + +h1, +h2, +h3, +h4, +h5, +h6, +#toctitle { + margin-top: 0; + margin-bottom: 1em; + font-weight: 500; + line-height: 1.25; + color: #f5f6fa; } + +p { + margin-top: 1em; + margin-bottom: 1em; } + +a { + color: #2c84fa; + text-decoration: none; } + +a:not([class]) { + text-decoration: underline; + text-decoration-color: #44434d; + text-underline-offset: 2px; } + a:not([class]):hover { + text-decoration-color: rgba(44, 132, 250, 0.45); } + +code { + font-family: "SFMono-Regular", menlo, consolas, monospace; + font-size: 0.75em; + line-height: 1.4; } + +figure, +pre { + margin: 0; } + +li { + margin: 0.25em 0; } + +img { + max-width: 100%; + height: auto; } + +hr { + height: 1px; + padding: 0; + margin: 2rem 0; + background-color: #44434d; + border: 0; } + +blockquote { + margin: 10px 0; + margin-block-start: 0; + margin-inline-start: 0; + padding-left: 1rem; + border-left: 3px solid #44434d; } + +.side-bar { + z-index: 0; + display: flex; + flex-wrap: wrap; + background-color: #27262b; } + @media (min-width: 50rem) { + .side-bar { + flex-flow: column nowrap; + position: fixed; + width: 15.5rem; + height: 100%; + border-right: 1px solid #44434d; + align-items: flex-end; } } + @media (min-width: 66.5rem) { + .side-bar { + width: calc((100% - 66.5rem) / 2 + 16.5rem); + min-width: 16.5rem; } } + +@media (min-width: 50rem) { + .main { + position: relative; + max-width: 50rem; + margin-left: 15.5rem; } } +@media (min-width: 66.5rem) { + .main { + margin-left: Max(16.5rem, calc((100% - 66.5rem) / 2 + 16.5rem)); } } + +.main-content-wrap { + padding-right: 1rem; + padding-left: 1rem; + padding-top: 1rem; + padding-bottom: 1rem; } + @media (min-width: 50rem) { + .main-content-wrap { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 50rem) { + .main-content-wrap { + padding-top: 2rem; + padding-bottom: 2rem; } } + +.main-header { + z-index: 0; + display: none; + background-color: #27262b; } + @media (min-width: 50rem) { + .main-header { + display: flex; + justify-content: space-between; + height: 3.75rem; + background-color: #27262b; + border-bottom: 1px solid #44434d; } } + .main-header.nav-open { + display: block; } + @media (min-width: 50rem) { + .main-header.nav-open { + display: flex; } } + +.site-nav, +.site-header, +.site-footer { + width: 100%; } + @media (min-width: 66.5rem) { + .site-nav, + .site-header, + .site-footer { + width: 16.5rem; } } + +.site-nav { + display: none; } + .site-nav.nav-open { + display: block; } + @media (min-width: 50rem) { + .site-nav { + display: block; + padding-top: 3rem; + padding-bottom: 1rem; + overflow-y: auto; + flex: 1 1 auto; } } + +.site-header { + display: flex; + min-height: 3.75rem; + align-items: center; } + @media (min-width: 50rem) { + .site-header { + height: 3.75rem; + max-height: 3.75rem; + border-bottom: 1px solid #44434d; } } + +.site-title { + padding-right: 1rem; + padding-left: 1rem; + flex-grow: 1; + display: flex; + height: 100%; + align-items: center; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #f5f6fa; + font-size: 1.125rem !important; } + @media (min-width: 50rem) { + .site-title { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 31.25rem) { + .site-title { + font-size: 1.5rem !important; + line-height: 1.25; } } + @media (min-width: 50rem) { + .site-title { + padding-top: 0.5rem; + padding-bottom: 0.5rem; } } + +.site-button { + display: flex; + height: 100%; + padding: 1rem; + align-items: center; } + +@media (min-width: 50rem) { + .site-header .site-button { + display: none; } } +.site-title:hover { + background-image: linear-gradient(-90deg, #201f23 0%, rgba(32, 31, 35, 0.8) 80%, rgba(32, 31, 35, 0) 100%); } + +.site-button:hover { + background-image: linear-gradient(-90deg, #201f23 0%, rgba(32, 31, 35, 0.8) 100%); } + +body { + position: relative; + padding-bottom: 4rem; + overflow-y: scroll; } + @media (min-width: 50rem) { + body { + position: static; + padding-bottom: 0; } } + +.site-footer { + padding-right: 1rem; + padding-left: 1rem; + position: absolute; + bottom: 0; + left: 0; + padding-top: 1rem; + padding-bottom: 1rem; + color: #959396; + font-size: 0.6875rem !important; } + @media (min-width: 50rem) { + .site-footer { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 31.25rem) { + .site-footer { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) { + .site-footer { + position: static; + justify-self: end; } } + +.icon { + width: 1.5rem; + height: 1.5rem; + color: #2c84fa; } + +.main-content { + line-height: 1.6; } + .main-content ol, + .main-content ul, + .main-content dl, + .main-content pre, + .main-content address, + .main-content blockquote, + .main-content .table-wrapper { + margin-top: 0.5em; } + .main-content a { + overflow: hidden; + text-overflow: ellipsis; } + .main-content ul, + .main-content ol { + padding-left: 1.5em; } + .main-content li .highlight { + margin-top: 0.25rem; } + .main-content ol { + list-style-type: none; + counter-reset: step-counter; } + .main-content ol > li { + position: relative; } + .main-content ol > li::before { + position: absolute; + top: 0.2em; + left: -1.6em; + color: #959396; + content: counter(step-counter); + counter-increment: step-counter; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .main-content ol > li::before { + font-size: 0.875rem !important; } } + @media (min-width: 31.25rem) { + .main-content ol > li::before { + top: 0.11em; } } + .main-content ol > li ol { + counter-reset: sub-counter; } + .main-content ol > li ol > li::before { + content: counter(sub-counter,lower-alpha); + counter-increment: sub-counter; } + .main-content ul { + list-style: none; } + .main-content ul > li::before { + position: absolute; + margin-left: -1.4em; + color: #959396; + content: "•"; } + .main-content .task-list-item::before { + content: ""; } + .main-content .task-list-item-checkbox { + margin-right: 0.6em; + margin-left: -1.4em; } + .main-content hr + * { + margin-top: 0; } + .main-content h1:first-of-type { + margin-top: 0.5em; } + .main-content dl { + display: grid; + grid-template: auto / 10em 1fr; } + .main-content dt, + .main-content dd { + margin: 0.25em 0; } + .main-content dt { + grid-column: 1; + font-weight: 500; + text-align: right; } + .main-content dt::after { + content: ":"; } + .main-content dd { + grid-column: 2; + margin-bottom: 0; + margin-left: 1em; } + .main-content dd blockquote:first-child, + .main-content dd div:first-child, + .main-content dd dl:first-child, + .main-content dd dt:first-child, + .main-content dd h1:first-child, + .main-content dd h2:first-child, + .main-content dd h3:first-child, + .main-content dd h4:first-child, + .main-content dd h5:first-child, + .main-content dd h6:first-child, + .main-content dd li:first-child, + .main-content dd ol:first-child, + .main-content dd p:first-child, + .main-content dd pre:first-child, + .main-content dd table:first-child, + .main-content dd ul:first-child, + .main-content dd .table-wrapper:first-child { + margin-top: 0; } + .main-content dd dl:first-child dt:first-child, + .main-content dd dl:first-child dd:nth-child(2), + .main-content ol dl:first-child dt:first-child, + .main-content ol dl:first-child dd:nth-child(2), + .main-content ul dl:first-child dt:first-child, + .main-content ul dl:first-child dd:nth-child(2) { + margin-top: 0; } + .main-content .anchor-heading { + position: absolute; + right: -1rem; + width: 1.5rem; + height: 100%; + padding-right: 0.25rem; + padding-left: 0.25rem; + overflow: visible; } + @media (min-width: 50rem) { + .main-content .anchor-heading { + right: auto; + left: -1.5rem; } } + .main-content .anchor-heading svg { + display: inline-block; + width: 100%; + height: 100%; + color: #2c84fa; + visibility: hidden; } + .main-content .anchor-heading:hover svg, + .main-content .anchor-heading:focus svg, + .main-content h1:hover > .anchor-heading svg, + .main-content h2:hover > .anchor-heading svg, + .main-content h3:hover > .anchor-heading svg, + .main-content h4:hover > .anchor-heading svg, + .main-content h5:hover > .anchor-heading svg, + .main-content h6:hover > .anchor-heading svg { + visibility: visible; } + .main-content summary { + cursor: pointer; } + .main-content h1, + .main-content h2, + .main-content h3, + .main-content h4, + .main-content h5, + .main-content h6, + .main-content #toctitle { + position: relative; + margin-top: 1.5em; + margin-bottom: 0.25em; } + .main-content h1 + table, + .main-content h1 + .table-wrapper, + .main-content h1 + .code-example, + .main-content h1 + .highlighter-rouge, + .main-content h1 + .sectionbody .listingblock, + .main-content h2 + table, + .main-content h2 + .table-wrapper, + .main-content h2 + .code-example, + .main-content h2 + .highlighter-rouge, + .main-content h2 + .sectionbody .listingblock, + .main-content h3 + table, + .main-content h3 + .table-wrapper, + .main-content h3 + .code-example, + .main-content h3 + .highlighter-rouge, + .main-content h3 + .sectionbody .listingblock, + .main-content h4 + table, + .main-content h4 + .table-wrapper, + .main-content h4 + .code-example, + .main-content h4 + .highlighter-rouge, + .main-content h4 + .sectionbody .listingblock, + .main-content h5 + table, + .main-content h5 + .table-wrapper, + .main-content h5 + .code-example, + .main-content h5 + .highlighter-rouge, + .main-content h5 + .sectionbody .listingblock, + .main-content h6 + table, + .main-content h6 + .table-wrapper, + .main-content h6 + .code-example, + .main-content h6 + .highlighter-rouge, + .main-content h6 + .sectionbody .listingblock, + .main-content #toctitle + table, + .main-content #toctitle + .table-wrapper, + .main-content #toctitle + .code-example, + .main-content #toctitle + .highlighter-rouge, + .main-content #toctitle + .sectionbody .listingblock { + margin-top: 1em; } + .main-content h1 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h2 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h3 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h4 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h5 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h6 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content #toctitle + p:not(.label):not(div.highlighter-rouge[overlay]::after) { + margin-top: 0; } + .main-content > h1:first-child, + .main-content > h2:first-child, + .main-content > h3:first-child, + .main-content > h4:first-child, + .main-content > h5:first-child, + .main-content > h6:first-child, + .main-content > .sect1:first-child > h2, + .main-content > .sect2:first-child > h3, + .main-content > .sect3:first-child > h4, + .main-content > .sect4:first-child > h5, + .main-content > .sect5:first-child > h6 { + margin-top: 0.5rem; } + +.nav-list { + padding: 0; + margin-top: 0; + margin-bottom: 0; + list-style: none; } + .nav-list .nav-list-item { + font-size: 0.875rem !important; + position: relative; + margin: 0; } + @media (min-width: 31.25rem) { + .nav-list .nav-list-item { + font-size: 1rem !important; } } + @media (min-width: 50rem) { + .nav-list .nav-list-item { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .nav-list .nav-list-item { + font-size: 0.875rem !important; } } + + .nav-list .nav-list-item .nav-list-link { + display: block; + min-height: 3rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + line-height: 2.5rem; + padding-right: 3rem; + padding-left: 1rem; } + @media (min-width: 50rem) { + .nav-list .nav-list-item .nav-list-link { + min-height: 2rem; + line-height: 1.5rem; + padding-right: 2rem; + padding-left: 2rem; } } + .nav-list .nav-list-item .nav-list-link.external > svg { + width: 1rem; + height: 1rem; + vertical-align: text-bottom; } + .nav-list .nav-list-item .nav-list-link.active { + font-weight: 600; + text-decoration: none; } + .nav-list .nav-list-item .nav-list-link:hover, .nav-list .nav-list-item .nav-list-link.active { + background-image: linear-gradient(-90deg, #201f23 0%, rgba(32, 31, 35, 0.8) 80%, rgba(32, 31, 35, 0) 100%); } + .nav-list .nav-list-item .nav-list-expander { + position: absolute; + right: 0; + width: 3rem; + height: 3rem; + padding: 0.75rem; + color: #2c84fa; } + @media (min-width: 50rem) { + .nav-list .nav-list-item .nav-list-expander { + width: 2rem; + height: 2rem; + padding: 0.5rem; } } + .nav-list .nav-list-item .nav-list-expander:hover { + background-image: linear-gradient(-90deg, #201f23 0%, rgba(32, 31, 35, 0.8) 100%); } + .nav-list .nav-list-item .nav-list-expander svg { + transform: rotate(90deg); } + .nav-list .nav-list-item > .nav-list { + display: none; + padding-left: 0.75rem; + list-style: none; } + .nav-list .nav-list-item > .nav-list .nav-list-item { + position: relative; } + .nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-link { + color: #959396; } + .nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-expander { + color: #959396; } + .nav-list .nav-list-item.active > .nav-list-expander svg { + transform: rotate(-90deg); } + .nav-list .nav-list-item.active > .nav-list { + display: block; } + +.nav-category { + padding: 0.5rem 1rem; + font-weight: 600; + text-align: start; + text-transform: uppercase; + border-bottom: 1px solid #44434d; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .nav-category { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) { + .nav-category { + padding: 0.5rem 2rem; + margin-top: 1rem; + text-align: start; } + .nav-category:first-child { + margin-top: 0; } } + +.nav-list.nav-category-list > .nav-list-item { + margin: 0; } + .nav-list.nav-category-list > .nav-list-item > .nav-list { + padding: 0; } + .nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-link { + color: #2c84fa; } + .nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-expander { + color: #2c84fa; } + +.aux-nav { + height: 100%; + overflow-x: auto; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .aux-nav { + font-size: 0.75rem !important; } } + .aux-nav .aux-nav-list { + display: flex; + height: 100%; + padding: 0; + margin: 0; + list-style: none; } + .aux-nav .aux-nav-list-item { + display: inline-block; + height: 100%; + padding: 0; + margin: 0; } + @media (min-width: 50rem) { + .aux-nav { + padding-right: 1rem; } } + +@media (min-width: 50rem) { + .breadcrumb-nav { + margin-top: -1rem; } } + +.breadcrumb-nav-list { + padding-left: 0; + margin-bottom: 0.75rem; + list-style: none; } + +.breadcrumb-nav-list-item { + display: table-cell; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .breadcrumb-nav-list-item { + font-size: 0.75rem !important; } } + .breadcrumb-nav-list-item::before { + display: none; } + .breadcrumb-nav-list-item::after { + display: inline-block; + margin-right: 0.5rem; + margin-left: 0.5rem; + color: #959396; + content: "/"; } + .breadcrumb-nav-list-item:last-child::after { + content: ""; } + +h1, +.text-alpha { + font-size: 2rem !important; + line-height: 1.25; + font-weight: 300; } + @media (min-width: 31.25rem) { + h1, + .text-alpha { + font-size: 2.25rem !important; } } + +h2, +.text-beta, +#toctitle { + font-size: 1.125rem !important; } + @media (min-width: 31.25rem) { + h2, + .text-beta, + #toctitle { + font-size: 1.5rem !important; + line-height: 1.25; } } + +h3, +.text-gamma, +.announcement h1, +.announcement h2 { + font-size: 1rem !important; } + @media (min-width: 31.25rem) { + h3, + .text-gamma, + .announcement h1, + .announcement h2 { + font-size: 1.125rem !important; } } + +h4, +.text-delta { + font-size: 0.6875rem !important; + font-weight: 400; + text-transform: uppercase; + letter-spacing: 0.1em; } + @media (min-width: 31.25rem) { + h4, + .text-delta { + font-size: 0.75rem !important; } } + +h4 code { + text-transform: none; } + +h5, +.text-epsilon, +.announcement .announcement-meta { + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + h5, + .text-epsilon, + .announcement .announcement-meta { + font-size: 0.875rem !important; } } + +h6, +.text-zeta { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + h6, + .text-zeta { + font-size: 0.75rem !important; } } + +.text-small { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .text-small { + font-size: 0.75rem !important; } } + +.text-mono { + font-family: "SFMono-Regular", menlo, consolas, monospace !important; } + +.text-left { + text-align: left !important; } + +.text-center { + text-align: center !important; } + +.text-right { + text-align: right !important; } + +.label:not(g), .staffer .staffer-pronouns:not(g), div.highlighter-rouge[overlay]:not(g)::after, +.label-blue:not(g) { + display: inline-block; + padding: 0.16em 0.56em; + margin-right: 0.5rem; + margin-left: 0.5rem; + color: #fff; + text-transform: uppercase; + vertical-align: middle; + background-color: #2869e6; + font-size: 0.6875rem !important; + border-radius: 12px; } + @media (min-width: 31.25rem) { + .label:not(g), .staffer .staffer-pronouns:not(g), div.highlighter-rouge[overlay]:not(g)::after, + .label-blue:not(g) { + font-size: 0.75rem !important; } } + +.label-green:not(g) { + background-color: #009c7b; } + +.label-purple:not(g) { + background-color: #5e41d0; } + +.label-red:not(g) { + background-color: #e94c4c; } + +.label-yellow:not(g) { + color: #44434d; + background-color: #f7d12e; } + +.btn, summary { + display: inline-block; + box-sizing: border-box; + padding: 0.3em 1em; + margin: 0; + font-family: inherit; + font-size: inherit; + font-weight: 500; + line-height: 1.5; + color: #2c84fa; + text-decoration: none; + vertical-align: baseline; + cursor: pointer; + background-color: #302d36; + border-width: 0; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + appearance: none; } + .btn:focus, summary:focus { + text-decoration: none; + outline: none; + box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn:focus:hover, summary:focus:hover, .btn.selected:focus, summary.selected:focus { + box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn:hover, summary:hover, .btn.zeroclipboard-is-hover, summary.zeroclipboard-is-hover { + color: #227efa; } + .btn:hover, summary:hover, .btn:active, summary:active, .btn.zeroclipboard-is-hover, summary.zeroclipboard-is-hover, .btn.zeroclipboard-is-active, summary.zeroclipboard-is-active { + text-decoration: none; + background-color: #2e2b33; } + .btn:active, summary:active, .btn.selected, summary.selected, .btn.zeroclipboard-is-active, summary.zeroclipboard-is-active { + background-color: #29262e; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn.selected:hover, summary.selected:hover { + background-color: #cfcfcf; } + .btn:disabled, summary:disabled, .btn:disabled:hover, summary:disabled:hover, .btn.disabled, summary.disabled, .btn.disabled:hover, summary.disabled:hover { + color: rgba(102, 102, 102, 0.5); + cursor: default; + background-color: rgba(229, 229, 229, 0.5); + background-image: none; + box-shadow: none; } + +.btn-outline, summary { + color: #2c84fa; + background: transparent; + box-shadow: inset 0 0 0 2px #e6e1e8; } + .btn-outline:hover, summary:hover, .btn-outline:active, summary:active, .btn-outline.zeroclipboard-is-hover, summary.zeroclipboard-is-hover, .btn-outline.zeroclipboard-is-active, summary.zeroclipboard-is-active { + color: #1878fa; + text-decoration: none; + background-color: transparent; + box-shadow: inset 0 0 0 3px #e6e1e8; } + .btn-outline:focus, summary:focus { + text-decoration: none; + outline: none; + box-shadow: inset 0 0 0 2px #5c5962, 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn-outline:focus:hover, summary:focus:hover, .btn-outline.selected:focus, summary.selected:focus { + box-shadow: inset 0 0 0 2px #5c5962; } + +.btn-primary { + color: #fff; + background-color: #2448a7; + background-image: linear-gradient(#2b55c4, #2448a7); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-primary:hover, .btn-primary.zeroclipboard-is-hover { + color: #fff; + background-color: #22459e; + background-image: linear-gradient(#2850b7, #22459e); } + .btn-primary:active, .btn-primary.selected, .btn-primary.zeroclipboard-is-active { + background-color: #21439a; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-primary.selected:hover { + background-color: #1d3a85; } + +.btn-purple { + color: #fff; + background-color: #5739ce; + background-image: linear-gradient(#6f55d5, #5739ce); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-purple:hover, .btn-purple.zeroclipboard-is-hover { + color: #fff; + background-color: #5132cb; + background-image: linear-gradient(#6549d2, #5132cb); } + .btn-purple:active, .btn-purple.selected, .btn-purple.zeroclipboard-is-active { + background-color: #4f31c6; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-purple.selected:hover { + background-color: #472cb2; } + +.btn-blue { + color: #fff; + background-color: #227efa; + background-image: linear-gradient(#4593fb, #227efa); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-blue:hover, .btn-blue.zeroclipboard-is-hover { + color: #fff; + background-color: #1878fa; + background-image: linear-gradient(#368afa, #1878fa); } + .btn-blue:active, .btn-blue.selected, .btn-blue.zeroclipboard-is-active { + background-color: #1375f9; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-blue.selected:hover { + background-color: #0669ed; } + +.btn-green { + color: #fff; + background-color: #10ac7d; + background-image: linear-gradient(#13cc95, #10ac7d); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-green:hover, .btn-green.zeroclipboard-is-hover { + color: #fff; + background-color: #0fa276; + background-image: linear-gradient(#12be8b, #0fa276); } + .btn-green:active, .btn-green.selected, .btn-green.zeroclipboard-is-active { + background-color: #0f9e73; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-green.selected:hover { + background-color: #0d8662; } + +.btn-reset { + background: none; + border: none; + margin: 0; + text-align: inherit; + font: inherit; + border-radius: 0; + appearance: none; } + +.search { + position: relative; + z-index: 2; + flex-grow: 1; + height: 4rem; + padding: 0.5rem; + transition: padding linear 200ms; } + @media (min-width: 50rem) { + .search { + position: relative !important; + width: auto !important; + height: 100% !important; + padding: 0; + transition: none; } } + +.search-input-wrap { + position: relative; + z-index: 1; + height: 3rem; + overflow: hidden; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + transition: height linear 200ms; } + @media (min-width: 50rem) { + .search-input-wrap { + position: absolute; + width: 100%; + max-width: 33.5rem; + height: 100% !important; + border-radius: 0; + box-shadow: none; + transition: width ease 400ms; } } + +.search-input { + position: absolute; + width: 100%; + height: 100%; + padding: 0.5rem 1rem 0.5rem 2.5rem; + font-size: 1rem; + color: #e6e1e8; + background-color: #302d36; + border-top: 0; + border-right: 0; + border-bottom: 0; + border-left: 0; + border-radius: 0; } + @media (min-width: 50rem) { + .search-input { + padding: 0.5rem 1rem 0.5rem 3.5rem; + font-size: 0.875rem; + background-color: #27262b; + transition: padding-left linear 200ms; } } + .search-input:focus { + outline: 0; } + .search-input:focus + .search-label .search-icon { + color: #2c84fa; } + +.search-label { + position: absolute; + display: flex; + height: 100%; + padding-left: 1rem; } + @media (min-width: 50rem) { + .search-label { + padding-left: 2rem; + transition: padding-left linear 200ms; } } + .search-label .search-icon { + width: 1.2rem; + height: 1.2rem; + align-self: center; + color: #959396; } + +.search-results { + position: absolute; + left: 0; + display: none; + width: 100%; + max-height: calc(100% - 4rem); + overflow-y: auto; + background-color: #302d36; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } + @media (min-width: 50rem) { + .search-results { + top: 100%; + width: 33.5rem; + max-height: calc(100vh - 200%) !important; } } + +.search-results-list { + padding-left: 0; + margin-bottom: 0.25rem; + list-style: none; + font-size: 0.875rem !important; } + @media (min-width: 31.25rem) { + .search-results-list { + font-size: 1rem !important; } } + @media (min-width: 50rem) { + .search-results-list { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .search-results-list { + font-size: 0.875rem !important; } } + +.search-results-list-item { + padding: 0; + margin: 0; } + +.search-result { + display: block; + padding: 0.25rem 0.75rem; } + .search-result:hover, .search-result.active { + background-color: #201f23; } + +.search-result-title { + display: block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; } + @media (min-width: 31.25rem) { + .search-result-title { + display: inline-block; + width: 40%; + padding-right: 0.5rem; + vertical-align: top; } } + +.search-result-doc { + display: flex; + align-items: center; + word-wrap: break-word; } + .search-result-doc.search-result-doc-parent { + opacity: 0.5; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.875rem !important; } } + @media (min-width: 50rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.6875rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.75rem !important; } } + + .search-result-doc .search-result-icon { + width: 1rem; + height: 1rem; + margin-right: 0.5rem; + color: #2c84fa; + flex-shrink: 0; } + .search-result-doc .search-result-doc-title { + overflow: auto; } + +.search-result-section { + margin-left: 1.5rem; + word-wrap: break-word; } + +.search-result-rel-url { + display: block; + margin-left: 1.5rem; + overflow: hidden; + color: #959396; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.5625rem !important; } + @media (min-width: 31.25rem) { + .search-result-rel-url { + font-size: 0.625rem !important; } } + +.search-result-previews { + display: block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + margin-left: 0.5rem; + color: #959396; + word-wrap: break-word; + border-left: 1px solid; + border-left-color: #44434d; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .search-result-previews { + font-size: 0.75rem !important; } } + @media (min-width: 31.25rem) { + .search-result-previews { + display: inline-block; + width: 60%; + padding-left: 0.5rem; + margin-left: 0; + vertical-align: top; } } + +.search-result-preview + .search-result-preview { + margin-top: 0.25rem; } + +.search-result-highlight { + font-weight: bold; } + +.search-no-result { + padding: 0.5rem 0.75rem; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .search-no-result { + font-size: 0.875rem !important; } } + +.search-button { + position: fixed; + right: 1rem; + bottom: 1rem; + display: flex; + width: 3.5rem; + height: 3.5rem; + background-color: #302d36; + border: 1px solid rgba(44, 132, 250, 0.3); + border-radius: 1.75rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + align-items: center; + justify-content: center; } + +.search-overlay { + position: fixed; + top: 0; + left: 0; + z-index: 1; + width: 0; + height: 0; + background-color: rgba(0, 0, 0, 0.3); + opacity: 0; + transition: opacity ease 400ms, width 0s 400ms, height 0s 400ms; } + +.search-active .search { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 0; } +.search-active .search-input-wrap { + height: 4rem; + border-radius: 0; } + @media (min-width: 50rem) { + .search-active .search-input-wrap { + width: 33.5rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } } +.search-active .search-input { + background-color: #302d36; } + @media (min-width: 50rem) { + .search-active .search-input { + padding-left: 2.3rem; } } +@media (min-width: 50rem) { + .search-active .search-label { + padding-left: 0.6rem; } } +.search-active .search-results { + display: block; } +.search-active .search-overlay { + width: 100%; + height: 100%; + opacity: 1; + transition: opacity ease 400ms, width 0s, height 0s; } +@media (min-width: 50rem) { + .search-active .main { + position: fixed; + right: 0; + left: 0; } } +.search-active .main-header { + padding-top: 4rem; } + @media (min-width: 50rem) { + .search-active .main-header { + padding-top: 0; } } + +.table-wrapper { + display: block; + width: 100%; + max-width: 100%; + margin-bottom: 1.5rem; + overflow-x: auto; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } + +table { + display: table; + min-width: 100%; + border-collapse: separate; } + +th, +td { + font-size: 0.75rem !important; + min-width: 7.5rem; + padding: 0.5rem 0.75rem; + background-color: #302d36; + border-bottom: 1px solid rgba(68, 67, 77, 0.5); + border-left: 1px solid #44434d; } + @media (min-width: 31.25rem) { + th, + td { + font-size: 0.875rem !important; } } + th:first-of-type, + td:first-of-type { + border-left: 0; } + +tbody tr:last-of-type th, +tbody tr:last-of-type td { + border-bottom: 0; } +tbody tr:last-of-type td { + padding-bottom: 0.75rem; } + +thead th { + border-bottom: 1px solid #44434d; } + +:not(pre, figure) > code { + padding: 0.2em 0.15em; + font-weight: 400; + background-color: #31343f; + border: 1px solid #44434d; + border-radius: 4px; } + +a:visited code { + border-color: #44434d; } + +div.highlighter-rouge, +div.listingblock > div.content, +figure.highlight { + margin-top: 0; + margin-bottom: 0.75rem; + background-color: #31343f; + border-radius: 4px; + box-shadow: none; + -webkit-overflow-scrolling: touch; + position: relative; + padding: 0; } + div.highlighter-rouge > button, + div.listingblock > div.content > button, + figure.highlight > button { + width: 0.75rem; + opacity: 0; + position: absolute; + top: 0; + right: 0; + border: 0.75rem solid #31343f; + background-color: #31343f; + color: #e6e1e8; + box-sizing: content-box; } + div.highlighter-rouge > button svg, + div.listingblock > div.content > button svg, + figure.highlight > button svg { + fill: #e6e1e8; } + div.highlighter-rouge > button:active, + div.listingblock > div.content > button:active, + figure.highlight > button:active { + text-decoration: none; + outline: none; + opacity: 1; } + div.highlighter-rouge > button:focus, + div.listingblock > div.content > button:focus, + figure.highlight > button:focus { + opacity: 1; } + div.highlighter-rouge:hover > button, + div.listingblock > div.content:hover > button, + figure.highlight:hover > button { + cursor: copy; + opacity: 1; } + +div.highlighter-rouge div.highlight { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } +div.highlighter-rouge pre.highlight, +div.highlighter-rouge code { + padding: 0; + margin: 0; + border: 0; } + +div.listingblock { + margin-top: 0; + margin-bottom: 0.75rem; } + div.listingblock div.content { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } + div.listingblock div.content > pre, + div.listingblock code { + padding: 0; + margin: 0; + border: 0; } + +figure.highlight pre, +figure.highlight :not(pre) > code { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } + +.highlight .table-wrapper { + padding: 0.75rem 0; + margin: 0; + border: 0; + box-shadow: none; } + .highlight .table-wrapper td, + .highlight .table-wrapper pre { + font-size: 0.6875rem !important; + min-width: 0; + padding: 0; + background-color: #31343f; + border: 0; } + @media (min-width: 31.25rem) { + .highlight .table-wrapper td, + .highlight .table-wrapper pre { + font-size: 0.75rem !important; } } + .highlight .table-wrapper td.gl { + width: 1em; + padding-right: 0.75rem; + padding-left: 0.75rem; } + .highlight .table-wrapper pre { + margin: 0; + line-height: 2; } + +.code-example, +.listingblock > .title { + padding: 0.75rem; + margin-bottom: 0.75rem; + overflow: auto; + border: 1px solid #44434d; + border-radius: 4px; } + .code-example + .highlighter-rouge, + .code-example + .sectionbody .listingblock, + .code-example + .content, + .code-example + figure.highlight, + .listingblock > .title + .highlighter-rouge, + .listingblock > .title + .sectionbody .listingblock, + .listingblock > .title + .content, + .listingblock > .title + figure.highlight { + position: relative; + margin-top: -1rem; + border-right: 1px solid #44434d; + border-bottom: 1px solid #44434d; + border-left: 1px solid #44434d; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +code.language-mermaid { + padding: 0; + background-color: inherit; + border: 0; } + +.highlight, +pre.highlight { + background: #31343f; + color: #dee2f7; } + +.highlight pre { + background: #31343f; } + +.text-grey-dk-000, .staffer .staffer-meta { + color: #959396 !important; } + +.text-grey-dk-100, .staffer .staffer-pronouns, div.highlighter-rouge[overlay]::after { + color: #5c5962 !important; } + +.text-grey-dk-200 { + color: #44434d !important; } + +.text-grey-dk-250 { + color: #302d36 !important; } + +.text-grey-dk-300 { + color: #27262b !important; } + +.text-grey-lt-000 { + color: #f5f6fa !important; } + +.text-grey-lt-100 { + color: #eeebee !important; } + +.text-grey-lt-200 { + color: #ecebed !important; } + +.text-grey-lt-300 { + color: #e6e1e8 !important; } + +.text-blue-000 { + color: #2c84fa !important; } + +.text-blue-100 { + color: #2869e6 !important; } + +.text-blue-200 { + color: #264caf !important; } + +.text-blue-300 { + color: #183385 !important; } + +.text-green-000 { + color: #41d693 !important; } + +.text-green-100 { + color: #11b584 !important; } + +.text-green-200 { + color: #009c7b !important; } + +.text-green-300 { + color: #026e57 !important; } + +.text-purple-000 { + color: #7253ed !important; } + +.text-purple-100 { + color: #5e41d0 !important; } + +.text-purple-200 { + color: #4e26af !important; } + +.text-purple-300 { + color: #381885 !important; } + +.text-yellow-000 { + color: #ffeb82 !important; } + +.text-yellow-100 { + color: #fadf50 !important; } + +.text-yellow-200 { + color: #f7d12e !important; } + +.text-yellow-300 { + color: #e7af06 !important; } + +.text-red-000 { + color: #f77e7e !important; } + +.text-red-100 { + color: #f96e65 !important; } + +.text-red-200 { + color: #e94c4c !important; } + +.text-red-300 { + color: #dd2e2e !important; } + +.bg-grey-dk-000 { + background-color: #959396 !important; } + +.bg-grey-dk-100 { + background-color: #5c5962 !important; } + +.bg-grey-dk-200 { + background-color: #44434d !important; } + +.bg-grey-dk-250 { + background-color: #302d36 !important; } + +.bg-grey-dk-300 { + background-color: #27262b !important; } + +.bg-grey-lt-000 { + background-color: #f5f6fa !important; } + +.bg-grey-lt-100 { + background-color: #eeebee !important; } + +.bg-grey-lt-200, .staffer .staffer-pronouns { + background-color: #ecebed !important; } + +.bg-grey-lt-300 { + background-color: #e6e1e8 !important; } + +.bg-blue-000 { + background-color: #2c84fa !important; } + +.bg-blue-100 { + background-color: #2869e6 !important; } + +.bg-blue-200 { + background-color: #264caf !important; } + +.bg-blue-300 { + background-color: #183385 !important; } + +.bg-green-000 { + background-color: #41d693 !important; } + +.bg-green-100 { + background-color: #11b584 !important; } + +.bg-green-200 { + background-color: #009c7b !important; } + +.bg-green-300 { + background-color: #026e57 !important; } + +.bg-purple-000 { + background-color: #7253ed !important; } + +.bg-purple-100 { + background-color: #5e41d0 !important; } + +.bg-purple-200 { + background-color: #4e26af !important; } + +.bg-purple-300 { + background-color: #381885 !important; } + +.bg-yellow-000 { + background-color: #ffeb82 !important; } + +.bg-yellow-100 { + background-color: #fadf50 !important; } + +.bg-yellow-200 { + background-color: #f7d12e !important; } + +.bg-yellow-300 { + background-color: #e7af06 !important; } + +.bg-red-000 { + background-color: #f77e7e !important; } + +.bg-red-100 { + background-color: #f96e65 !important; } + +.bg-red-200 { + background-color: #e94c4c !important; } + +.bg-red-300 { + background-color: #dd2e2e !important; } + +.d-block { + display: block !important; } + +.d-flex { + display: flex !important; } + +.d-inline { + display: inline !important; } + +.d-inline-block { + display: inline-block !important; } + +.d-none { + display: none !important; } + +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +.float-left { + float: left !important; } + +.float-right { + float: right !important; } + +.flex-justify-start { + justify-content: flex-start !important; } + +.flex-justify-end { + justify-content: flex-end !important; } + +.flex-justify-between { + justify-content: space-between !important; } + +.flex-justify-around { + justify-content: space-around !important; } + +.v-align-baseline { + vertical-align: baseline !important; } + +.v-align-bottom { + vertical-align: bottom !important; } + +.v-align-middle { + vertical-align: middle !important; } + +.v-align-text-bottom { + vertical-align: text-bottom !important; } + +.v-align-text-top { + vertical-align: text-top !important; } + +.v-align-top { + vertical-align: top !important; } + +.fs-1 { + font-size: 0.5625rem !important; } + @media (min-width: 31.25rem) { + .fs-1 { + font-size: 0.625rem !important; } } + +.fs-2, .bldschedule .schedule-time, .bldschedule .schedule-event .time, +.bldschedule .schedule-event .location { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .fs-2, .bldschedule .schedule-time, .bldschedule .schedule-event .time, + .bldschedule .schedule-event .location { + font-size: 0.75rem !important; } } + +.fs-3, .bldschedule .schedule-event .name { + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .fs-3, .bldschedule .schedule-event .name { + font-size: 0.875rem !important; } } + +.fs-4 { + font-size: 0.875rem !important; } + @media (min-width: 31.25rem) { + .fs-4 { + font-size: 1rem !important; } } + +.fs-5 { + font-size: 1rem !important; } + @media (min-width: 31.25rem) { + .fs-5 { + font-size: 1.125rem !important; } } + +.fs-6 { + font-size: 1.125rem !important; } + @media (min-width: 31.25rem) { + .fs-6 { + font-size: 1.5rem !important; + line-height: 1.25; } } + +.fs-7 { + font-size: 1.5rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-7 { + font-size: 2rem !important; } } + +.fs-8 { + font-size: 2rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-8 { + font-size: 2.25rem !important; } } + +.fs-9 { + font-size: 2.25rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-9 { + font-size: 2.625rem !important; } } + +.fs-10 { + font-size: 2.625rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-10 { + font-size: 3rem !important; } } + +.fw-300 { + font-weight: 300 !important; } + +.fw-400 { + font-weight: 400 !important; } + +.fw-500 { + font-weight: 500 !important; } + +.fw-700, .bldschedule .schedule-event .name { + font-weight: 700 !important; } + +.lh-0 { + line-height: 0 !important; } + +.lh-default { + line-height: 1.4; } + +.lh-tight { + line-height: 1.25; } + +.ls-5 { + letter-spacing: 0.05em !important; } + +.ls-10 { + letter-spacing: 0.1em !important; } + +.ls-0 { + letter-spacing: 0 !important; } + +.text-uppercase { + text-transform: uppercase !important; } + +.list-style-none { + padding: 0 !important; + margin: 0 !important; + list-style: none !important; } + .list-style-none li::before { + display: none !important; } + +.mx-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-0 { + margin: 0 !important; } + +.mt-0 { + margin-top: 0 !important; } + +.mr-0 { + margin-right: 0 !important; } + +.mb-0 { + margin-bottom: 0 !important; } + +.ml-0 { + margin-left: 0 !important; } + +.mx-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + +.my-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + +.mxn-0 { + margin-right: -0 !important; + margin-left: -0 !important; } + +.mx-0-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-1 { + margin: 0.25rem !important; } + +.mt-1 { + margin-top: 0.25rem !important; } + +.mr-1 { + margin-right: 0.25rem !important; } + +.mb-1 { + margin-bottom: 0.25rem !important; } + +.ml-1 { + margin-left: 0.25rem !important; } + +.mx-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + +.my-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + +.mxn-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } + +.mx-1-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-2 { + margin: 0.5rem !important; } + +.mt-2 { + margin-top: 0.5rem !important; } + +.mr-2 { + margin-right: 0.5rem !important; } + +.mb-2 { + margin-bottom: 0.5rem !important; } + +.ml-2 { + margin-left: 0.5rem !important; } + +.mx-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + +.my-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + +.mxn-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } + +.mx-2-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-3 { + margin: 0.75rem !important; } + +.mt-3 { + margin-top: 0.75rem !important; } + +.mr-3 { + margin-right: 0.75rem !important; } + +.mb-3 { + margin-bottom: 0.75rem !important; } + +.ml-3 { + margin-left: 0.75rem !important; } + +.mx-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + +.my-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + +.mxn-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } + +.mx-3-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-4 { + margin: 1rem !important; } + +.mt-4 { + margin-top: 1rem !important; } + +.mr-4 { + margin-right: 1rem !important; } + +.mb-4 { + margin-bottom: 1rem !important; } + +.ml-4 { + margin-left: 1rem !important; } + +.mx-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + +.my-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + +.mxn-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } + +.mx-4-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-5 { + margin: 1.5rem !important; } + +.mt-5 { + margin-top: 1.5rem !important; } + +.mr-5 { + margin-right: 1.5rem !important; } + +.mb-5 { + margin-bottom: 1.5rem !important; } + +.ml-5 { + margin-left: 1.5rem !important; } + +.mx-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + +.my-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + +.mxn-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } + +.mx-5-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-6 { + margin: 2rem !important; } + +.mt-6 { + margin-top: 2rem !important; } + +.mr-6 { + margin-right: 2rem !important; } + +.mb-6 { + margin-bottom: 2rem !important; } + +.ml-6 { + margin-left: 2rem !important; } + +.mx-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + +.my-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + +.mxn-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } + +.mx-6-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-7 { + margin: 2.5rem !important; } + +.mt-7 { + margin-top: 2.5rem !important; } + +.mr-7 { + margin-right: 2.5rem !important; } + +.mb-7 { + margin-bottom: 2.5rem !important; } + +.ml-7 { + margin-left: 2.5rem !important; } + +.mx-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + +.my-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + +.mxn-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } + +.mx-7-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-8 { + margin: 3rem !important; } + +.mt-8 { + margin-top: 3rem !important; } + +.mr-8 { + margin-right: 3rem !important; } + +.mb-8 { + margin-bottom: 3rem !important; } + +.ml-8 { + margin-left: 3rem !important; } + +.mx-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + +.my-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + +.mxn-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } + +.mx-8-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-9 { + margin: 3.5rem !important; } + +.mt-9 { + margin-top: 3.5rem !important; } + +.mr-9 { + margin-right: 3.5rem !important; } + +.mb-9 { + margin-bottom: 3.5rem !important; } + +.ml-9 { + margin-left: 3.5rem !important; } + +.mx-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + +.my-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + +.mxn-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } + +.mx-9-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-10 { + margin: 4rem !important; } + +.mt-10 { + margin-top: 4rem !important; } + +.mr-10 { + margin-right: 4rem !important; } + +.mb-10 { + margin-bottom: 4rem !important; } + +.ml-10 { + margin-left: 4rem !important; } + +.mx-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + +.my-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + +.mxn-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } + +.mx-10-auto { + margin-right: auto !important; + margin-left: auto !important; } + +@media (min-width: 20rem) { + .m-xs-0 { + margin: 0 !important; } + + .mt-xs-0 { + margin-top: 0 !important; } + + .mr-xs-0 { + margin-right: 0 !important; } + + .mb-xs-0 { + margin-bottom: 0 !important; } + + .ml-xs-0 { + margin-left: 0 !important; } + + .mx-xs-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-xs-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-xs-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 20rem) { + .m-xs-1 { + margin: 0.25rem !important; } + + .mt-xs-1 { + margin-top: 0.25rem !important; } + + .mr-xs-1 { + margin-right: 0.25rem !important; } + + .mb-xs-1 { + margin-bottom: 0.25rem !important; } + + .ml-xs-1 { + margin-left: 0.25rem !important; } + + .mx-xs-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-xs-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-xs-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 20rem) { + .m-xs-2 { + margin: 0.5rem !important; } + + .mt-xs-2 { + margin-top: 0.5rem !important; } + + .mr-xs-2 { + margin-right: 0.5rem !important; } + + .mb-xs-2 { + margin-bottom: 0.5rem !important; } + + .ml-xs-2 { + margin-left: 0.5rem !important; } + + .mx-xs-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-xs-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-xs-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-3 { + margin: 0.75rem !important; } + + .mt-xs-3 { + margin-top: 0.75rem !important; } + + .mr-xs-3 { + margin-right: 0.75rem !important; } + + .mb-xs-3 { + margin-bottom: 0.75rem !important; } + + .ml-xs-3 { + margin-left: 0.75rem !important; } + + .mx-xs-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-xs-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-xs-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 20rem) { + .m-xs-4 { + margin: 1rem !important; } + + .mt-xs-4 { + margin-top: 1rem !important; } + + .mr-xs-4 { + margin-right: 1rem !important; } + + .mb-xs-4 { + margin-bottom: 1rem !important; } + + .ml-xs-4 { + margin-left: 1rem !important; } + + .mx-xs-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-xs-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-xs-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 20rem) { + .m-xs-5 { + margin: 1.5rem !important; } + + .mt-xs-5 { + margin-top: 1.5rem !important; } + + .mr-xs-5 { + margin-right: 1.5rem !important; } + + .mb-xs-5 { + margin-bottom: 1.5rem !important; } + + .ml-xs-5 { + margin-left: 1.5rem !important; } + + .mx-xs-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-xs-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-xs-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-6 { + margin: 2rem !important; } + + .mt-xs-6 { + margin-top: 2rem !important; } + + .mr-xs-6 { + margin-right: 2rem !important; } + + .mb-xs-6 { + margin-bottom: 2rem !important; } + + .ml-xs-6 { + margin-left: 2rem !important; } + + .mx-xs-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-xs-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-xs-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 20rem) { + .m-xs-7 { + margin: 2.5rem !important; } + + .mt-xs-7 { + margin-top: 2.5rem !important; } + + .mr-xs-7 { + margin-right: 2.5rem !important; } + + .mb-xs-7 { + margin-bottom: 2.5rem !important; } + + .ml-xs-7 { + margin-left: 2.5rem !important; } + + .mx-xs-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-xs-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-xs-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-8 { + margin: 3rem !important; } + + .mt-xs-8 { + margin-top: 3rem !important; } + + .mr-xs-8 { + margin-right: 3rem !important; } + + .mb-xs-8 { + margin-bottom: 3rem !important; } + + .ml-xs-8 { + margin-left: 3rem !important; } + + .mx-xs-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-xs-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-xs-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 20rem) { + .m-xs-9 { + margin: 3.5rem !important; } + + .mt-xs-9 { + margin-top: 3.5rem !important; } + + .mr-xs-9 { + margin-right: 3.5rem !important; } + + .mb-xs-9 { + margin-bottom: 3.5rem !important; } + + .ml-xs-9 { + margin-left: 3.5rem !important; } + + .mx-xs-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-xs-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-xs-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-10 { + margin: 4rem !important; } + + .mt-xs-10 { + margin-top: 4rem !important; } + + .mr-xs-10 { + margin-right: 4rem !important; } + + .mb-xs-10 { + margin-bottom: 4rem !important; } + + .ml-xs-10 { + margin-left: 4rem !important; } + + .mx-xs-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-xs-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-xs-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-0 { + margin: 0 !important; } + + .mt-sm-0 { + margin-top: 0 !important; } + + .mr-sm-0 { + margin-right: 0 !important; } + + .mb-sm-0 { + margin-bottom: 0 !important; } + + .ml-sm-0 { + margin-left: 0 !important; } + + .mx-sm-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-sm-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 31.25rem) { + .m-sm-1 { + margin: 0.25rem !important; } + + .mt-sm-1 { + margin-top: 0.25rem !important; } + + .mr-sm-1 { + margin-right: 0.25rem !important; } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; } + + .ml-sm-1 { + margin-left: 0.25rem !important; } + + .mx-sm-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-sm-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-2 { + margin: 0.5rem !important; } + + .mt-sm-2 { + margin-top: 0.5rem !important; } + + .mr-sm-2 { + margin-right: 0.5rem !important; } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; } + + .ml-sm-2 { + margin-left: 0.5rem !important; } + + .mx-sm-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-sm-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-3 { + margin: 0.75rem !important; } + + .mt-sm-3 { + margin-top: 0.75rem !important; } + + .mr-sm-3 { + margin-right: 0.75rem !important; } + + .mb-sm-3 { + margin-bottom: 0.75rem !important; } + + .ml-sm-3 { + margin-left: 0.75rem !important; } + + .mx-sm-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-sm-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-sm-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-4 { + margin: 1rem !important; } + + .mt-sm-4 { + margin-top: 1rem !important; } + + .mr-sm-4 { + margin-right: 1rem !important; } + + .mb-sm-4 { + margin-bottom: 1rem !important; } + + .ml-sm-4 { + margin-left: 1rem !important; } + + .mx-sm-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-sm-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-sm-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-5 { + margin: 1.5rem !important; } + + .mt-sm-5 { + margin-top: 1.5rem !important; } + + .mr-sm-5 { + margin-right: 1.5rem !important; } + + .mb-sm-5 { + margin-bottom: 1.5rem !important; } + + .ml-sm-5 { + margin-left: 1.5rem !important; } + + .mx-sm-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-sm-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-sm-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-6 { + margin: 2rem !important; } + + .mt-sm-6 { + margin-top: 2rem !important; } + + .mr-sm-6 { + margin-right: 2rem !important; } + + .mb-sm-6 { + margin-bottom: 2rem !important; } + + .ml-sm-6 { + margin-left: 2rem !important; } + + .mx-sm-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-sm-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-sm-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-7 { + margin: 2.5rem !important; } + + .mt-sm-7 { + margin-top: 2.5rem !important; } + + .mr-sm-7 { + margin-right: 2.5rem !important; } + + .mb-sm-7 { + margin-bottom: 2.5rem !important; } + + .ml-sm-7 { + margin-left: 2.5rem !important; } + + .mx-sm-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-sm-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-sm-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-8 { + margin: 3rem !important; } + + .mt-sm-8 { + margin-top: 3rem !important; } + + .mr-sm-8 { + margin-right: 3rem !important; } + + .mb-sm-8 { + margin-bottom: 3rem !important; } + + .ml-sm-8 { + margin-left: 3rem !important; } + + .mx-sm-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-sm-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-sm-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-9 { + margin: 3.5rem !important; } + + .mt-sm-9 { + margin-top: 3.5rem !important; } + + .mr-sm-9 { + margin-right: 3.5rem !important; } + + .mb-sm-9 { + margin-bottom: 3.5rem !important; } + + .ml-sm-9 { + margin-left: 3.5rem !important; } + + .mx-sm-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-sm-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-sm-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-10 { + margin: 4rem !important; } + + .mt-sm-10 { + margin-top: 4rem !important; } + + .mr-sm-10 { + margin-right: 4rem !important; } + + .mb-sm-10 { + margin-bottom: 4rem !important; } + + .ml-sm-10 { + margin-left: 4rem !important; } + + .mx-sm-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-sm-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-sm-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 50rem) { + .m-md-0 { + margin: 0 !important; } + + .mt-md-0 { + margin-top: 0 !important; } + + .mr-md-0 { + margin-right: 0 !important; } + + .mb-md-0 { + margin-bottom: 0 !important; } + + .ml-md-0 { + margin-left: 0 !important; } + + .mx-md-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-md-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 50rem) { + .m-md-1 { + margin: 0.25rem !important; } + + .mt-md-1 { + margin-top: 0.25rem !important; } + + .mr-md-1 { + margin-right: 0.25rem !important; } + + .mb-md-1 { + margin-bottom: 0.25rem !important; } + + .ml-md-1 { + margin-left: 0.25rem !important; } + + .mx-md-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-md-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 50rem) { + .m-md-2 { + margin: 0.5rem !important; } + + .mt-md-2 { + margin-top: 0.5rem !important; } + + .mr-md-2 { + margin-right: 0.5rem !important; } + + .mb-md-2 { + margin-bottom: 0.5rem !important; } + + .ml-md-2 { + margin-left: 0.5rem !important; } + + .mx-md-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-md-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 50rem) { + .m-md-3 { + margin: 0.75rem !important; } + + .mt-md-3 { + margin-top: 0.75rem !important; } + + .mr-md-3 { + margin-right: 0.75rem !important; } + + .mb-md-3 { + margin-bottom: 0.75rem !important; } + + .ml-md-3 { + margin-left: 0.75rem !important; } + + .mx-md-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-md-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-md-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 50rem) { + .m-md-4 { + margin: 1rem !important; } + + .mt-md-4 { + margin-top: 1rem !important; } + + .mr-md-4 { + margin-right: 1rem !important; } + + .mb-md-4 { + margin-bottom: 1rem !important; } + + .ml-md-4 { + margin-left: 1rem !important; } + + .mx-md-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-md-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-md-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 50rem) { + .m-md-5 { + margin: 1.5rem !important; } + + .mt-md-5 { + margin-top: 1.5rem !important; } + + .mr-md-5 { + margin-right: 1.5rem !important; } + + .mb-md-5 { + margin-bottom: 1.5rem !important; } + + .ml-md-5 { + margin-left: 1.5rem !important; } + + .mx-md-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-md-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-md-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 50rem) { + .m-md-6 { + margin: 2rem !important; } + + .mt-md-6 { + margin-top: 2rem !important; } + + .mr-md-6 { + margin-right: 2rem !important; } + + .mb-md-6 { + margin-bottom: 2rem !important; } + + .ml-md-6 { + margin-left: 2rem !important; } + + .mx-md-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-md-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-md-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 50rem) { + .m-md-7 { + margin: 2.5rem !important; } + + .mt-md-7 { + margin-top: 2.5rem !important; } + + .mr-md-7 { + margin-right: 2.5rem !important; } + + .mb-md-7 { + margin-bottom: 2.5rem !important; } + + .ml-md-7 { + margin-left: 2.5rem !important; } + + .mx-md-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-md-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-md-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 50rem) { + .m-md-8 { + margin: 3rem !important; } + + .mt-md-8 { + margin-top: 3rem !important; } + + .mr-md-8 { + margin-right: 3rem !important; } + + .mb-md-8 { + margin-bottom: 3rem !important; } + + .ml-md-8 { + margin-left: 3rem !important; } + + .mx-md-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-md-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-md-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 50rem) { + .m-md-9 { + margin: 3.5rem !important; } + + .mt-md-9 { + margin-top: 3.5rem !important; } + + .mr-md-9 { + margin-right: 3.5rem !important; } + + .mb-md-9 { + margin-bottom: 3.5rem !important; } + + .ml-md-9 { + margin-left: 3.5rem !important; } + + .mx-md-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-md-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-md-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 50rem) { + .m-md-10 { + margin: 4rem !important; } + + .mt-md-10 { + margin-top: 4rem !important; } + + .mr-md-10 { + margin-right: 4rem !important; } + + .mb-md-10 { + margin-bottom: 4rem !important; } + + .ml-md-10 { + margin-left: 4rem !important; } + + .mx-md-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-md-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-md-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-0 { + margin: 0 !important; } + + .mt-lg-0 { + margin-top: 0 !important; } + + .mr-lg-0 { + margin-right: 0 !important; } + + .mb-lg-0 { + margin-bottom: 0 !important; } + + .ml-lg-0 { + margin-left: 0 !important; } + + .mx-lg-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-lg-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 66.5rem) { + .m-lg-1 { + margin: 0.25rem !important; } + + .mt-lg-1 { + margin-top: 0.25rem !important; } + + .mr-lg-1 { + margin-right: 0.25rem !important; } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; } + + .ml-lg-1 { + margin-left: 0.25rem !important; } + + .mx-lg-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-lg-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-2 { + margin: 0.5rem !important; } + + .mt-lg-2 { + margin-top: 0.5rem !important; } + + .mr-lg-2 { + margin-right: 0.5rem !important; } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; } + + .ml-lg-2 { + margin-left: 0.5rem !important; } + + .mx-lg-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-lg-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-3 { + margin: 0.75rem !important; } + + .mt-lg-3 { + margin-top: 0.75rem !important; } + + .mr-lg-3 { + margin-right: 0.75rem !important; } + + .mb-lg-3 { + margin-bottom: 0.75rem !important; } + + .ml-lg-3 { + margin-left: 0.75rem !important; } + + .mx-lg-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-lg-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-lg-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-4 { + margin: 1rem !important; } + + .mt-lg-4 { + margin-top: 1rem !important; } + + .mr-lg-4 { + margin-right: 1rem !important; } + + .mb-lg-4 { + margin-bottom: 1rem !important; } + + .ml-lg-4 { + margin-left: 1rem !important; } + + .mx-lg-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-lg-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-lg-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-5 { + margin: 1.5rem !important; } + + .mt-lg-5 { + margin-top: 1.5rem !important; } + + .mr-lg-5 { + margin-right: 1.5rem !important; } + + .mb-lg-5 { + margin-bottom: 1.5rem !important; } + + .ml-lg-5 { + margin-left: 1.5rem !important; } + + .mx-lg-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-lg-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-lg-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-6 { + margin: 2rem !important; } + + .mt-lg-6 { + margin-top: 2rem !important; } + + .mr-lg-6 { + margin-right: 2rem !important; } + + .mb-lg-6 { + margin-bottom: 2rem !important; } + + .ml-lg-6 { + margin-left: 2rem !important; } + + .mx-lg-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-lg-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-lg-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-7 { + margin: 2.5rem !important; } + + .mt-lg-7 { + margin-top: 2.5rem !important; } + + .mr-lg-7 { + margin-right: 2.5rem !important; } + + .mb-lg-7 { + margin-bottom: 2.5rem !important; } + + .ml-lg-7 { + margin-left: 2.5rem !important; } + + .mx-lg-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-lg-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-lg-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-8 { + margin: 3rem !important; } + + .mt-lg-8 { + margin-top: 3rem !important; } + + .mr-lg-8 { + margin-right: 3rem !important; } + + .mb-lg-8 { + margin-bottom: 3rem !important; } + + .ml-lg-8 { + margin-left: 3rem !important; } + + .mx-lg-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-lg-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-lg-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-9 { + margin: 3.5rem !important; } + + .mt-lg-9 { + margin-top: 3.5rem !important; } + + .mr-lg-9 { + margin-right: 3.5rem !important; } + + .mb-lg-9 { + margin-bottom: 3.5rem !important; } + + .ml-lg-9 { + margin-left: 3.5rem !important; } + + .mx-lg-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-lg-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-lg-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-10 { + margin: 4rem !important; } + + .mt-lg-10 { + margin-top: 4rem !important; } + + .mr-lg-10 { + margin-right: 4rem !important; } + + .mb-lg-10 { + margin-bottom: 4rem !important; } + + .ml-lg-10 { + margin-left: 4rem !important; } + + .mx-lg-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-lg-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-lg-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-0 { + margin: 0 !important; } + + .mt-xl-0 { + margin-top: 0 !important; } + + .mr-xl-0 { + margin-right: 0 !important; } + + .mb-xl-0 { + margin-bottom: 0 !important; } + + .ml-xl-0 { + margin-left: 0 !important; } + + .mx-xl-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-xl-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 87.5rem) { + .m-xl-1 { + margin: 0.25rem !important; } + + .mt-xl-1 { + margin-top: 0.25rem !important; } + + .mr-xl-1 { + margin-right: 0.25rem !important; } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; } + + .ml-xl-1 { + margin-left: 0.25rem !important; } + + .mx-xl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-xl-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-2 { + margin: 0.5rem !important; } + + .mt-xl-2 { + margin-top: 0.5rem !important; } + + .mr-xl-2 { + margin-right: 0.5rem !important; } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; } + + .ml-xl-2 { + margin-left: 0.5rem !important; } + + .mx-xl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-xl-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-3 { + margin: 0.75rem !important; } + + .mt-xl-3 { + margin-top: 0.75rem !important; } + + .mr-xl-3 { + margin-right: 0.75rem !important; } + + .mb-xl-3 { + margin-bottom: 0.75rem !important; } + + .ml-xl-3 { + margin-left: 0.75rem !important; } + + .mx-xl-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-xl-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-xl-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-4 { + margin: 1rem !important; } + + .mt-xl-4 { + margin-top: 1rem !important; } + + .mr-xl-4 { + margin-right: 1rem !important; } + + .mb-xl-4 { + margin-bottom: 1rem !important; } + + .ml-xl-4 { + margin-left: 1rem !important; } + + .mx-xl-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-xl-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-xl-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-5 { + margin: 1.5rem !important; } + + .mt-xl-5 { + margin-top: 1.5rem !important; } + + .mr-xl-5 { + margin-right: 1.5rem !important; } + + .mb-xl-5 { + margin-bottom: 1.5rem !important; } + + .ml-xl-5 { + margin-left: 1.5rem !important; } + + .mx-xl-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-xl-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-xl-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-6 { + margin: 2rem !important; } + + .mt-xl-6 { + margin-top: 2rem !important; } + + .mr-xl-6 { + margin-right: 2rem !important; } + + .mb-xl-6 { + margin-bottom: 2rem !important; } + + .ml-xl-6 { + margin-left: 2rem !important; } + + .mx-xl-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-xl-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-xl-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-7 { + margin: 2.5rem !important; } + + .mt-xl-7 { + margin-top: 2.5rem !important; } + + .mr-xl-7 { + margin-right: 2.5rem !important; } + + .mb-xl-7 { + margin-bottom: 2.5rem !important; } + + .ml-xl-7 { + margin-left: 2.5rem !important; } + + .mx-xl-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-xl-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-xl-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-8 { + margin: 3rem !important; } + + .mt-xl-8 { + margin-top: 3rem !important; } + + .mr-xl-8 { + margin-right: 3rem !important; } + + .mb-xl-8 { + margin-bottom: 3rem !important; } + + .ml-xl-8 { + margin-left: 3rem !important; } + + .mx-xl-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-xl-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-xl-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-9 { + margin: 3.5rem !important; } + + .mt-xl-9 { + margin-top: 3.5rem !important; } + + .mr-xl-9 { + margin-right: 3.5rem !important; } + + .mb-xl-9 { + margin-bottom: 3.5rem !important; } + + .ml-xl-9 { + margin-left: 3.5rem !important; } + + .mx-xl-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-xl-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-xl-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-10 { + margin: 4rem !important; } + + .mt-xl-10 { + margin-top: 4rem !important; } + + .mr-xl-10 { + margin-right: 4rem !important; } + + .mb-xl-10 { + margin-bottom: 4rem !important; } + + .ml-xl-10 { + margin-left: 4rem !important; } + + .mx-xl-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-xl-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-xl-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +.p-0 { + padding: 0 !important; } + +.pt-0 { + padding-top: 0 !important; } + +.pr-0 { + padding-right: 0 !important; } + +.pb-0 { + padding-bottom: 0 !important; } + +.pl-0 { + padding-left: 0 !important; } + +.px-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + +.py-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + +.p-1 { + padding: 0.25rem !important; } + +.pt-1 { + padding-top: 0.25rem !important; } + +.pr-1 { + padding-right: 0.25rem !important; } + +.pb-1 { + padding-bottom: 0.25rem !important; } + +.pl-1 { + padding-left: 0.25rem !important; } + +.px-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + +.py-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + +.p-2 { + padding: 0.5rem !important; } + +.pt-2 { + padding-top: 0.5rem !important; } + +.pr-2 { + padding-right: 0.5rem !important; } + +.pb-2 { + padding-bottom: 0.5rem !important; } + +.pl-2 { + padding-left: 0.5rem !important; } + +.px-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + +.py-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + +.p-3 { + padding: 0.75rem !important; } + +.pt-3 { + padding-top: 0.75rem !important; } + +.pr-3 { + padding-right: 0.75rem !important; } + +.pb-3 { + padding-bottom: 0.75rem !important; } + +.pl-3 { + padding-left: 0.75rem !important; } + +.px-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + +.py-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + +.p-4 { + padding: 1rem !important; } + +.pt-4 { + padding-top: 1rem !important; } + +.pr-4 { + padding-right: 1rem !important; } + +.pb-4 { + padding-bottom: 1rem !important; } + +.pl-4 { + padding-left: 1rem !important; } + +.px-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + +.py-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + +.p-5 { + padding: 1.5rem !important; } + +.pt-5 { + padding-top: 1.5rem !important; } + +.pr-5 { + padding-right: 1.5rem !important; } + +.pb-5 { + padding-bottom: 1.5rem !important; } + +.pl-5 { + padding-left: 1.5rem !important; } + +.px-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + +.py-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + +.p-6 { + padding: 2rem !important; } + +.pt-6 { + padding-top: 2rem !important; } + +.pr-6 { + padding-right: 2rem !important; } + +.pb-6 { + padding-bottom: 2rem !important; } + +.pl-6 { + padding-left: 2rem !important; } + +.px-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + +.py-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + +.p-7 { + padding: 2.5rem !important; } + +.pt-7 { + padding-top: 2.5rem !important; } + +.pr-7 { + padding-right: 2.5rem !important; } + +.pb-7 { + padding-bottom: 2.5rem !important; } + +.pl-7 { + padding-left: 2.5rem !important; } + +.px-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + +.py-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + +.p-8 { + padding: 3rem !important; } + +.pt-8 { + padding-top: 3rem !important; } + +.pr-8 { + padding-right: 3rem !important; } + +.pb-8 { + padding-bottom: 3rem !important; } + +.pl-8 { + padding-left: 3rem !important; } + +.px-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + +.py-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + +.p-9 { + padding: 3.5rem !important; } + +.pt-9 { + padding-top: 3.5rem !important; } + +.pr-9 { + padding-right: 3.5rem !important; } + +.pb-9 { + padding-bottom: 3.5rem !important; } + +.pl-9 { + padding-left: 3.5rem !important; } + +.px-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + +.py-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + +.p-10 { + padding: 4rem !important; } + +.pt-10 { + padding-top: 4rem !important; } + +.pr-10 { + padding-right: 4rem !important; } + +.pb-10 { + padding-bottom: 4rem !important; } + +.pl-10 { + padding-left: 4rem !important; } + +.px-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + +.py-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } + +@media (min-width: 20rem) { + .p-xs-0 { + padding: 0 !important; } + + .pt-xs-0 { + padding-top: 0 !important; } + + .pr-xs-0 { + padding-right: 0 !important; } + + .pb-xs-0 { + padding-bottom: 0 !important; } + + .pl-xs-0 { + padding-left: 0 !important; } + + .px-xs-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-xs-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-xs-1 { + padding: 0.25rem !important; } + + .pt-xs-1 { + padding-top: 0.25rem !important; } + + .pr-xs-1 { + padding-right: 0.25rem !important; } + + .pb-xs-1 { + padding-bottom: 0.25rem !important; } + + .pl-xs-1 { + padding-left: 0.25rem !important; } + + .px-xs-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-xs-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-xs-2 { + padding: 0.5rem !important; } + + .pt-xs-2 { + padding-top: 0.5rem !important; } + + .pr-xs-2 { + padding-right: 0.5rem !important; } + + .pb-xs-2 { + padding-bottom: 0.5rem !important; } + + .pl-xs-2 { + padding-left: 0.5rem !important; } + + .px-xs-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-xs-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-xs-3 { + padding: 0.75rem !important; } + + .pt-xs-3 { + padding-top: 0.75rem !important; } + + .pr-xs-3 { + padding-right: 0.75rem !important; } + + .pb-xs-3 { + padding-bottom: 0.75rem !important; } + + .pl-xs-3 { + padding-left: 0.75rem !important; } + + .px-xs-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-xs-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-xs-4 { + padding: 1rem !important; } + + .pt-xs-4 { + padding-top: 1rem !important; } + + .pr-xs-4 { + padding-right: 1rem !important; } + + .pb-xs-4 { + padding-bottom: 1rem !important; } + + .pl-xs-4 { + padding-left: 1rem !important; } + + .px-xs-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-xs-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-xs-5 { + padding: 1.5rem !important; } + + .pt-xs-5 { + padding-top: 1.5rem !important; } + + .pr-xs-5 { + padding-right: 1.5rem !important; } + + .pb-xs-5 { + padding-bottom: 1.5rem !important; } + + .pl-xs-5 { + padding-left: 1.5rem !important; } + + .px-xs-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-xs-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-xs-6 { + padding: 2rem !important; } + + .pt-xs-6 { + padding-top: 2rem !important; } + + .pr-xs-6 { + padding-right: 2rem !important; } + + .pb-xs-6 { + padding-bottom: 2rem !important; } + + .pl-xs-6 { + padding-left: 2rem !important; } + + .px-xs-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-xs-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-xs-7 { + padding: 2.5rem !important; } + + .pt-xs-7 { + padding-top: 2.5rem !important; } + + .pr-xs-7 { + padding-right: 2.5rem !important; } + + .pb-xs-7 { + padding-bottom: 2.5rem !important; } + + .pl-xs-7 { + padding-left: 2.5rem !important; } + + .px-xs-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-xs-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-xs-8 { + padding: 3rem !important; } + + .pt-xs-8 { + padding-top: 3rem !important; } + + .pr-xs-8 { + padding-right: 3rem !important; } + + .pb-xs-8 { + padding-bottom: 3rem !important; } + + .pl-xs-8 { + padding-left: 3rem !important; } + + .px-xs-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-xs-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-xs-9 { + padding: 3.5rem !important; } + + .pt-xs-9 { + padding-top: 3.5rem !important; } + + .pr-xs-9 { + padding-right: 3.5rem !important; } + + .pb-xs-9 { + padding-bottom: 3.5rem !important; } + + .pl-xs-9 { + padding-left: 3.5rem !important; } + + .px-xs-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-xs-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-xs-10 { + padding: 4rem !important; } + + .pt-xs-10 { + padding-top: 4rem !important; } + + .pr-xs-10 { + padding-right: 4rem !important; } + + .pb-xs-10 { + padding-bottom: 4rem !important; } + + .pl-xs-10 { + padding-left: 4rem !important; } + + .px-xs-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-xs-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 31.25rem) { + .p-sm-0 { + padding: 0 !important; } + + .pt-sm-0 { + padding-top: 0 !important; } + + .pr-sm-0 { + padding-right: 0 !important; } + + .pb-sm-0 { + padding-bottom: 0 !important; } + + .pl-sm-0 { + padding-left: 0 !important; } + + .px-sm-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-sm-1 { + padding: 0.25rem !important; } + + .pt-sm-1 { + padding-top: 0.25rem !important; } + + .pr-sm-1 { + padding-right: 0.25rem !important; } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; } + + .pl-sm-1 { + padding-left: 0.25rem !important; } + + .px-sm-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-sm-2 { + padding: 0.5rem !important; } + + .pt-sm-2 { + padding-top: 0.5rem !important; } + + .pr-sm-2 { + padding-right: 0.5rem !important; } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; } + + .pl-sm-2 { + padding-left: 0.5rem !important; } + + .px-sm-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-sm-3 { + padding: 0.75rem !important; } + + .pt-sm-3 { + padding-top: 0.75rem !important; } + + .pr-sm-3 { + padding-right: 0.75rem !important; } + + .pb-sm-3 { + padding-bottom: 0.75rem !important; } + + .pl-sm-3 { + padding-left: 0.75rem !important; } + + .px-sm-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-sm-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-sm-4 { + padding: 1rem !important; } + + .pt-sm-4 { + padding-top: 1rem !important; } + + .pr-sm-4 { + padding-right: 1rem !important; } + + .pb-sm-4 { + padding-bottom: 1rem !important; } + + .pl-sm-4 { + padding-left: 1rem !important; } + + .px-sm-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-sm-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-sm-5 { + padding: 1.5rem !important; } + + .pt-sm-5 { + padding-top: 1.5rem !important; } + + .pr-sm-5 { + padding-right: 1.5rem !important; } + + .pb-sm-5 { + padding-bottom: 1.5rem !important; } + + .pl-sm-5 { + padding-left: 1.5rem !important; } + + .px-sm-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-sm-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-sm-6 { + padding: 2rem !important; } + + .pt-sm-6 { + padding-top: 2rem !important; } + + .pr-sm-6 { + padding-right: 2rem !important; } + + .pb-sm-6 { + padding-bottom: 2rem !important; } + + .pl-sm-6 { + padding-left: 2rem !important; } + + .px-sm-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-sm-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-sm-7 { + padding: 2.5rem !important; } + + .pt-sm-7 { + padding-top: 2.5rem !important; } + + .pr-sm-7 { + padding-right: 2.5rem !important; } + + .pb-sm-7 { + padding-bottom: 2.5rem !important; } + + .pl-sm-7 { + padding-left: 2.5rem !important; } + + .px-sm-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-sm-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-sm-8 { + padding: 3rem !important; } + + .pt-sm-8 { + padding-top: 3rem !important; } + + .pr-sm-8 { + padding-right: 3rem !important; } + + .pb-sm-8 { + padding-bottom: 3rem !important; } + + .pl-sm-8 { + padding-left: 3rem !important; } + + .px-sm-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-sm-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-sm-9 { + padding: 3.5rem !important; } + + .pt-sm-9 { + padding-top: 3.5rem !important; } + + .pr-sm-9 { + padding-right: 3.5rem !important; } + + .pb-sm-9 { + padding-bottom: 3.5rem !important; } + + .pl-sm-9 { + padding-left: 3.5rem !important; } + + .px-sm-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-sm-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-sm-10 { + padding: 4rem !important; } + + .pt-sm-10 { + padding-top: 4rem !important; } + + .pr-sm-10 { + padding-right: 4rem !important; } + + .pb-sm-10 { + padding-bottom: 4rem !important; } + + .pl-sm-10 { + padding-left: 4rem !important; } + + .px-sm-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-sm-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 50rem) { + .p-md-0 { + padding: 0 !important; } + + .pt-md-0 { + padding-top: 0 !important; } + + .pr-md-0 { + padding-right: 0 !important; } + + .pb-md-0 { + padding-bottom: 0 !important; } + + .pl-md-0 { + padding-left: 0 !important; } + + .px-md-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-md-1 { + padding: 0.25rem !important; } + + .pt-md-1 { + padding-top: 0.25rem !important; } + + .pr-md-1 { + padding-right: 0.25rem !important; } + + .pb-md-1 { + padding-bottom: 0.25rem !important; } + + .pl-md-1 { + padding-left: 0.25rem !important; } + + .px-md-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-md-2 { + padding: 0.5rem !important; } + + .pt-md-2 { + padding-top: 0.5rem !important; } + + .pr-md-2 { + padding-right: 0.5rem !important; } + + .pb-md-2 { + padding-bottom: 0.5rem !important; } + + .pl-md-2 { + padding-left: 0.5rem !important; } + + .px-md-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-md-3 { + padding: 0.75rem !important; } + + .pt-md-3 { + padding-top: 0.75rem !important; } + + .pr-md-3 { + padding-right: 0.75rem !important; } + + .pb-md-3 { + padding-bottom: 0.75rem !important; } + + .pl-md-3 { + padding-left: 0.75rem !important; } + + .px-md-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-md-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-md-4 { + padding: 1rem !important; } + + .pt-md-4 { + padding-top: 1rem !important; } + + .pr-md-4 { + padding-right: 1rem !important; } + + .pb-md-4 { + padding-bottom: 1rem !important; } + + .pl-md-4 { + padding-left: 1rem !important; } + + .px-md-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-md-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-md-5 { + padding: 1.5rem !important; } + + .pt-md-5 { + padding-top: 1.5rem !important; } + + .pr-md-5 { + padding-right: 1.5rem !important; } + + .pb-md-5 { + padding-bottom: 1.5rem !important; } + + .pl-md-5 { + padding-left: 1.5rem !important; } + + .px-md-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-md-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-md-6 { + padding: 2rem !important; } + + .pt-md-6 { + padding-top: 2rem !important; } + + .pr-md-6 { + padding-right: 2rem !important; } + + .pb-md-6 { + padding-bottom: 2rem !important; } + + .pl-md-6 { + padding-left: 2rem !important; } + + .px-md-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-md-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-md-7 { + padding: 2.5rem !important; } + + .pt-md-7 { + padding-top: 2.5rem !important; } + + .pr-md-7 { + padding-right: 2.5rem !important; } + + .pb-md-7 { + padding-bottom: 2.5rem !important; } + + .pl-md-7 { + padding-left: 2.5rem !important; } + + .px-md-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-md-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-md-8 { + padding: 3rem !important; } + + .pt-md-8 { + padding-top: 3rem !important; } + + .pr-md-8 { + padding-right: 3rem !important; } + + .pb-md-8 { + padding-bottom: 3rem !important; } + + .pl-md-8 { + padding-left: 3rem !important; } + + .px-md-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-md-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-md-9 { + padding: 3.5rem !important; } + + .pt-md-9 { + padding-top: 3.5rem !important; } + + .pr-md-9 { + padding-right: 3.5rem !important; } + + .pb-md-9 { + padding-bottom: 3.5rem !important; } + + .pl-md-9 { + padding-left: 3.5rem !important; } + + .px-md-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-md-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-md-10 { + padding: 4rem !important; } + + .pt-md-10 { + padding-top: 4rem !important; } + + .pr-md-10 { + padding-right: 4rem !important; } + + .pb-md-10 { + padding-bottom: 4rem !important; } + + .pl-md-10 { + padding-left: 4rem !important; } + + .px-md-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-md-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 66.5rem) { + .p-lg-0 { + padding: 0 !important; } + + .pt-lg-0 { + padding-top: 0 !important; } + + .pr-lg-0 { + padding-right: 0 !important; } + + .pb-lg-0 { + padding-bottom: 0 !important; } + + .pl-lg-0 { + padding-left: 0 !important; } + + .px-lg-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-lg-1 { + padding: 0.25rem !important; } + + .pt-lg-1 { + padding-top: 0.25rem !important; } + + .pr-lg-1 { + padding-right: 0.25rem !important; } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; } + + .pl-lg-1 { + padding-left: 0.25rem !important; } + + .px-lg-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-lg-2 { + padding: 0.5rem !important; } + + .pt-lg-2 { + padding-top: 0.5rem !important; } + + .pr-lg-2 { + padding-right: 0.5rem !important; } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; } + + .pl-lg-2 { + padding-left: 0.5rem !important; } + + .px-lg-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-lg-3 { + padding: 0.75rem !important; } + + .pt-lg-3 { + padding-top: 0.75rem !important; } + + .pr-lg-3 { + padding-right: 0.75rem !important; } + + .pb-lg-3 { + padding-bottom: 0.75rem !important; } + + .pl-lg-3 { + padding-left: 0.75rem !important; } + + .px-lg-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-lg-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-lg-4 { + padding: 1rem !important; } + + .pt-lg-4 { + padding-top: 1rem !important; } + + .pr-lg-4 { + padding-right: 1rem !important; } + + .pb-lg-4 { + padding-bottom: 1rem !important; } + + .pl-lg-4 { + padding-left: 1rem !important; } + + .px-lg-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-lg-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-lg-5 { + padding: 1.5rem !important; } + + .pt-lg-5 { + padding-top: 1.5rem !important; } + + .pr-lg-5 { + padding-right: 1.5rem !important; } + + .pb-lg-5 { + padding-bottom: 1.5rem !important; } + + .pl-lg-5 { + padding-left: 1.5rem !important; } + + .px-lg-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-lg-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-lg-6 { + padding: 2rem !important; } + + .pt-lg-6 { + padding-top: 2rem !important; } + + .pr-lg-6 { + padding-right: 2rem !important; } + + .pb-lg-6 { + padding-bottom: 2rem !important; } + + .pl-lg-6 { + padding-left: 2rem !important; } + + .px-lg-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-lg-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-lg-7 { + padding: 2.5rem !important; } + + .pt-lg-7 { + padding-top: 2.5rem !important; } + + .pr-lg-7 { + padding-right: 2.5rem !important; } + + .pb-lg-7 { + padding-bottom: 2.5rem !important; } + + .pl-lg-7 { + padding-left: 2.5rem !important; } + + .px-lg-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-lg-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-lg-8 { + padding: 3rem !important; } + + .pt-lg-8 { + padding-top: 3rem !important; } + + .pr-lg-8 { + padding-right: 3rem !important; } + + .pb-lg-8 { + padding-bottom: 3rem !important; } + + .pl-lg-8 { + padding-left: 3rem !important; } + + .px-lg-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-lg-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-lg-9 { + padding: 3.5rem !important; } + + .pt-lg-9 { + padding-top: 3.5rem !important; } + + .pr-lg-9 { + padding-right: 3.5rem !important; } + + .pb-lg-9 { + padding-bottom: 3.5rem !important; } + + .pl-lg-9 { + padding-left: 3.5rem !important; } + + .px-lg-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-lg-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-lg-10 { + padding: 4rem !important; } + + .pt-lg-10 { + padding-top: 4rem !important; } + + .pr-lg-10 { + padding-right: 4rem !important; } + + .pb-lg-10 { + padding-bottom: 4rem !important; } + + .pl-lg-10 { + padding-left: 4rem !important; } + + .px-lg-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-lg-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 87.5rem) { + .p-xl-0 { + padding: 0 !important; } + + .pt-xl-0 { + padding-top: 0 !important; } + + .pr-xl-0 { + padding-right: 0 !important; } + + .pb-xl-0 { + padding-bottom: 0 !important; } + + .pl-xl-0 { + padding-left: 0 !important; } + + .px-xl-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-xl-1 { + padding: 0.25rem !important; } + + .pt-xl-1 { + padding-top: 0.25rem !important; } + + .pr-xl-1 { + padding-right: 0.25rem !important; } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; } + + .pl-xl-1 { + padding-left: 0.25rem !important; } + + .px-xl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-xl-2 { + padding: 0.5rem !important; } + + .pt-xl-2 { + padding-top: 0.5rem !important; } + + .pr-xl-2 { + padding-right: 0.5rem !important; } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; } + + .pl-xl-2 { + padding-left: 0.5rem !important; } + + .px-xl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-xl-3 { + padding: 0.75rem !important; } + + .pt-xl-3 { + padding-top: 0.75rem !important; } + + .pr-xl-3 { + padding-right: 0.75rem !important; } + + .pb-xl-3 { + padding-bottom: 0.75rem !important; } + + .pl-xl-3 { + padding-left: 0.75rem !important; } + + .px-xl-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-xl-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-xl-4 { + padding: 1rem !important; } + + .pt-xl-4 { + padding-top: 1rem !important; } + + .pr-xl-4 { + padding-right: 1rem !important; } + + .pb-xl-4 { + padding-bottom: 1rem !important; } + + .pl-xl-4 { + padding-left: 1rem !important; } + + .px-xl-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-xl-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-xl-5 { + padding: 1.5rem !important; } + + .pt-xl-5 { + padding-top: 1.5rem !important; } + + .pr-xl-5 { + padding-right: 1.5rem !important; } + + .pb-xl-5 { + padding-bottom: 1.5rem !important; } + + .pl-xl-5 { + padding-left: 1.5rem !important; } + + .px-xl-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-xl-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-xl-6 { + padding: 2rem !important; } + + .pt-xl-6 { + padding-top: 2rem !important; } + + .pr-xl-6 { + padding-right: 2rem !important; } + + .pb-xl-6 { + padding-bottom: 2rem !important; } + + .pl-xl-6 { + padding-left: 2rem !important; } + + .px-xl-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-xl-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-xl-7 { + padding: 2.5rem !important; } + + .pt-xl-7 { + padding-top: 2.5rem !important; } + + .pr-xl-7 { + padding-right: 2.5rem !important; } + + .pb-xl-7 { + padding-bottom: 2.5rem !important; } + + .pl-xl-7 { + padding-left: 2.5rem !important; } + + .px-xl-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-xl-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-xl-8 { + padding: 3rem !important; } + + .pt-xl-8 { + padding-top: 3rem !important; } + + .pr-xl-8 { + padding-right: 3rem !important; } + + .pb-xl-8 { + padding-bottom: 3rem !important; } + + .pl-xl-8 { + padding-left: 3rem !important; } + + .px-xl-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-xl-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-xl-9 { + padding: 3.5rem !important; } + + .pt-xl-9 { + padding-top: 3.5rem !important; } + + .pr-xl-9 { + padding-right: 3.5rem !important; } + + .pb-xl-9 { + padding-bottom: 3.5rem !important; } + + .pl-xl-9 { + padding-left: 3.5rem !important; } + + .px-xl-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-xl-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-xl-10 { + padding: 4rem !important; } + + .pt-xl-10 { + padding-top: 4rem !important; } + + .pr-xl-10 { + padding-right: 4rem !important; } + + .pb-xl-10 { + padding-bottom: 4rem !important; } + + .pl-xl-10 { + padding-left: 4rem !important; } + + .px-xl-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-xl-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media print { + .site-footer, + .site-button, + #edit-this-page, + #back-to-top, + .site-nav, + .main-header { + display: none !important; } + + .side-bar { + width: 100%; + height: auto; + border-right: 0 !important; } + + .site-header { + border-bottom: 1px solid #44434d; } + + .site-title { + font-size: 1rem !important; + font-weight: 700 !important; } + + .text-small { + font-size: 8pt !important; } + + pre.highlight { + border: 1px solid #44434d; } + + .main { + max-width: none; + margin-left: 0; } } +a.skip-to-main { + left: -999px; + position: absolute; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + z-index: -999; } + +a.skip-to-main:focus, +a.skip-to-main:active { + color: #2c84fa; + background-color: #27262b; + left: auto; + top: auto; + width: 30%; + height: auto; + overflow: auto; + margin: 10px 35%; + padding: 5px; + border-radius: 15px; + border: 4px solid #264caf; + text-align: center; + font-size: 1.2em; + z-index: 999; } + +div.opaque { + background-color: #27262b; } + +p.warning, blockquote.warning { + background: rgba(221, 46, 46, 0.2); + border-left: 4px solid #f77e7e; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: .8rem; } + p.warning::before, blockquote.warning::before { + color: #f77e7e; + content: "Warning"; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + p.warning > .warning-title, blockquote.warning > .warning-title { + color: #f77e7e; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + +p.warning-title, blockquote.warning-title { + background: rgba(221, 46, 46, 0.2); + border-left: 4px solid #f77e7e; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: .8rem; } + p.warning-title > p:first-child, blockquote.warning-title > p:first-child { + margin-top: 0; + margin-bottom: 0; + color: #f77e7e; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + +blockquote.warning { + margin-left: 0; + margin-right: 0; } + blockquote.warning > p:first-child { + margin-top: 0; } + blockquote.warning > p:last-child { + margin-bottom: 0; } + +blockquote.warning-title { + margin-left: 0; + margin-right: 0; } + blockquote.warning-title > p:nth-child(2) { + margin-top: 0; } + blockquote.warning-title > p:last-child { + margin-bottom: 0; } + +.announcement, .main-content .module, +.module { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07), 0 4px 14px rgba(0, 0, 0, 0.05); + margin: 1rem -1rem; + display: flex; + flex-direction: column; + min-width: 0; + padding: 0 1rem; + position: relative; + word-wrap: break-word; } + @media (min-width: 50rem) { + .announcement, .main-content .module, + .module { + border-radius: 4px; + margin: 1rem 0; } } + .announcement > :first-child, .main-content .module > :first-child, + .module > :first-child { + border-top: none !important; } + .announcement > :last-child, .main-content .module > :last-child, + .module > :last-child { + border-bottom: none !important; } + .announcement .label, + .module .label, .announcement .staffer .staffer-pronouns, .staffer .announcement .staffer-pronouns, + .module .staffer .staffer-pronouns, + .staffer .module .staffer-pronouns, .announcement div.highlighter-rouge[overlay]::after, + .module div.highlighter-rouge[overlay]::after { + border-radius: 4px; + margin-left: 0; + user-select: none; } + +.main-content .module h1:first-child, +.main-content .module h2:first-child, +.main-content .module h3:first-child, +.main-content .module h4:first-child, +.main-content .module h5:first-child, +.main-content .module h6:first-child, +.module h1:first-child, +.module h2:first-child, +.module h3:first-child, +.module h4:first-child, +.module h5:first-child, +.module h6:first-child { + margin-top: 1rem; } +.main-content .module > dl, +.module > dl { + border-bottom: 1px solid #44434d; + border-top: 1px solid #44434d; + display: grid; + grid-template-columns: max-content 1fr; + margin: 0.5rem -1rem; } + .main-content .module > dl:first-child, + .module > dl:first-child { + margin-top: 0; } + .main-content .module > dl:last-child, + .module > dl:last-child { + margin-bottom: 0; } + @media (min-width: 66.5rem) { + .main-content .module > dl, + .module > dl { + grid-template-columns: 1fr 7fr; } } + .main-content .module > dl > dt, + .main-content .module > dl > dd, + .main-content .module > dl > dt, + .module > dl > dt, + .main-content .module > dl > dd, + .module > dl > dd { + margin: 0; + padding: 0.5rem; } + @media (min-width: 31.25rem) { + .main-content .module > dl > dt, + .main-content .module > dl > dd, + .main-content .module > dl > dt, + .module > dl > dt, + .main-content .module > dl > dd, + .module > dl > dd { + padding: 0.5rem 1rem; } } + .main-content .module > dl > dt, + .module > dl > dt { + border-top: 1px solid #44434d; + font-weight: normal; + text-align: right; } + .main-content .module > dl > dt + dd, + .module > dl > dt + dd { + border-top: 1px solid #44434d; } + .main-content .module > dl > dt:first-child, + .module > dl > dt:first-child { + border-top: none; } + .main-content .module > dl > dt:first-child + dd, + .module > dl > dt:first-child + dd { + border-top: none; } + .main-content .module > dl > dt::after, + .module > dl > dt::after { + content: ":"; } + .main-content .module > dl > dd + dd, + .module > dl > dd + dd { + padding-top: 0; } + .main-content .module > dl > dd ol, .main-content .module > dl > dd ul, .main-content .module > dl > dd dl, + .module > dl > dd ol, + .module > dl > dd ul, + .module > dl > dd dl { + margin: 0; } + .main-content .module > dl > dd dl, + .module > dl > dd dl { + display: flex; + flex-direction: column; } + @media (min-width: 31.25rem) { + .main-content .module > dl > dd dl, + .module > dl > dd dl { + flex-direction: row; } } + .main-content .module > dl > dd dl dt, + .module > dl > dd dl dt { + flex: 0 0 62.5%; + margin: 0; } + .main-content .module > dl > dd dl dd, + .module > dl > dd dl dd { + margin: 0; } + +.schedule td { + min-width: 0; + padding: 0; + vertical-align: top; } +.schedule .note { + font-style: italic; } +.schedule .holiday { + background-color: #efefef; + padding: 5px; } +.schedule .assignment { + border: 1px dashed red; + padding: 5px; } +.schedule .day { + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; + padding: 4px; } +.schedule .week-label { + min-width: 80px; + vertical-align: middle; + padding: 4px; + text-align: center; } +.schedule .content { + flex-grow: 100; } +.schedule .date { + font-weight: bolder; } + +.bldschedule { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07), 0 4px 14px rgba(0, 0, 0, 0.05); + margin: 1rem -1rem; + overflow-x: scroll; + position: relative; } + @media (min-width: 50rem) { + .bldschedule { + border-radius: 4px; + margin: 1rem 0; } } + .bldschedule li::before { + display: none; } + .bldschedule ul.schedule-timeline, + .bldschedule ul.schedule-group, + .bldschedule ul.schedule-events { + margin-top: 0; + padding-left: 0; } + .bldschedule ul.schedule-timeline { + margin: 40px auto 0; + position: absolute; + width: 100%; } + .bldschedule .schedule-time { + color: #959396; + height: 40px; + margin: 0; + padding: 0.5rem; + position: relative; } + .bldschedule .schedule-time::after { + background-color: #44434d; + content: ''; + height: 1px; + left: 0; + position: absolute; + top: 0; + width: 100%; } + .bldschedule .schedule-group { + display: flex; + margin-bottom: 0; + position: relative; } + .bldschedule .schedule-day { + border-left: 1px solid #44434d; + flex: 1 0 0; + margin: 0; + min-width: 130px; } + .bldschedule .schedule-day:first-of-type { + border-left: 0; } + .bldschedule h2.schedule-header { + align-items: center; + display: flex; + font-size: 18px !important; + height: 40px; + justify-content: center; + margin: 0; } + .bldschedule .schedule-events { + display: flex; + padding: 0; + position: relative; } + .bldschedule .schedule-event { + background-color: #959396; + border-radius: 4px; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), inset 0 -3px 0 rgba(0, 0, 0, 0.2); + color: #fff; + float: left; + height: 100%; + margin: 0; + padding: 0.25rem 0.5rem; + position: absolute; + width: 100%; } + .bldschedule .schedule-event.lecture { + background-color: #959396; } + .bldschedule .schedule-event.section { + background-color: #7253ed; } + .bldschedule .schedule-event.office-hours { + background-color: #2c84fa; } + +.staffer { + display: flex; + margin: 1rem; } + .staffer .staffer-image { + border-radius: 50%; + height: 100px; + margin-right: 1rem; } + .staffer p, + .staffer .staffer-name { + margin: 0.25rem !important; } + +a abbr[title] { + border-bottom: none; } + +abbr[title] { + text-decoration: none; } + +code { + font-size: 14px; + padding: 0.2em 0.4em; + border: none; } + +div.highlighter-rouge[overlay] { + position: relative; } + div.highlighter-rouge[overlay]::after { + background-color: #fff; + border-radius: 4px; + bottom: 0.5rem; + content: attr(overlay); + position: absolute; + right: 0; + user-select: none; } + +details { + margin: 0 40px 1em; } + +h1, h2, h3, h4, h5, h6 { + align-items: center; + display: flex; } + +iframe, +summary { + max-width: 100%; } + +.main-content-wrap { + max-width: 50rem; + margin: auto; } + +.main-content a { + overflow-wrap: anywhere; + white-space: normal; } +.main-content dl { + display: block; + grid-template-columns: none; } +.main-content dt { + font-weight: 700; + text-align: start; } + .main-content dt::after { + content: normal; } +.main-content dd { + font-weight: normal; } + .main-content dd + dt { + margin-top: 1em; } +.main-content .katex { + font-size: 1.1em; } + +[style*="--aspect-ratio"] > :first-child { + width: 100%; } + +[style*="--aspect-ratio"] > img { + height: auto; } + +@supports (--custom: property) { + [style*="--aspect-ratio"] { + position: relative; } + + [style*="--aspect-ratio"]::before { + content: ""; + display: block; + padding-bottom: calc(100% / (var(--aspect-ratio))); } + + [style*="--add-height"]::before { + padding-bottom: calc(100% / (var(--aspect-ratio)) + (var(--add-height))); } + + [style*="--aspect-ratio"] > :first-child { + position: absolute; + top: 0; + left: 0; + height: 100%; } } +.mermaid .label, .mermaid .staffer .staffer-pronouns, .staffer .mermaid .staffer-pronouns, .mermaid div.highlighter-rouge[overlay]::after { + text-transform: none; } diff --git a/assets/css/just-the-docs-default.css b/assets/css/just-the-docs-default.css new file mode 100644 index 00000000..eed28ec8 --- /dev/null +++ b/assets/css/just-the-docs-default.css @@ -0,0 +1,6757 @@ +@charset "UTF-8"; +.highlight, +pre.highlight { + background: #f9f9f9; + color: #383942; } + +.highlight pre { + background: #f9f9f9; } + +.highlight .hll { + background: #f9f9f9; } + +.highlight .c { + color: #9fa0a6; + font-style: italic; } + +.highlight .err { + color: #fff; + background-color: #e05151; } + +.highlight .k { + color: #a625a4; } + +.highlight .l { + color: #50a04f; } + +.highlight .n { + color: #383942; } + +.highlight .o { + color: #383942; } + +.highlight .p { + color: #383942; } + +.highlight .cm { + color: #9fa0a6; + font-style: italic; } + +.highlight .cp { + color: #9fa0a6; + font-style: italic; } + +.highlight .c1 { + color: #9fa0a6; + font-style: italic; } + +.highlight .cs { + color: #9fa0a6; + font-style: italic; } + +.highlight .ge { + font-style: italic; } + +.highlight .gs { + font-weight: 700; } + +.highlight .kc { + color: #a625a4; } + +.highlight .kd { + color: #a625a4; } + +.highlight .kn { + color: #a625a4; } + +.highlight .kp { + color: #a625a4; } + +.highlight .kr { + color: #a625a4; } + +.highlight .kt { + color: #a625a4; } + +.highlight .ld { + color: #50a04f; } + +.highlight .m { + color: #b66a00; } + +.highlight .s { + color: #50a04f; } + +.highlight .na { + color: #b66a00; } + +.highlight .nb { + color: #ca7601; } + +.highlight .nc { + color: #ca7601; } + +.highlight .no { + color: #ca7601; } + +.highlight .nd { + color: #ca7601; } + +.highlight .ni { + color: #ca7601; } + +.highlight .ne { + color: #ca7601; } + +.highlight .nf { + color: #383942; } + +.highlight .nl { + color: #ca7601; } + +.highlight .nn { + color: #383942; } + +.highlight .nx { + color: #383942; } + +.highlight .py { + color: #ca7601; } + +.highlight .nt { + color: #e35549; } + +.highlight .nv { + color: #ca7601; } + +.highlight .ow { + font-weight: 700; } + +.highlight .w { + color: #f8f8f2; } + +.highlight .mf { + color: #b66a00; } + +.highlight .mh { + color: #b66a00; } + +.highlight .mi { + color: #b66a00; } + +.highlight .mo { + color: #b66a00; } + +.highlight .sb { + color: #50a04f; } + +.highlight .sc { + color: #50a04f; } + +.highlight .sd { + color: #50a04f; } + +.highlight .s2 { + color: #50a04f; } + +.highlight .se { + color: #50a04f; } + +.highlight .sh { + color: #50a04f; } + +.highlight .si { + color: #50a04f; } + +.highlight .sx { + color: #50a04f; } + +.highlight .sr { + color: #0083bb; } + +.highlight .s1 { + color: #50a04f; } + +.highlight .ss { + color: #0083bb; } + +.highlight .bp { + color: #ca7601; } + +.highlight .vc { + color: #ca7601; } + +.highlight .vg { + color: #ca7601; } + +.highlight .vi { + color: #e35549; } + +.highlight .il { + color: #b66a00; } + +.highlight .gu { + color: #75715e; } + +.highlight .gd { + color: #e05151; } + +.highlight .gi { + color: #43d089; } + +.highlight .language-json .w + .s2 { + color: #e35549; } + +.highlight .language-json .kc { + color: #0083bb; } + +p.important { + background: rgba(44, 132, 250, 0.2); + border-left: 4px solid #183385; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: 0.8rem; } + +p.note { + background: rgba(114, 83, 237, 0.2); + border-left: 4px solid #381885; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: 0.8rem; } + +p.highlight { + background: rgba(255, 235, 130, 0.2); + border-left: 4px solid #e7af06; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: 0.8rem; } + +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +/* Document + ========================================================================== */ +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ +html { + line-height: 1.15; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ } + +/* Sections + ========================================================================== */ +/** + * Remove the margin in all browsers. + */ +body { + margin: 0; } + +/** + * Render the `main` element consistently in IE. + */ +main { + display: block; } + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ +h1 { + font-size: 2em; + margin: 0.67em 0; } + +/* Grouping content + ========================================================================== */ +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ +hr { + box-sizing: content-box; + /* 1 */ + height: 0; + /* 1 */ + overflow: visible; + /* 2 */ } + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ +pre { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ } + +/* Text-level semantics + ========================================================================== */ +/** + * Remove the gray background on active links in IE 10. + */ +a { + background-color: transparent; } + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ +abbr[title] { + border-bottom: none; + /* 1 */ + text-decoration: underline; + /* 2 */ + text-decoration: underline dotted; + /* 2 */ } + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ +b, +strong { + font-weight: bolder; } + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ +code, +kbd, +samp { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ } + +/** + * Add the correct font size in all browsers. + */ +small { + font-size: 80%; } + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } + +sub { + bottom: -0.25em; } + +sup { + top: -0.5em; } + +/* Embedded content + ========================================================================== */ +/** + * Remove the border on images inside links in IE 10. + */ +img { + border-style: none; } + +/* Forms + ========================================================================== */ +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: 1.15; + /* 1 */ + margin: 0; + /* 2 */ } + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ +button, +input { + /* 1 */ + overflow: visible; } + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ +button, +select { + /* 1 */ + text-transform: none; } + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; } + +/** + * Remove the inner border and padding in Firefox. + */ +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; } + +/** + * Restore the focus styles unset by the previous rule. + */ +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; } + +/** + * Correct the padding in Firefox. + */ +fieldset { + padding: 0.35em 0.75em 0.625em; } + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ +legend { + box-sizing: border-box; + /* 1 */ + color: inherit; + /* 2 */ + display: table; + /* 1 */ + max-width: 100%; + /* 1 */ + padding: 0; + /* 3 */ + white-space: normal; + /* 1 */ } + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ +progress { + vertical-align: baseline; } + +/** + * Remove the default vertical scrollbar in IE 10+. + */ +textarea { + overflow: auto; } + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ } + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; } + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ +[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ } + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ } + +/* Interactive + ========================================================================== */ +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ +details { + display: block; } + +/* + * Add the correct display in all browsers. + */ +summary { + display: list-item; } + +/* Misc + ========================================================================== */ +/** + * Add the correct display in IE 10+. + */ +template { + display: none; } + +/** + * Add the correct display in IE 10. + */ +[hidden] { + display: none; } + +:root { + color-scheme: light; } + +* { + box-sizing: border-box; } + +html { + font-size: 0.875rem !important; + scroll-behavior: smooth; } + @media (min-width: 31.25rem) { + html { + font-size: 1rem !important; } } + +body { + font-family: system-ui, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, sans-serif, "Segoe UI Emoji"; + font-size: inherit; + line-height: 1.4; + color: #5c5962; + background-color: #fff; + overflow-wrap: break-word; } + +ol, +ul, +dl, +pre, +address, +blockquote, +table, +div, +hr, +form, +fieldset, +noscript .table-wrapper { + margin-top: 0; } + +h1, +h2, +h3, +h4, +h5, +h6, +#toctitle { + margin-top: 0; + margin-bottom: 1em; + font-weight: 500; + line-height: 1.25; + color: #27262b; } + +p { + margin-top: 1em; + margin-bottom: 1em; } + +a { + color: #d41b2c; + text-decoration: none; } + +a:not([class]) { + text-decoration: underline; + text-decoration-color: #eeebee; + text-underline-offset: 2px; } + a:not([class]):hover { + text-decoration-color: rgba(212, 27, 44, 0.45); } + +code { + font-family: "SFMono-Regular", menlo, consolas, monospace; + font-size: 0.75em; + line-height: 1.4; } + +figure, +pre { + margin: 0; } + +li { + margin: 0.25em 0; } + +img { + max-width: 100%; + height: auto; } + +hr { + height: 1px; + padding: 0; + margin: 2rem 0; + background-color: #eeebee; + border: 0; } + +blockquote { + margin: 10px 0; + margin-block-start: 0; + margin-inline-start: 0; + padding-left: 1rem; + border-left: 3px solid #eeebee; } + +.side-bar { + z-index: 0; + display: flex; + flex-wrap: wrap; + background-color: #f5f6fa; } + @media (min-width: 50rem) { + .side-bar { + flex-flow: column nowrap; + position: fixed; + width: 15.5rem; + height: 100%; + border-right: 1px solid #eeebee; + align-items: flex-end; } } + @media (min-width: 66.5rem) { + .side-bar { + width: calc((100% - 66.5rem) / 2 + 16.5rem); + min-width: 16.5rem; } } + +@media (min-width: 50rem) { + .main { + position: relative; + max-width: 50rem; + margin-left: 15.5rem; } } +@media (min-width: 66.5rem) { + .main { + margin-left: Max(16.5rem, calc((100% - 66.5rem) / 2 + 16.5rem)); } } + +.main-content-wrap { + padding-right: 1rem; + padding-left: 1rem; + padding-top: 1rem; + padding-bottom: 1rem; } + @media (min-width: 50rem) { + .main-content-wrap { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 50rem) { + .main-content-wrap { + padding-top: 2rem; + padding-bottom: 2rem; } } + +.main-header { + z-index: 0; + display: none; + background-color: #f5f6fa; } + @media (min-width: 50rem) { + .main-header { + display: flex; + justify-content: space-between; + height: 3.75rem; + background-color: #fff; + border-bottom: 1px solid #eeebee; } } + .main-header.nav-open { + display: block; } + @media (min-width: 50rem) { + .main-header.nav-open { + display: flex; } } + +.site-nav, +.site-header, +.site-footer { + width: 100%; } + @media (min-width: 66.5rem) { + .site-nav, + .site-header, + .site-footer { + width: 16.5rem; } } + +.site-nav { + display: none; } + .site-nav.nav-open { + display: block; } + @media (min-width: 50rem) { + .site-nav { + display: block; + padding-top: 3rem; + padding-bottom: 1rem; + overflow-y: auto; + flex: 1 1 auto; } } + +.site-header { + display: flex; + min-height: 3.75rem; + align-items: center; } + @media (min-width: 50rem) { + .site-header { + height: 3.75rem; + max-height: 3.75rem; + border-bottom: 1px solid #eeebee; } } + +.site-title { + padding-right: 1rem; + padding-left: 1rem; + flex-grow: 1; + display: flex; + height: 100%; + align-items: center; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #27262b; + font-size: 1.125rem !important; } + @media (min-width: 50rem) { + .site-title { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 31.25rem) { + .site-title { + font-size: 1.5rem !important; + line-height: 1.25; } } + @media (min-width: 50rem) { + .site-title { + padding-top: 0.5rem; + padding-bottom: 0.5rem; } } + +.site-button { + display: flex; + height: 100%; + padding: 1rem; + align-items: center; } + +@media (min-width: 50rem) { + .site-header .site-button { + display: none; } } +.site-title:hover { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%); } + +.site-button:hover { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%); } + +body { + position: relative; + padding-bottom: 4rem; + overflow-y: scroll; } + @media (min-width: 50rem) { + body { + position: static; + padding-bottom: 0; } } + +.site-footer { + padding-right: 1rem; + padding-left: 1rem; + position: absolute; + bottom: 0; + left: 0; + padding-top: 1rem; + padding-bottom: 1rem; + color: #959396; + font-size: 0.6875rem !important; } + @media (min-width: 50rem) { + .site-footer { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 31.25rem) { + .site-footer { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) { + .site-footer { + position: static; + justify-self: end; } } + +.icon { + width: 1.5rem; + height: 1.5rem; + color: #d41b2c; } + +.main-content { + line-height: 1.6; } + .main-content ol, + .main-content ul, + .main-content dl, + .main-content pre, + .main-content address, + .main-content blockquote, + .main-content .table-wrapper { + margin-top: 0.5em; } + .main-content a { + overflow: hidden; + text-overflow: ellipsis; } + .main-content ul, + .main-content ol { + padding-left: 1.5em; } + .main-content li .highlight { + margin-top: 0.25rem; } + .main-content ol { + list-style-type: none; + counter-reset: step-counter; } + .main-content ol > li { + position: relative; } + .main-content ol > li::before { + position: absolute; + top: 0.2em; + left: -1.6em; + color: #959396; + content: counter(step-counter); + counter-increment: step-counter; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .main-content ol > li::before { + font-size: 0.875rem !important; } } + @media (min-width: 31.25rem) { + .main-content ol > li::before { + top: 0.11em; } } + .main-content ol > li ol { + counter-reset: sub-counter; } + .main-content ol > li ol > li::before { + content: counter(sub-counter,lower-alpha); + counter-increment: sub-counter; } + .main-content ul { + list-style: none; } + .main-content ul > li::before { + position: absolute; + margin-left: -1.4em; + color: #959396; + content: "•"; } + .main-content .task-list-item::before { + content: ""; } + .main-content .task-list-item-checkbox { + margin-right: 0.6em; + margin-left: -1.4em; } + .main-content hr + * { + margin-top: 0; } + .main-content h1:first-of-type { + margin-top: 0.5em; } + .main-content dl { + display: grid; + grid-template: auto / 10em 1fr; } + .main-content dt, + .main-content dd { + margin: 0.25em 0; } + .main-content dt { + grid-column: 1; + font-weight: 500; + text-align: right; } + .main-content dt::after { + content: ":"; } + .main-content dd { + grid-column: 2; + margin-bottom: 0; + margin-left: 1em; } + .main-content dd blockquote:first-child, + .main-content dd div:first-child, + .main-content dd dl:first-child, + .main-content dd dt:first-child, + .main-content dd h1:first-child, + .main-content dd h2:first-child, + .main-content dd h3:first-child, + .main-content dd h4:first-child, + .main-content dd h5:first-child, + .main-content dd h6:first-child, + .main-content dd li:first-child, + .main-content dd ol:first-child, + .main-content dd p:first-child, + .main-content dd pre:first-child, + .main-content dd table:first-child, + .main-content dd ul:first-child, + .main-content dd .table-wrapper:first-child { + margin-top: 0; } + .main-content dd dl:first-child dt:first-child, + .main-content dd dl:first-child dd:nth-child(2), + .main-content ol dl:first-child dt:first-child, + .main-content ol dl:first-child dd:nth-child(2), + .main-content ul dl:first-child dt:first-child, + .main-content ul dl:first-child dd:nth-child(2) { + margin-top: 0; } + .main-content .anchor-heading { + position: absolute; + right: -1rem; + width: 1.5rem; + height: 100%; + padding-right: 0.25rem; + padding-left: 0.25rem; + overflow: visible; } + @media (min-width: 50rem) { + .main-content .anchor-heading { + right: auto; + left: -1.5rem; } } + .main-content .anchor-heading svg { + display: inline-block; + width: 100%; + height: 100%; + color: #d41b2c; + visibility: hidden; } + .main-content .anchor-heading:hover svg, + .main-content .anchor-heading:focus svg, + .main-content h1:hover > .anchor-heading svg, + .main-content h2:hover > .anchor-heading svg, + .main-content h3:hover > .anchor-heading svg, + .main-content h4:hover > .anchor-heading svg, + .main-content h5:hover > .anchor-heading svg, + .main-content h6:hover > .anchor-heading svg { + visibility: visible; } + .main-content summary { + cursor: pointer; } + .main-content h1, + .main-content h2, + .main-content h3, + .main-content h4, + .main-content h5, + .main-content h6, + .main-content #toctitle { + position: relative; + margin-top: 1.5em; + margin-bottom: 0.25em; } + .main-content h1 + table, + .main-content h1 + .table-wrapper, + .main-content h1 + .code-example, + .main-content h1 + .highlighter-rouge, + .main-content h1 + .sectionbody .listingblock, + .main-content h2 + table, + .main-content h2 + .table-wrapper, + .main-content h2 + .code-example, + .main-content h2 + .highlighter-rouge, + .main-content h2 + .sectionbody .listingblock, + .main-content h3 + table, + .main-content h3 + .table-wrapper, + .main-content h3 + .code-example, + .main-content h3 + .highlighter-rouge, + .main-content h3 + .sectionbody .listingblock, + .main-content h4 + table, + .main-content h4 + .table-wrapper, + .main-content h4 + .code-example, + .main-content h4 + .highlighter-rouge, + .main-content h4 + .sectionbody .listingblock, + .main-content h5 + table, + .main-content h5 + .table-wrapper, + .main-content h5 + .code-example, + .main-content h5 + .highlighter-rouge, + .main-content h5 + .sectionbody .listingblock, + .main-content h6 + table, + .main-content h6 + .table-wrapper, + .main-content h6 + .code-example, + .main-content h6 + .highlighter-rouge, + .main-content h6 + .sectionbody .listingblock, + .main-content #toctitle + table, + .main-content #toctitle + .table-wrapper, + .main-content #toctitle + .code-example, + .main-content #toctitle + .highlighter-rouge, + .main-content #toctitle + .sectionbody .listingblock { + margin-top: 1em; } + .main-content h1 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h2 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h3 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h4 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h5 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h6 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content #toctitle + p:not(.label):not(div.highlighter-rouge[overlay]::after) { + margin-top: 0; } + .main-content > h1:first-child, + .main-content > h2:first-child, + .main-content > h3:first-child, + .main-content > h4:first-child, + .main-content > h5:first-child, + .main-content > h6:first-child, + .main-content > .sect1:first-child > h2, + .main-content > .sect2:first-child > h3, + .main-content > .sect3:first-child > h4, + .main-content > .sect4:first-child > h5, + .main-content > .sect5:first-child > h6 { + margin-top: 0.5rem; } + +.nav-list { + padding: 0; + margin-top: 0; + margin-bottom: 0; + list-style: none; } + .nav-list .nav-list-item { + font-size: 0.875rem !important; + position: relative; + margin: 0; } + @media (min-width: 31.25rem) { + .nav-list .nav-list-item { + font-size: 1rem !important; } } + @media (min-width: 50rem) { + .nav-list .nav-list-item { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .nav-list .nav-list-item { + font-size: 0.875rem !important; } } + + .nav-list .nav-list-item .nav-list-link { + display: block; + min-height: 3rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + line-height: 2.5rem; + padding-right: 3rem; + padding-left: 1rem; } + @media (min-width: 50rem) { + .nav-list .nav-list-item .nav-list-link { + min-height: 2rem; + line-height: 1.5rem; + padding-right: 2rem; + padding-left: 2rem; } } + .nav-list .nav-list-item .nav-list-link.external > svg { + width: 1rem; + height: 1rem; + vertical-align: text-bottom; } + .nav-list .nav-list-item .nav-list-link.active { + font-weight: 600; + text-decoration: none; } + .nav-list .nav-list-item .nav-list-link:hover, .nav-list .nav-list-item .nav-list-link.active { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%); } + .nav-list .nav-list-item .nav-list-expander { + position: absolute; + right: 0; + width: 3rem; + height: 3rem; + padding: 0.75rem; + color: #d41b2c; } + @media (min-width: 50rem) { + .nav-list .nav-list-item .nav-list-expander { + width: 2rem; + height: 2rem; + padding: 0.5rem; } } + .nav-list .nav-list-item .nav-list-expander:hover { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%); } + .nav-list .nav-list-item .nav-list-expander svg { + transform: rotate(90deg); } + .nav-list .nav-list-item > .nav-list { + display: none; + padding-left: 0.75rem; + list-style: none; } + .nav-list .nav-list-item > .nav-list .nav-list-item { + position: relative; } + .nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-link { + color: #5c5962; } + .nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-expander { + color: #5c5962; } + .nav-list .nav-list-item.active > .nav-list-expander svg { + transform: rotate(-90deg); } + .nav-list .nav-list-item.active > .nav-list { + display: block; } + +.nav-category { + padding: 0.5rem 1rem; + font-weight: 600; + text-align: start; + text-transform: uppercase; + border-bottom: 1px solid #eeebee; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .nav-category { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) { + .nav-category { + padding: 0.5rem 2rem; + margin-top: 1rem; + text-align: start; } + .nav-category:first-child { + margin-top: 0; } } + +.nav-list.nav-category-list > .nav-list-item { + margin: 0; } + .nav-list.nav-category-list > .nav-list-item > .nav-list { + padding: 0; } + .nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-link { + color: #d41b2c; } + .nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-expander { + color: #d41b2c; } + +.aux-nav { + height: 100%; + overflow-x: auto; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .aux-nav { + font-size: 0.75rem !important; } } + .aux-nav .aux-nav-list { + display: flex; + height: 100%; + padding: 0; + margin: 0; + list-style: none; } + .aux-nav .aux-nav-list-item { + display: inline-block; + height: 100%; + padding: 0; + margin: 0; } + @media (min-width: 50rem) { + .aux-nav { + padding-right: 1rem; } } + +@media (min-width: 50rem) { + .breadcrumb-nav { + margin-top: -1rem; } } + +.breadcrumb-nav-list { + padding-left: 0; + margin-bottom: 0.75rem; + list-style: none; } + +.breadcrumb-nav-list-item { + display: table-cell; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .breadcrumb-nav-list-item { + font-size: 0.75rem !important; } } + .breadcrumb-nav-list-item::before { + display: none; } + .breadcrumb-nav-list-item::after { + display: inline-block; + margin-right: 0.5rem; + margin-left: 0.5rem; + color: #959396; + content: "/"; } + .breadcrumb-nav-list-item:last-child::after { + content: ""; } + +h1, +.text-alpha { + font-size: 2rem !important; + line-height: 1.25; + font-weight: 300; } + @media (min-width: 31.25rem) { + h1, + .text-alpha { + font-size: 2.25rem !important; } } + +h2, +.text-beta, +#toctitle { + font-size: 1.125rem !important; } + @media (min-width: 31.25rem) { + h2, + .text-beta, + #toctitle { + font-size: 1.5rem !important; + line-height: 1.25; } } + +h3, +.text-gamma, +.announcement h1, +.announcement h2 { + font-size: 1rem !important; } + @media (min-width: 31.25rem) { + h3, + .text-gamma, + .announcement h1, + .announcement h2 { + font-size: 1.125rem !important; } } + +h4, +.text-delta { + font-size: 0.6875rem !important; + font-weight: 400; + text-transform: uppercase; + letter-spacing: 0.1em; } + @media (min-width: 31.25rem) { + h4, + .text-delta { + font-size: 0.75rem !important; } } + +h4 code { + text-transform: none; } + +h5, +.text-epsilon, +.announcement .announcement-meta { + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + h5, + .text-epsilon, + .announcement .announcement-meta { + font-size: 0.875rem !important; } } + +h6, +.text-zeta { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + h6, + .text-zeta { + font-size: 0.75rem !important; } } + +.text-small { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .text-small { + font-size: 0.75rem !important; } } + +.text-mono { + font-family: "SFMono-Regular", menlo, consolas, monospace !important; } + +.text-left { + text-align: left !important; } + +.text-center { + text-align: center !important; } + +.text-right { + text-align: right !important; } + +.label:not(g), .staffer .staffer-pronouns:not(g), div.highlighter-rouge[overlay]:not(g)::after, +.label-blue:not(g) { + display: inline-block; + padding: 0.16em 0.56em; + margin-right: 0.5rem; + margin-left: 0.5rem; + color: #fff; + text-transform: uppercase; + vertical-align: middle; + background-color: #2869e6; + font-size: 0.6875rem !important; + border-radius: 12px; } + @media (min-width: 31.25rem) { + .label:not(g), .staffer .staffer-pronouns:not(g), div.highlighter-rouge[overlay]:not(g)::after, + .label-blue:not(g) { + font-size: 0.75rem !important; } } + +.label-green:not(g) { + background-color: #009c7b; } + +.label-purple:not(g) { + background-color: #5e41d0; } + +.label-red:not(g) { + background-color: #e94c4c; } + +.label-yellow:not(g) { + color: #44434d; + background-color: #f7d12e; } + +.btn, summary { + display: inline-block; + box-sizing: border-box; + padding: 0.3em 1em; + margin: 0; + font-family: inherit; + font-size: inherit; + font-weight: 500; + line-height: 1.5; + color: #d41b2c; + text-decoration: none; + vertical-align: baseline; + cursor: pointer; + background-color: #f7f7f7; + border-width: 0; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + appearance: none; } + .btn:focus, summary:focus { + text-decoration: none; + outline: none; + box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn:focus:hover, summary:focus:hover, .btn.selected:focus, summary.selected:focus { + box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn:hover, summary:hover, .btn.zeroclipboard-is-hover, summary.zeroclipboard-is-hover { + color: #cb1a2a; } + .btn:hover, summary:hover, .btn:active, summary:active, .btn.zeroclipboard-is-hover, summary.zeroclipboard-is-hover, .btn.zeroclipboard-is-active, summary.zeroclipboard-is-active { + text-decoration: none; + background-color: #f4f4f4; } + .btn:active, summary:active, .btn.selected, summary.selected, .btn.zeroclipboard-is-active, summary.zeroclipboard-is-active { + background-color: #efefef; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn.selected:hover, summary.selected:hover { + background-color: #cfcfcf; } + .btn:disabled, summary:disabled, .btn:disabled:hover, summary:disabled:hover, .btn.disabled, summary.disabled, .btn.disabled:hover, summary.disabled:hover { + color: rgba(102, 102, 102, 0.5); + cursor: default; + background-color: rgba(229, 229, 229, 0.5); + background-image: none; + box-shadow: none; } + +.btn-outline, summary { + color: #d41b2c; + background: transparent; + box-shadow: inset 0 0 0 2px #e6e1e8; } + .btn-outline:hover, summary:hover, .btn-outline:active, summary:active, .btn-outline.zeroclipboard-is-hover, summary.zeroclipboard-is-hover, .btn-outline.zeroclipboard-is-active, summary.zeroclipboard-is-active { + color: #c21928; + text-decoration: none; + background-color: transparent; + box-shadow: inset 0 0 0 3px #e6e1e8; } + .btn-outline:focus, summary:focus { + text-decoration: none; + outline: none; + box-shadow: inset 0 0 0 2px #5c5962, 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn-outline:focus:hover, summary:focus:hover, .btn-outline.selected:focus, summary.selected:focus { + box-shadow: inset 0 0 0 2px #5c5962; } + +.btn-primary { + color: #fff; + background-color: #5739ce; + background-image: linear-gradient(#6f55d5, #5739ce); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-primary:hover, .btn-primary.zeroclipboard-is-hover { + color: #fff; + background-color: #5132cb; + background-image: linear-gradient(#6549d2, #5132cb); } + .btn-primary:active, .btn-primary.selected, .btn-primary.zeroclipboard-is-active { + background-color: #4f31c6; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-primary.selected:hover { + background-color: #472cb2; } + +.btn-purple { + color: #fff; + background-color: #5739ce; + background-image: linear-gradient(#6f55d5, #5739ce); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-purple:hover, .btn-purple.zeroclipboard-is-hover { + color: #fff; + background-color: #5132cb; + background-image: linear-gradient(#6549d2, #5132cb); } + .btn-purple:active, .btn-purple.selected, .btn-purple.zeroclipboard-is-active { + background-color: #4f31c6; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-purple.selected:hover { + background-color: #472cb2; } + +.btn-blue { + color: #fff; + background-color: #227efa; + background-image: linear-gradient(#4593fb, #227efa); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-blue:hover, .btn-blue.zeroclipboard-is-hover { + color: #fff; + background-color: #1878fa; + background-image: linear-gradient(#368afa, #1878fa); } + .btn-blue:active, .btn-blue.selected, .btn-blue.zeroclipboard-is-active { + background-color: #1375f9; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-blue.selected:hover { + background-color: #0669ed; } + +.btn-green { + color: #fff; + background-color: #10ac7d; + background-image: linear-gradient(#13cc95, #10ac7d); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-green:hover, .btn-green.zeroclipboard-is-hover { + color: #fff; + background-color: #0fa276; + background-image: linear-gradient(#12be8b, #0fa276); } + .btn-green:active, .btn-green.selected, .btn-green.zeroclipboard-is-active { + background-color: #0f9e73; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-green.selected:hover { + background-color: #0d8662; } + +.btn-reset { + background: none; + border: none; + margin: 0; + text-align: inherit; + font: inherit; + border-radius: 0; + appearance: none; } + +.search { + position: relative; + z-index: 2; + flex-grow: 1; + height: 4rem; + padding: 0.5rem; + transition: padding linear 200ms; } + @media (min-width: 50rem) { + .search { + position: relative !important; + width: auto !important; + height: 100% !important; + padding: 0; + transition: none; } } + +.search-input-wrap { + position: relative; + z-index: 1; + height: 3rem; + overflow: hidden; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + transition: height linear 200ms; } + @media (min-width: 50rem) { + .search-input-wrap { + position: absolute; + width: 100%; + max-width: 33.5rem; + height: 100% !important; + border-radius: 0; + box-shadow: none; + transition: width ease 400ms; } } + +.search-input { + position: absolute; + width: 100%; + height: 100%; + padding: 0.5rem 1rem 0.5rem 2.5rem; + font-size: 1rem; + color: #5c5962; + background-color: #fff; + border-top: 0; + border-right: 0; + border-bottom: 0; + border-left: 0; + border-radius: 0; } + @media (min-width: 50rem) { + .search-input { + padding: 0.5rem 1rem 0.5rem 3.5rem; + font-size: 0.875rem; + background-color: #fff; + transition: padding-left linear 200ms; } } + .search-input:focus { + outline: 0; } + .search-input:focus + .search-label .search-icon { + color: #d41b2c; } + +.search-label { + position: absolute; + display: flex; + height: 100%; + padding-left: 1rem; } + @media (min-width: 50rem) { + .search-label { + padding-left: 2rem; + transition: padding-left linear 200ms; } } + .search-label .search-icon { + width: 1.2rem; + height: 1.2rem; + align-self: center; + color: #959396; } + +.search-results { + position: absolute; + left: 0; + display: none; + width: 100%; + max-height: calc(100% - 4rem); + overflow-y: auto; + background-color: #fff; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } + @media (min-width: 50rem) { + .search-results { + top: 100%; + width: 33.5rem; + max-height: calc(100vh - 200%) !important; } } + +.search-results-list { + padding-left: 0; + margin-bottom: 0.25rem; + list-style: none; + font-size: 0.875rem !important; } + @media (min-width: 31.25rem) { + .search-results-list { + font-size: 1rem !important; } } + @media (min-width: 50rem) { + .search-results-list { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .search-results-list { + font-size: 0.875rem !important; } } + +.search-results-list-item { + padding: 0; + margin: 0; } + +.search-result { + display: block; + padding: 0.25rem 0.75rem; } + .search-result:hover, .search-result.active { + background-color: #ebedf5; } + +.search-result-title { + display: block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; } + @media (min-width: 31.25rem) { + .search-result-title { + display: inline-block; + width: 40%; + padding-right: 0.5rem; + vertical-align: top; } } + +.search-result-doc { + display: flex; + align-items: center; + word-wrap: break-word; } + .search-result-doc.search-result-doc-parent { + opacity: 0.5; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.875rem !important; } } + @media (min-width: 50rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.6875rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.75rem !important; } } + + .search-result-doc .search-result-icon { + width: 1rem; + height: 1rem; + margin-right: 0.5rem; + color: #d41b2c; + flex-shrink: 0; } + .search-result-doc .search-result-doc-title { + overflow: auto; } + +.search-result-section { + margin-left: 1.5rem; + word-wrap: break-word; } + +.search-result-rel-url { + display: block; + margin-left: 1.5rem; + overflow: hidden; + color: #959396; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.5625rem !important; } + @media (min-width: 31.25rem) { + .search-result-rel-url { + font-size: 0.625rem !important; } } + +.search-result-previews { + display: block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + margin-left: 0.5rem; + color: #959396; + word-wrap: break-word; + border-left: 1px solid; + border-left-color: #eeebee; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .search-result-previews { + font-size: 0.75rem !important; } } + @media (min-width: 31.25rem) { + .search-result-previews { + display: inline-block; + width: 60%; + padding-left: 0.5rem; + margin-left: 0; + vertical-align: top; } } + +.search-result-preview + .search-result-preview { + margin-top: 0.25rem; } + +.search-result-highlight { + font-weight: bold; } + +.search-no-result { + padding: 0.5rem 0.75rem; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .search-no-result { + font-size: 0.875rem !important; } } + +.search-button { + position: fixed; + right: 1rem; + bottom: 1rem; + display: flex; + width: 3.5rem; + height: 3.5rem; + background-color: #fff; + border: 1px solid rgba(212, 27, 44, 0.3); + border-radius: 1.75rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + align-items: center; + justify-content: center; } + +.search-overlay { + position: fixed; + top: 0; + left: 0; + z-index: 1; + width: 0; + height: 0; + background-color: rgba(0, 0, 0, 0.3); + opacity: 0; + transition: opacity ease 400ms, width 0s 400ms, height 0s 400ms; } + +.search-active .search { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 0; } +.search-active .search-input-wrap { + height: 4rem; + border-radius: 0; } + @media (min-width: 50rem) { + .search-active .search-input-wrap { + width: 33.5rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } } +.search-active .search-input { + background-color: #fff; } + @media (min-width: 50rem) { + .search-active .search-input { + padding-left: 2.3rem; } } +@media (min-width: 50rem) { + .search-active .search-label { + padding-left: 0.6rem; } } +.search-active .search-results { + display: block; } +.search-active .search-overlay { + width: 100%; + height: 100%; + opacity: 1; + transition: opacity ease 400ms, width 0s, height 0s; } +@media (min-width: 50rem) { + .search-active .main { + position: fixed; + right: 0; + left: 0; } } +.search-active .main-header { + padding-top: 4rem; } + @media (min-width: 50rem) { + .search-active .main-header { + padding-top: 0; } } + +.table-wrapper { + display: block; + width: 100%; + max-width: 100%; + margin-bottom: 1.5rem; + overflow-x: auto; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } + +table { + display: table; + min-width: 100%; + border-collapse: separate; } + +th, +td { + font-size: 0.75rem !important; + min-width: 7.5rem; + padding: 0.5rem 0.75rem; + background-color: #fff; + border-bottom: 1px solid rgba(238, 235, 238, 0.5); + border-left: 1px solid #eeebee; } + @media (min-width: 31.25rem) { + th, + td { + font-size: 0.875rem !important; } } + th:first-of-type, + td:first-of-type { + border-left: 0; } + +tbody tr:last-of-type th, +tbody tr:last-of-type td { + border-bottom: 0; } +tbody tr:last-of-type td { + padding-bottom: 0.75rem; } + +thead th { + border-bottom: 1px solid #eeebee; } + +:not(pre, figure) > code { + padding: 0.2em 0.15em; + font-weight: 400; + background-color: #f5f6fa; + border: 1px solid #eeebee; + border-radius: 4px; } + +a:visited code { + border-color: #eeebee; } + +div.highlighter-rouge, +div.listingblock > div.content, +figure.highlight { + margin-top: 0; + margin-bottom: 0.75rem; + background-color: #f5f6fa; + border-radius: 4px; + box-shadow: none; + -webkit-overflow-scrolling: touch; + position: relative; + padding: 0; } + div.highlighter-rouge > button, + div.listingblock > div.content > button, + figure.highlight > button { + width: 0.75rem; + opacity: 0; + position: absolute; + top: 0; + right: 0; + border: 0.75rem solid #f5f6fa; + background-color: #f5f6fa; + color: #5c5962; + box-sizing: content-box; } + div.highlighter-rouge > button svg, + div.listingblock > div.content > button svg, + figure.highlight > button svg { + fill: #5c5962; } + div.highlighter-rouge > button:active, + div.listingblock > div.content > button:active, + figure.highlight > button:active { + text-decoration: none; + outline: none; + opacity: 1; } + div.highlighter-rouge > button:focus, + div.listingblock > div.content > button:focus, + figure.highlight > button:focus { + opacity: 1; } + div.highlighter-rouge:hover > button, + div.listingblock > div.content:hover > button, + figure.highlight:hover > button { + cursor: copy; + opacity: 1; } + +div.highlighter-rouge div.highlight { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } +div.highlighter-rouge pre.highlight, +div.highlighter-rouge code { + padding: 0; + margin: 0; + border: 0; } + +div.listingblock { + margin-top: 0; + margin-bottom: 0.75rem; } + div.listingblock div.content { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } + div.listingblock div.content > pre, + div.listingblock code { + padding: 0; + margin: 0; + border: 0; } + +figure.highlight pre, +figure.highlight :not(pre) > code { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } + +.highlight .table-wrapper { + padding: 0.75rem 0; + margin: 0; + border: 0; + box-shadow: none; } + .highlight .table-wrapper td, + .highlight .table-wrapper pre { + font-size: 0.6875rem !important; + min-width: 0; + padding: 0; + background-color: #f5f6fa; + border: 0; } + @media (min-width: 31.25rem) { + .highlight .table-wrapper td, + .highlight .table-wrapper pre { + font-size: 0.75rem !important; } } + .highlight .table-wrapper td.gl { + width: 1em; + padding-right: 0.75rem; + padding-left: 0.75rem; } + .highlight .table-wrapper pre { + margin: 0; + line-height: 2; } + +.code-example, +.listingblock > .title { + padding: 0.75rem; + margin-bottom: 0.75rem; + overflow: auto; + border: 1px solid #eeebee; + border-radius: 4px; } + .code-example + .highlighter-rouge, + .code-example + .sectionbody .listingblock, + .code-example + .content, + .code-example + figure.highlight, + .listingblock > .title + .highlighter-rouge, + .listingblock > .title + .sectionbody .listingblock, + .listingblock > .title + .content, + .listingblock > .title + figure.highlight { + position: relative; + margin-top: -1rem; + border-right: 1px solid #eeebee; + border-bottom: 1px solid #eeebee; + border-left: 1px solid #eeebee; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +code.language-mermaid { + padding: 0; + background-color: inherit; + border: 0; } + +.highlight, +pre.highlight { + background: #f5f6fa; + color: #5c5962; } + +.highlight pre { + background: #f5f6fa; } + +.text-grey-dk-000, .staffer .staffer-meta { + color: #959396 !important; } + +.text-grey-dk-100, .staffer .staffer-pronouns, div.highlighter-rouge[overlay]::after { + color: #5c5962 !important; } + +.text-grey-dk-200 { + color: #44434d !important; } + +.text-grey-dk-250 { + color: #302d36 !important; } + +.text-grey-dk-300 { + color: #27262b !important; } + +.text-grey-lt-000 { + color: #f5f6fa !important; } + +.text-grey-lt-100 { + color: #eeebee !important; } + +.text-grey-lt-200 { + color: #ecebed !important; } + +.text-grey-lt-300 { + color: #e6e1e8 !important; } + +.text-blue-000 { + color: #2c84fa !important; } + +.text-blue-100 { + color: #2869e6 !important; } + +.text-blue-200 { + color: #264caf !important; } + +.text-blue-300 { + color: #183385 !important; } + +.text-green-000 { + color: #41d693 !important; } + +.text-green-100 { + color: #11b584 !important; } + +.text-green-200 { + color: #009c7b !important; } + +.text-green-300 { + color: #026e57 !important; } + +.text-purple-000 { + color: #7253ed !important; } + +.text-purple-100 { + color: #5e41d0 !important; } + +.text-purple-200 { + color: #4e26af !important; } + +.text-purple-300 { + color: #381885 !important; } + +.text-yellow-000 { + color: #ffeb82 !important; } + +.text-yellow-100 { + color: #fadf50 !important; } + +.text-yellow-200 { + color: #f7d12e !important; } + +.text-yellow-300 { + color: #e7af06 !important; } + +.text-red-000 { + color: #f77e7e !important; } + +.text-red-100 { + color: #f96e65 !important; } + +.text-red-200 { + color: #e94c4c !important; } + +.text-red-300 { + color: #dd2e2e !important; } + +.bg-grey-dk-000 { + background-color: #959396 !important; } + +.bg-grey-dk-100 { + background-color: #5c5962 !important; } + +.bg-grey-dk-200 { + background-color: #44434d !important; } + +.bg-grey-dk-250 { + background-color: #302d36 !important; } + +.bg-grey-dk-300 { + background-color: #27262b !important; } + +.bg-grey-lt-000 { + background-color: #f5f6fa !important; } + +.bg-grey-lt-100 { + background-color: #eeebee !important; } + +.bg-grey-lt-200, .staffer .staffer-pronouns { + background-color: #ecebed !important; } + +.bg-grey-lt-300 { + background-color: #e6e1e8 !important; } + +.bg-blue-000 { + background-color: #2c84fa !important; } + +.bg-blue-100 { + background-color: #2869e6 !important; } + +.bg-blue-200 { + background-color: #264caf !important; } + +.bg-blue-300 { + background-color: #183385 !important; } + +.bg-green-000 { + background-color: #41d693 !important; } + +.bg-green-100 { + background-color: #11b584 !important; } + +.bg-green-200 { + background-color: #009c7b !important; } + +.bg-green-300 { + background-color: #026e57 !important; } + +.bg-purple-000 { + background-color: #7253ed !important; } + +.bg-purple-100 { + background-color: #5e41d0 !important; } + +.bg-purple-200 { + background-color: #4e26af !important; } + +.bg-purple-300 { + background-color: #381885 !important; } + +.bg-yellow-000 { + background-color: #ffeb82 !important; } + +.bg-yellow-100 { + background-color: #fadf50 !important; } + +.bg-yellow-200 { + background-color: #f7d12e !important; } + +.bg-yellow-300 { + background-color: #e7af06 !important; } + +.bg-red-000 { + background-color: #f77e7e !important; } + +.bg-red-100 { + background-color: #f96e65 !important; } + +.bg-red-200 { + background-color: #e94c4c !important; } + +.bg-red-300 { + background-color: #dd2e2e !important; } + +.d-block { + display: block !important; } + +.d-flex { + display: flex !important; } + +.d-inline { + display: inline !important; } + +.d-inline-block { + display: inline-block !important; } + +.d-none { + display: none !important; } + +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +.float-left { + float: left !important; } + +.float-right { + float: right !important; } + +.flex-justify-start { + justify-content: flex-start !important; } + +.flex-justify-end { + justify-content: flex-end !important; } + +.flex-justify-between { + justify-content: space-between !important; } + +.flex-justify-around { + justify-content: space-around !important; } + +.v-align-baseline { + vertical-align: baseline !important; } + +.v-align-bottom { + vertical-align: bottom !important; } + +.v-align-middle { + vertical-align: middle !important; } + +.v-align-text-bottom { + vertical-align: text-bottom !important; } + +.v-align-text-top { + vertical-align: text-top !important; } + +.v-align-top { + vertical-align: top !important; } + +.fs-1 { + font-size: 0.5625rem !important; } + @media (min-width: 31.25rem) { + .fs-1 { + font-size: 0.625rem !important; } } + +.fs-2, .bldschedule .schedule-time, .bldschedule .schedule-event .time, +.bldschedule .schedule-event .location { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .fs-2, .bldschedule .schedule-time, .bldschedule .schedule-event .time, + .bldschedule .schedule-event .location { + font-size: 0.75rem !important; } } + +.fs-3, .bldschedule .schedule-event .name { + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .fs-3, .bldschedule .schedule-event .name { + font-size: 0.875rem !important; } } + +.fs-4 { + font-size: 0.875rem !important; } + @media (min-width: 31.25rem) { + .fs-4 { + font-size: 1rem !important; } } + +.fs-5 { + font-size: 1rem !important; } + @media (min-width: 31.25rem) { + .fs-5 { + font-size: 1.125rem !important; } } + +.fs-6 { + font-size: 1.125rem !important; } + @media (min-width: 31.25rem) { + .fs-6 { + font-size: 1.5rem !important; + line-height: 1.25; } } + +.fs-7 { + font-size: 1.5rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-7 { + font-size: 2rem !important; } } + +.fs-8 { + font-size: 2rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-8 { + font-size: 2.25rem !important; } } + +.fs-9 { + font-size: 2.25rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-9 { + font-size: 2.625rem !important; } } + +.fs-10 { + font-size: 2.625rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-10 { + font-size: 3rem !important; } } + +.fw-300 { + font-weight: 300 !important; } + +.fw-400 { + font-weight: 400 !important; } + +.fw-500 { + font-weight: 500 !important; } + +.fw-700, .bldschedule .schedule-event .name { + font-weight: 700 !important; } + +.lh-0 { + line-height: 0 !important; } + +.lh-default { + line-height: 1.4; } + +.lh-tight { + line-height: 1.25; } + +.ls-5 { + letter-spacing: 0.05em !important; } + +.ls-10 { + letter-spacing: 0.1em !important; } + +.ls-0 { + letter-spacing: 0 !important; } + +.text-uppercase { + text-transform: uppercase !important; } + +.list-style-none { + padding: 0 !important; + margin: 0 !important; + list-style: none !important; } + .list-style-none li::before { + display: none !important; } + +.mx-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-0 { + margin: 0 !important; } + +.mt-0 { + margin-top: 0 !important; } + +.mr-0 { + margin-right: 0 !important; } + +.mb-0 { + margin-bottom: 0 !important; } + +.ml-0 { + margin-left: 0 !important; } + +.mx-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + +.my-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + +.mxn-0 { + margin-right: -0 !important; + margin-left: -0 !important; } + +.mx-0-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-1 { + margin: 0.25rem !important; } + +.mt-1 { + margin-top: 0.25rem !important; } + +.mr-1 { + margin-right: 0.25rem !important; } + +.mb-1 { + margin-bottom: 0.25rem !important; } + +.ml-1 { + margin-left: 0.25rem !important; } + +.mx-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + +.my-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + +.mxn-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } + +.mx-1-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-2 { + margin: 0.5rem !important; } + +.mt-2 { + margin-top: 0.5rem !important; } + +.mr-2 { + margin-right: 0.5rem !important; } + +.mb-2 { + margin-bottom: 0.5rem !important; } + +.ml-2 { + margin-left: 0.5rem !important; } + +.mx-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + +.my-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + +.mxn-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } + +.mx-2-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-3 { + margin: 0.75rem !important; } + +.mt-3 { + margin-top: 0.75rem !important; } + +.mr-3 { + margin-right: 0.75rem !important; } + +.mb-3 { + margin-bottom: 0.75rem !important; } + +.ml-3 { + margin-left: 0.75rem !important; } + +.mx-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + +.my-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + +.mxn-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } + +.mx-3-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-4 { + margin: 1rem !important; } + +.mt-4 { + margin-top: 1rem !important; } + +.mr-4 { + margin-right: 1rem !important; } + +.mb-4 { + margin-bottom: 1rem !important; } + +.ml-4 { + margin-left: 1rem !important; } + +.mx-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + +.my-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + +.mxn-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } + +.mx-4-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-5 { + margin: 1.5rem !important; } + +.mt-5 { + margin-top: 1.5rem !important; } + +.mr-5 { + margin-right: 1.5rem !important; } + +.mb-5 { + margin-bottom: 1.5rem !important; } + +.ml-5 { + margin-left: 1.5rem !important; } + +.mx-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + +.my-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + +.mxn-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } + +.mx-5-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-6 { + margin: 2rem !important; } + +.mt-6 { + margin-top: 2rem !important; } + +.mr-6 { + margin-right: 2rem !important; } + +.mb-6 { + margin-bottom: 2rem !important; } + +.ml-6 { + margin-left: 2rem !important; } + +.mx-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + +.my-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + +.mxn-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } + +.mx-6-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-7 { + margin: 2.5rem !important; } + +.mt-7 { + margin-top: 2.5rem !important; } + +.mr-7 { + margin-right: 2.5rem !important; } + +.mb-7 { + margin-bottom: 2.5rem !important; } + +.ml-7 { + margin-left: 2.5rem !important; } + +.mx-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + +.my-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + +.mxn-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } + +.mx-7-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-8 { + margin: 3rem !important; } + +.mt-8 { + margin-top: 3rem !important; } + +.mr-8 { + margin-right: 3rem !important; } + +.mb-8 { + margin-bottom: 3rem !important; } + +.ml-8 { + margin-left: 3rem !important; } + +.mx-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + +.my-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + +.mxn-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } + +.mx-8-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-9 { + margin: 3.5rem !important; } + +.mt-9 { + margin-top: 3.5rem !important; } + +.mr-9 { + margin-right: 3.5rem !important; } + +.mb-9 { + margin-bottom: 3.5rem !important; } + +.ml-9 { + margin-left: 3.5rem !important; } + +.mx-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + +.my-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + +.mxn-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } + +.mx-9-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-10 { + margin: 4rem !important; } + +.mt-10 { + margin-top: 4rem !important; } + +.mr-10 { + margin-right: 4rem !important; } + +.mb-10 { + margin-bottom: 4rem !important; } + +.ml-10 { + margin-left: 4rem !important; } + +.mx-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + +.my-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + +.mxn-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } + +.mx-10-auto { + margin-right: auto !important; + margin-left: auto !important; } + +@media (min-width: 20rem) { + .m-xs-0 { + margin: 0 !important; } + + .mt-xs-0 { + margin-top: 0 !important; } + + .mr-xs-0 { + margin-right: 0 !important; } + + .mb-xs-0 { + margin-bottom: 0 !important; } + + .ml-xs-0 { + margin-left: 0 !important; } + + .mx-xs-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-xs-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-xs-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 20rem) { + .m-xs-1 { + margin: 0.25rem !important; } + + .mt-xs-1 { + margin-top: 0.25rem !important; } + + .mr-xs-1 { + margin-right: 0.25rem !important; } + + .mb-xs-1 { + margin-bottom: 0.25rem !important; } + + .ml-xs-1 { + margin-left: 0.25rem !important; } + + .mx-xs-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-xs-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-xs-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 20rem) { + .m-xs-2 { + margin: 0.5rem !important; } + + .mt-xs-2 { + margin-top: 0.5rem !important; } + + .mr-xs-2 { + margin-right: 0.5rem !important; } + + .mb-xs-2 { + margin-bottom: 0.5rem !important; } + + .ml-xs-2 { + margin-left: 0.5rem !important; } + + .mx-xs-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-xs-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-xs-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-3 { + margin: 0.75rem !important; } + + .mt-xs-3 { + margin-top: 0.75rem !important; } + + .mr-xs-3 { + margin-right: 0.75rem !important; } + + .mb-xs-3 { + margin-bottom: 0.75rem !important; } + + .ml-xs-3 { + margin-left: 0.75rem !important; } + + .mx-xs-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-xs-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-xs-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 20rem) { + .m-xs-4 { + margin: 1rem !important; } + + .mt-xs-4 { + margin-top: 1rem !important; } + + .mr-xs-4 { + margin-right: 1rem !important; } + + .mb-xs-4 { + margin-bottom: 1rem !important; } + + .ml-xs-4 { + margin-left: 1rem !important; } + + .mx-xs-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-xs-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-xs-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 20rem) { + .m-xs-5 { + margin: 1.5rem !important; } + + .mt-xs-5 { + margin-top: 1.5rem !important; } + + .mr-xs-5 { + margin-right: 1.5rem !important; } + + .mb-xs-5 { + margin-bottom: 1.5rem !important; } + + .ml-xs-5 { + margin-left: 1.5rem !important; } + + .mx-xs-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-xs-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-xs-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-6 { + margin: 2rem !important; } + + .mt-xs-6 { + margin-top: 2rem !important; } + + .mr-xs-6 { + margin-right: 2rem !important; } + + .mb-xs-6 { + margin-bottom: 2rem !important; } + + .ml-xs-6 { + margin-left: 2rem !important; } + + .mx-xs-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-xs-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-xs-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 20rem) { + .m-xs-7 { + margin: 2.5rem !important; } + + .mt-xs-7 { + margin-top: 2.5rem !important; } + + .mr-xs-7 { + margin-right: 2.5rem !important; } + + .mb-xs-7 { + margin-bottom: 2.5rem !important; } + + .ml-xs-7 { + margin-left: 2.5rem !important; } + + .mx-xs-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-xs-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-xs-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-8 { + margin: 3rem !important; } + + .mt-xs-8 { + margin-top: 3rem !important; } + + .mr-xs-8 { + margin-right: 3rem !important; } + + .mb-xs-8 { + margin-bottom: 3rem !important; } + + .ml-xs-8 { + margin-left: 3rem !important; } + + .mx-xs-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-xs-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-xs-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 20rem) { + .m-xs-9 { + margin: 3.5rem !important; } + + .mt-xs-9 { + margin-top: 3.5rem !important; } + + .mr-xs-9 { + margin-right: 3.5rem !important; } + + .mb-xs-9 { + margin-bottom: 3.5rem !important; } + + .ml-xs-9 { + margin-left: 3.5rem !important; } + + .mx-xs-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-xs-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-xs-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-10 { + margin: 4rem !important; } + + .mt-xs-10 { + margin-top: 4rem !important; } + + .mr-xs-10 { + margin-right: 4rem !important; } + + .mb-xs-10 { + margin-bottom: 4rem !important; } + + .ml-xs-10 { + margin-left: 4rem !important; } + + .mx-xs-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-xs-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-xs-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-0 { + margin: 0 !important; } + + .mt-sm-0 { + margin-top: 0 !important; } + + .mr-sm-0 { + margin-right: 0 !important; } + + .mb-sm-0 { + margin-bottom: 0 !important; } + + .ml-sm-0 { + margin-left: 0 !important; } + + .mx-sm-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-sm-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 31.25rem) { + .m-sm-1 { + margin: 0.25rem !important; } + + .mt-sm-1 { + margin-top: 0.25rem !important; } + + .mr-sm-1 { + margin-right: 0.25rem !important; } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; } + + .ml-sm-1 { + margin-left: 0.25rem !important; } + + .mx-sm-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-sm-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-2 { + margin: 0.5rem !important; } + + .mt-sm-2 { + margin-top: 0.5rem !important; } + + .mr-sm-2 { + margin-right: 0.5rem !important; } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; } + + .ml-sm-2 { + margin-left: 0.5rem !important; } + + .mx-sm-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-sm-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-3 { + margin: 0.75rem !important; } + + .mt-sm-3 { + margin-top: 0.75rem !important; } + + .mr-sm-3 { + margin-right: 0.75rem !important; } + + .mb-sm-3 { + margin-bottom: 0.75rem !important; } + + .ml-sm-3 { + margin-left: 0.75rem !important; } + + .mx-sm-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-sm-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-sm-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-4 { + margin: 1rem !important; } + + .mt-sm-4 { + margin-top: 1rem !important; } + + .mr-sm-4 { + margin-right: 1rem !important; } + + .mb-sm-4 { + margin-bottom: 1rem !important; } + + .ml-sm-4 { + margin-left: 1rem !important; } + + .mx-sm-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-sm-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-sm-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-5 { + margin: 1.5rem !important; } + + .mt-sm-5 { + margin-top: 1.5rem !important; } + + .mr-sm-5 { + margin-right: 1.5rem !important; } + + .mb-sm-5 { + margin-bottom: 1.5rem !important; } + + .ml-sm-5 { + margin-left: 1.5rem !important; } + + .mx-sm-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-sm-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-sm-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-6 { + margin: 2rem !important; } + + .mt-sm-6 { + margin-top: 2rem !important; } + + .mr-sm-6 { + margin-right: 2rem !important; } + + .mb-sm-6 { + margin-bottom: 2rem !important; } + + .ml-sm-6 { + margin-left: 2rem !important; } + + .mx-sm-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-sm-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-sm-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-7 { + margin: 2.5rem !important; } + + .mt-sm-7 { + margin-top: 2.5rem !important; } + + .mr-sm-7 { + margin-right: 2.5rem !important; } + + .mb-sm-7 { + margin-bottom: 2.5rem !important; } + + .ml-sm-7 { + margin-left: 2.5rem !important; } + + .mx-sm-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-sm-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-sm-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-8 { + margin: 3rem !important; } + + .mt-sm-8 { + margin-top: 3rem !important; } + + .mr-sm-8 { + margin-right: 3rem !important; } + + .mb-sm-8 { + margin-bottom: 3rem !important; } + + .ml-sm-8 { + margin-left: 3rem !important; } + + .mx-sm-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-sm-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-sm-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-9 { + margin: 3.5rem !important; } + + .mt-sm-9 { + margin-top: 3.5rem !important; } + + .mr-sm-9 { + margin-right: 3.5rem !important; } + + .mb-sm-9 { + margin-bottom: 3.5rem !important; } + + .ml-sm-9 { + margin-left: 3.5rem !important; } + + .mx-sm-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-sm-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-sm-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-10 { + margin: 4rem !important; } + + .mt-sm-10 { + margin-top: 4rem !important; } + + .mr-sm-10 { + margin-right: 4rem !important; } + + .mb-sm-10 { + margin-bottom: 4rem !important; } + + .ml-sm-10 { + margin-left: 4rem !important; } + + .mx-sm-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-sm-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-sm-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 50rem) { + .m-md-0 { + margin: 0 !important; } + + .mt-md-0 { + margin-top: 0 !important; } + + .mr-md-0 { + margin-right: 0 !important; } + + .mb-md-0 { + margin-bottom: 0 !important; } + + .ml-md-0 { + margin-left: 0 !important; } + + .mx-md-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-md-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 50rem) { + .m-md-1 { + margin: 0.25rem !important; } + + .mt-md-1 { + margin-top: 0.25rem !important; } + + .mr-md-1 { + margin-right: 0.25rem !important; } + + .mb-md-1 { + margin-bottom: 0.25rem !important; } + + .ml-md-1 { + margin-left: 0.25rem !important; } + + .mx-md-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-md-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 50rem) { + .m-md-2 { + margin: 0.5rem !important; } + + .mt-md-2 { + margin-top: 0.5rem !important; } + + .mr-md-2 { + margin-right: 0.5rem !important; } + + .mb-md-2 { + margin-bottom: 0.5rem !important; } + + .ml-md-2 { + margin-left: 0.5rem !important; } + + .mx-md-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-md-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 50rem) { + .m-md-3 { + margin: 0.75rem !important; } + + .mt-md-3 { + margin-top: 0.75rem !important; } + + .mr-md-3 { + margin-right: 0.75rem !important; } + + .mb-md-3 { + margin-bottom: 0.75rem !important; } + + .ml-md-3 { + margin-left: 0.75rem !important; } + + .mx-md-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-md-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-md-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 50rem) { + .m-md-4 { + margin: 1rem !important; } + + .mt-md-4 { + margin-top: 1rem !important; } + + .mr-md-4 { + margin-right: 1rem !important; } + + .mb-md-4 { + margin-bottom: 1rem !important; } + + .ml-md-4 { + margin-left: 1rem !important; } + + .mx-md-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-md-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-md-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 50rem) { + .m-md-5 { + margin: 1.5rem !important; } + + .mt-md-5 { + margin-top: 1.5rem !important; } + + .mr-md-5 { + margin-right: 1.5rem !important; } + + .mb-md-5 { + margin-bottom: 1.5rem !important; } + + .ml-md-5 { + margin-left: 1.5rem !important; } + + .mx-md-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-md-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-md-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 50rem) { + .m-md-6 { + margin: 2rem !important; } + + .mt-md-6 { + margin-top: 2rem !important; } + + .mr-md-6 { + margin-right: 2rem !important; } + + .mb-md-6 { + margin-bottom: 2rem !important; } + + .ml-md-6 { + margin-left: 2rem !important; } + + .mx-md-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-md-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-md-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 50rem) { + .m-md-7 { + margin: 2.5rem !important; } + + .mt-md-7 { + margin-top: 2.5rem !important; } + + .mr-md-7 { + margin-right: 2.5rem !important; } + + .mb-md-7 { + margin-bottom: 2.5rem !important; } + + .ml-md-7 { + margin-left: 2.5rem !important; } + + .mx-md-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-md-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-md-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 50rem) { + .m-md-8 { + margin: 3rem !important; } + + .mt-md-8 { + margin-top: 3rem !important; } + + .mr-md-8 { + margin-right: 3rem !important; } + + .mb-md-8 { + margin-bottom: 3rem !important; } + + .ml-md-8 { + margin-left: 3rem !important; } + + .mx-md-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-md-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-md-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 50rem) { + .m-md-9 { + margin: 3.5rem !important; } + + .mt-md-9 { + margin-top: 3.5rem !important; } + + .mr-md-9 { + margin-right: 3.5rem !important; } + + .mb-md-9 { + margin-bottom: 3.5rem !important; } + + .ml-md-9 { + margin-left: 3.5rem !important; } + + .mx-md-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-md-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-md-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 50rem) { + .m-md-10 { + margin: 4rem !important; } + + .mt-md-10 { + margin-top: 4rem !important; } + + .mr-md-10 { + margin-right: 4rem !important; } + + .mb-md-10 { + margin-bottom: 4rem !important; } + + .ml-md-10 { + margin-left: 4rem !important; } + + .mx-md-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-md-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-md-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-0 { + margin: 0 !important; } + + .mt-lg-0 { + margin-top: 0 !important; } + + .mr-lg-0 { + margin-right: 0 !important; } + + .mb-lg-0 { + margin-bottom: 0 !important; } + + .ml-lg-0 { + margin-left: 0 !important; } + + .mx-lg-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-lg-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 66.5rem) { + .m-lg-1 { + margin: 0.25rem !important; } + + .mt-lg-1 { + margin-top: 0.25rem !important; } + + .mr-lg-1 { + margin-right: 0.25rem !important; } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; } + + .ml-lg-1 { + margin-left: 0.25rem !important; } + + .mx-lg-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-lg-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-2 { + margin: 0.5rem !important; } + + .mt-lg-2 { + margin-top: 0.5rem !important; } + + .mr-lg-2 { + margin-right: 0.5rem !important; } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; } + + .ml-lg-2 { + margin-left: 0.5rem !important; } + + .mx-lg-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-lg-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-3 { + margin: 0.75rem !important; } + + .mt-lg-3 { + margin-top: 0.75rem !important; } + + .mr-lg-3 { + margin-right: 0.75rem !important; } + + .mb-lg-3 { + margin-bottom: 0.75rem !important; } + + .ml-lg-3 { + margin-left: 0.75rem !important; } + + .mx-lg-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-lg-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-lg-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-4 { + margin: 1rem !important; } + + .mt-lg-4 { + margin-top: 1rem !important; } + + .mr-lg-4 { + margin-right: 1rem !important; } + + .mb-lg-4 { + margin-bottom: 1rem !important; } + + .ml-lg-4 { + margin-left: 1rem !important; } + + .mx-lg-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-lg-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-lg-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-5 { + margin: 1.5rem !important; } + + .mt-lg-5 { + margin-top: 1.5rem !important; } + + .mr-lg-5 { + margin-right: 1.5rem !important; } + + .mb-lg-5 { + margin-bottom: 1.5rem !important; } + + .ml-lg-5 { + margin-left: 1.5rem !important; } + + .mx-lg-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-lg-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-lg-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-6 { + margin: 2rem !important; } + + .mt-lg-6 { + margin-top: 2rem !important; } + + .mr-lg-6 { + margin-right: 2rem !important; } + + .mb-lg-6 { + margin-bottom: 2rem !important; } + + .ml-lg-6 { + margin-left: 2rem !important; } + + .mx-lg-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-lg-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-lg-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-7 { + margin: 2.5rem !important; } + + .mt-lg-7 { + margin-top: 2.5rem !important; } + + .mr-lg-7 { + margin-right: 2.5rem !important; } + + .mb-lg-7 { + margin-bottom: 2.5rem !important; } + + .ml-lg-7 { + margin-left: 2.5rem !important; } + + .mx-lg-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-lg-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-lg-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-8 { + margin: 3rem !important; } + + .mt-lg-8 { + margin-top: 3rem !important; } + + .mr-lg-8 { + margin-right: 3rem !important; } + + .mb-lg-8 { + margin-bottom: 3rem !important; } + + .ml-lg-8 { + margin-left: 3rem !important; } + + .mx-lg-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-lg-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-lg-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-9 { + margin: 3.5rem !important; } + + .mt-lg-9 { + margin-top: 3.5rem !important; } + + .mr-lg-9 { + margin-right: 3.5rem !important; } + + .mb-lg-9 { + margin-bottom: 3.5rem !important; } + + .ml-lg-9 { + margin-left: 3.5rem !important; } + + .mx-lg-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-lg-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-lg-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-10 { + margin: 4rem !important; } + + .mt-lg-10 { + margin-top: 4rem !important; } + + .mr-lg-10 { + margin-right: 4rem !important; } + + .mb-lg-10 { + margin-bottom: 4rem !important; } + + .ml-lg-10 { + margin-left: 4rem !important; } + + .mx-lg-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-lg-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-lg-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-0 { + margin: 0 !important; } + + .mt-xl-0 { + margin-top: 0 !important; } + + .mr-xl-0 { + margin-right: 0 !important; } + + .mb-xl-0 { + margin-bottom: 0 !important; } + + .ml-xl-0 { + margin-left: 0 !important; } + + .mx-xl-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-xl-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 87.5rem) { + .m-xl-1 { + margin: 0.25rem !important; } + + .mt-xl-1 { + margin-top: 0.25rem !important; } + + .mr-xl-1 { + margin-right: 0.25rem !important; } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; } + + .ml-xl-1 { + margin-left: 0.25rem !important; } + + .mx-xl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-xl-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-2 { + margin: 0.5rem !important; } + + .mt-xl-2 { + margin-top: 0.5rem !important; } + + .mr-xl-2 { + margin-right: 0.5rem !important; } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; } + + .ml-xl-2 { + margin-left: 0.5rem !important; } + + .mx-xl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-xl-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-3 { + margin: 0.75rem !important; } + + .mt-xl-3 { + margin-top: 0.75rem !important; } + + .mr-xl-3 { + margin-right: 0.75rem !important; } + + .mb-xl-3 { + margin-bottom: 0.75rem !important; } + + .ml-xl-3 { + margin-left: 0.75rem !important; } + + .mx-xl-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-xl-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-xl-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-4 { + margin: 1rem !important; } + + .mt-xl-4 { + margin-top: 1rem !important; } + + .mr-xl-4 { + margin-right: 1rem !important; } + + .mb-xl-4 { + margin-bottom: 1rem !important; } + + .ml-xl-4 { + margin-left: 1rem !important; } + + .mx-xl-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-xl-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-xl-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-5 { + margin: 1.5rem !important; } + + .mt-xl-5 { + margin-top: 1.5rem !important; } + + .mr-xl-5 { + margin-right: 1.5rem !important; } + + .mb-xl-5 { + margin-bottom: 1.5rem !important; } + + .ml-xl-5 { + margin-left: 1.5rem !important; } + + .mx-xl-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-xl-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-xl-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-6 { + margin: 2rem !important; } + + .mt-xl-6 { + margin-top: 2rem !important; } + + .mr-xl-6 { + margin-right: 2rem !important; } + + .mb-xl-6 { + margin-bottom: 2rem !important; } + + .ml-xl-6 { + margin-left: 2rem !important; } + + .mx-xl-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-xl-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-xl-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-7 { + margin: 2.5rem !important; } + + .mt-xl-7 { + margin-top: 2.5rem !important; } + + .mr-xl-7 { + margin-right: 2.5rem !important; } + + .mb-xl-7 { + margin-bottom: 2.5rem !important; } + + .ml-xl-7 { + margin-left: 2.5rem !important; } + + .mx-xl-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-xl-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-xl-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-8 { + margin: 3rem !important; } + + .mt-xl-8 { + margin-top: 3rem !important; } + + .mr-xl-8 { + margin-right: 3rem !important; } + + .mb-xl-8 { + margin-bottom: 3rem !important; } + + .ml-xl-8 { + margin-left: 3rem !important; } + + .mx-xl-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-xl-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-xl-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-9 { + margin: 3.5rem !important; } + + .mt-xl-9 { + margin-top: 3.5rem !important; } + + .mr-xl-9 { + margin-right: 3.5rem !important; } + + .mb-xl-9 { + margin-bottom: 3.5rem !important; } + + .ml-xl-9 { + margin-left: 3.5rem !important; } + + .mx-xl-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-xl-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-xl-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-10 { + margin: 4rem !important; } + + .mt-xl-10 { + margin-top: 4rem !important; } + + .mr-xl-10 { + margin-right: 4rem !important; } + + .mb-xl-10 { + margin-bottom: 4rem !important; } + + .ml-xl-10 { + margin-left: 4rem !important; } + + .mx-xl-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-xl-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-xl-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +.p-0 { + padding: 0 !important; } + +.pt-0 { + padding-top: 0 !important; } + +.pr-0 { + padding-right: 0 !important; } + +.pb-0 { + padding-bottom: 0 !important; } + +.pl-0 { + padding-left: 0 !important; } + +.px-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + +.py-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + +.p-1 { + padding: 0.25rem !important; } + +.pt-1 { + padding-top: 0.25rem !important; } + +.pr-1 { + padding-right: 0.25rem !important; } + +.pb-1 { + padding-bottom: 0.25rem !important; } + +.pl-1 { + padding-left: 0.25rem !important; } + +.px-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + +.py-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + +.p-2 { + padding: 0.5rem !important; } + +.pt-2 { + padding-top: 0.5rem !important; } + +.pr-2 { + padding-right: 0.5rem !important; } + +.pb-2 { + padding-bottom: 0.5rem !important; } + +.pl-2 { + padding-left: 0.5rem !important; } + +.px-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + +.py-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + +.p-3 { + padding: 0.75rem !important; } + +.pt-3 { + padding-top: 0.75rem !important; } + +.pr-3 { + padding-right: 0.75rem !important; } + +.pb-3 { + padding-bottom: 0.75rem !important; } + +.pl-3 { + padding-left: 0.75rem !important; } + +.px-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + +.py-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + +.p-4 { + padding: 1rem !important; } + +.pt-4 { + padding-top: 1rem !important; } + +.pr-4 { + padding-right: 1rem !important; } + +.pb-4 { + padding-bottom: 1rem !important; } + +.pl-4 { + padding-left: 1rem !important; } + +.px-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + +.py-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + +.p-5 { + padding: 1.5rem !important; } + +.pt-5 { + padding-top: 1.5rem !important; } + +.pr-5 { + padding-right: 1.5rem !important; } + +.pb-5 { + padding-bottom: 1.5rem !important; } + +.pl-5 { + padding-left: 1.5rem !important; } + +.px-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + +.py-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + +.p-6 { + padding: 2rem !important; } + +.pt-6 { + padding-top: 2rem !important; } + +.pr-6 { + padding-right: 2rem !important; } + +.pb-6 { + padding-bottom: 2rem !important; } + +.pl-6 { + padding-left: 2rem !important; } + +.px-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + +.py-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + +.p-7 { + padding: 2.5rem !important; } + +.pt-7 { + padding-top: 2.5rem !important; } + +.pr-7 { + padding-right: 2.5rem !important; } + +.pb-7 { + padding-bottom: 2.5rem !important; } + +.pl-7 { + padding-left: 2.5rem !important; } + +.px-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + +.py-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + +.p-8 { + padding: 3rem !important; } + +.pt-8 { + padding-top: 3rem !important; } + +.pr-8 { + padding-right: 3rem !important; } + +.pb-8 { + padding-bottom: 3rem !important; } + +.pl-8 { + padding-left: 3rem !important; } + +.px-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + +.py-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + +.p-9 { + padding: 3.5rem !important; } + +.pt-9 { + padding-top: 3.5rem !important; } + +.pr-9 { + padding-right: 3.5rem !important; } + +.pb-9 { + padding-bottom: 3.5rem !important; } + +.pl-9 { + padding-left: 3.5rem !important; } + +.px-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + +.py-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + +.p-10 { + padding: 4rem !important; } + +.pt-10 { + padding-top: 4rem !important; } + +.pr-10 { + padding-right: 4rem !important; } + +.pb-10 { + padding-bottom: 4rem !important; } + +.pl-10 { + padding-left: 4rem !important; } + +.px-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + +.py-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } + +@media (min-width: 20rem) { + .p-xs-0 { + padding: 0 !important; } + + .pt-xs-0 { + padding-top: 0 !important; } + + .pr-xs-0 { + padding-right: 0 !important; } + + .pb-xs-0 { + padding-bottom: 0 !important; } + + .pl-xs-0 { + padding-left: 0 !important; } + + .px-xs-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-xs-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-xs-1 { + padding: 0.25rem !important; } + + .pt-xs-1 { + padding-top: 0.25rem !important; } + + .pr-xs-1 { + padding-right: 0.25rem !important; } + + .pb-xs-1 { + padding-bottom: 0.25rem !important; } + + .pl-xs-1 { + padding-left: 0.25rem !important; } + + .px-xs-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-xs-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-xs-2 { + padding: 0.5rem !important; } + + .pt-xs-2 { + padding-top: 0.5rem !important; } + + .pr-xs-2 { + padding-right: 0.5rem !important; } + + .pb-xs-2 { + padding-bottom: 0.5rem !important; } + + .pl-xs-2 { + padding-left: 0.5rem !important; } + + .px-xs-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-xs-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-xs-3 { + padding: 0.75rem !important; } + + .pt-xs-3 { + padding-top: 0.75rem !important; } + + .pr-xs-3 { + padding-right: 0.75rem !important; } + + .pb-xs-3 { + padding-bottom: 0.75rem !important; } + + .pl-xs-3 { + padding-left: 0.75rem !important; } + + .px-xs-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-xs-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-xs-4 { + padding: 1rem !important; } + + .pt-xs-4 { + padding-top: 1rem !important; } + + .pr-xs-4 { + padding-right: 1rem !important; } + + .pb-xs-4 { + padding-bottom: 1rem !important; } + + .pl-xs-4 { + padding-left: 1rem !important; } + + .px-xs-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-xs-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-xs-5 { + padding: 1.5rem !important; } + + .pt-xs-5 { + padding-top: 1.5rem !important; } + + .pr-xs-5 { + padding-right: 1.5rem !important; } + + .pb-xs-5 { + padding-bottom: 1.5rem !important; } + + .pl-xs-5 { + padding-left: 1.5rem !important; } + + .px-xs-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-xs-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-xs-6 { + padding: 2rem !important; } + + .pt-xs-6 { + padding-top: 2rem !important; } + + .pr-xs-6 { + padding-right: 2rem !important; } + + .pb-xs-6 { + padding-bottom: 2rem !important; } + + .pl-xs-6 { + padding-left: 2rem !important; } + + .px-xs-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-xs-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-xs-7 { + padding: 2.5rem !important; } + + .pt-xs-7 { + padding-top: 2.5rem !important; } + + .pr-xs-7 { + padding-right: 2.5rem !important; } + + .pb-xs-7 { + padding-bottom: 2.5rem !important; } + + .pl-xs-7 { + padding-left: 2.5rem !important; } + + .px-xs-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-xs-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-xs-8 { + padding: 3rem !important; } + + .pt-xs-8 { + padding-top: 3rem !important; } + + .pr-xs-8 { + padding-right: 3rem !important; } + + .pb-xs-8 { + padding-bottom: 3rem !important; } + + .pl-xs-8 { + padding-left: 3rem !important; } + + .px-xs-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-xs-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-xs-9 { + padding: 3.5rem !important; } + + .pt-xs-9 { + padding-top: 3.5rem !important; } + + .pr-xs-9 { + padding-right: 3.5rem !important; } + + .pb-xs-9 { + padding-bottom: 3.5rem !important; } + + .pl-xs-9 { + padding-left: 3.5rem !important; } + + .px-xs-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-xs-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-xs-10 { + padding: 4rem !important; } + + .pt-xs-10 { + padding-top: 4rem !important; } + + .pr-xs-10 { + padding-right: 4rem !important; } + + .pb-xs-10 { + padding-bottom: 4rem !important; } + + .pl-xs-10 { + padding-left: 4rem !important; } + + .px-xs-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-xs-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 31.25rem) { + .p-sm-0 { + padding: 0 !important; } + + .pt-sm-0 { + padding-top: 0 !important; } + + .pr-sm-0 { + padding-right: 0 !important; } + + .pb-sm-0 { + padding-bottom: 0 !important; } + + .pl-sm-0 { + padding-left: 0 !important; } + + .px-sm-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-sm-1 { + padding: 0.25rem !important; } + + .pt-sm-1 { + padding-top: 0.25rem !important; } + + .pr-sm-1 { + padding-right: 0.25rem !important; } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; } + + .pl-sm-1 { + padding-left: 0.25rem !important; } + + .px-sm-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-sm-2 { + padding: 0.5rem !important; } + + .pt-sm-2 { + padding-top: 0.5rem !important; } + + .pr-sm-2 { + padding-right: 0.5rem !important; } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; } + + .pl-sm-2 { + padding-left: 0.5rem !important; } + + .px-sm-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-sm-3 { + padding: 0.75rem !important; } + + .pt-sm-3 { + padding-top: 0.75rem !important; } + + .pr-sm-3 { + padding-right: 0.75rem !important; } + + .pb-sm-3 { + padding-bottom: 0.75rem !important; } + + .pl-sm-3 { + padding-left: 0.75rem !important; } + + .px-sm-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-sm-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-sm-4 { + padding: 1rem !important; } + + .pt-sm-4 { + padding-top: 1rem !important; } + + .pr-sm-4 { + padding-right: 1rem !important; } + + .pb-sm-4 { + padding-bottom: 1rem !important; } + + .pl-sm-4 { + padding-left: 1rem !important; } + + .px-sm-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-sm-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-sm-5 { + padding: 1.5rem !important; } + + .pt-sm-5 { + padding-top: 1.5rem !important; } + + .pr-sm-5 { + padding-right: 1.5rem !important; } + + .pb-sm-5 { + padding-bottom: 1.5rem !important; } + + .pl-sm-5 { + padding-left: 1.5rem !important; } + + .px-sm-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-sm-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-sm-6 { + padding: 2rem !important; } + + .pt-sm-6 { + padding-top: 2rem !important; } + + .pr-sm-6 { + padding-right: 2rem !important; } + + .pb-sm-6 { + padding-bottom: 2rem !important; } + + .pl-sm-6 { + padding-left: 2rem !important; } + + .px-sm-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-sm-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-sm-7 { + padding: 2.5rem !important; } + + .pt-sm-7 { + padding-top: 2.5rem !important; } + + .pr-sm-7 { + padding-right: 2.5rem !important; } + + .pb-sm-7 { + padding-bottom: 2.5rem !important; } + + .pl-sm-7 { + padding-left: 2.5rem !important; } + + .px-sm-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-sm-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-sm-8 { + padding: 3rem !important; } + + .pt-sm-8 { + padding-top: 3rem !important; } + + .pr-sm-8 { + padding-right: 3rem !important; } + + .pb-sm-8 { + padding-bottom: 3rem !important; } + + .pl-sm-8 { + padding-left: 3rem !important; } + + .px-sm-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-sm-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-sm-9 { + padding: 3.5rem !important; } + + .pt-sm-9 { + padding-top: 3.5rem !important; } + + .pr-sm-9 { + padding-right: 3.5rem !important; } + + .pb-sm-9 { + padding-bottom: 3.5rem !important; } + + .pl-sm-9 { + padding-left: 3.5rem !important; } + + .px-sm-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-sm-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-sm-10 { + padding: 4rem !important; } + + .pt-sm-10 { + padding-top: 4rem !important; } + + .pr-sm-10 { + padding-right: 4rem !important; } + + .pb-sm-10 { + padding-bottom: 4rem !important; } + + .pl-sm-10 { + padding-left: 4rem !important; } + + .px-sm-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-sm-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 50rem) { + .p-md-0 { + padding: 0 !important; } + + .pt-md-0 { + padding-top: 0 !important; } + + .pr-md-0 { + padding-right: 0 !important; } + + .pb-md-0 { + padding-bottom: 0 !important; } + + .pl-md-0 { + padding-left: 0 !important; } + + .px-md-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-md-1 { + padding: 0.25rem !important; } + + .pt-md-1 { + padding-top: 0.25rem !important; } + + .pr-md-1 { + padding-right: 0.25rem !important; } + + .pb-md-1 { + padding-bottom: 0.25rem !important; } + + .pl-md-1 { + padding-left: 0.25rem !important; } + + .px-md-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-md-2 { + padding: 0.5rem !important; } + + .pt-md-2 { + padding-top: 0.5rem !important; } + + .pr-md-2 { + padding-right: 0.5rem !important; } + + .pb-md-2 { + padding-bottom: 0.5rem !important; } + + .pl-md-2 { + padding-left: 0.5rem !important; } + + .px-md-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-md-3 { + padding: 0.75rem !important; } + + .pt-md-3 { + padding-top: 0.75rem !important; } + + .pr-md-3 { + padding-right: 0.75rem !important; } + + .pb-md-3 { + padding-bottom: 0.75rem !important; } + + .pl-md-3 { + padding-left: 0.75rem !important; } + + .px-md-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-md-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-md-4 { + padding: 1rem !important; } + + .pt-md-4 { + padding-top: 1rem !important; } + + .pr-md-4 { + padding-right: 1rem !important; } + + .pb-md-4 { + padding-bottom: 1rem !important; } + + .pl-md-4 { + padding-left: 1rem !important; } + + .px-md-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-md-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-md-5 { + padding: 1.5rem !important; } + + .pt-md-5 { + padding-top: 1.5rem !important; } + + .pr-md-5 { + padding-right: 1.5rem !important; } + + .pb-md-5 { + padding-bottom: 1.5rem !important; } + + .pl-md-5 { + padding-left: 1.5rem !important; } + + .px-md-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-md-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-md-6 { + padding: 2rem !important; } + + .pt-md-6 { + padding-top: 2rem !important; } + + .pr-md-6 { + padding-right: 2rem !important; } + + .pb-md-6 { + padding-bottom: 2rem !important; } + + .pl-md-6 { + padding-left: 2rem !important; } + + .px-md-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-md-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-md-7 { + padding: 2.5rem !important; } + + .pt-md-7 { + padding-top: 2.5rem !important; } + + .pr-md-7 { + padding-right: 2.5rem !important; } + + .pb-md-7 { + padding-bottom: 2.5rem !important; } + + .pl-md-7 { + padding-left: 2.5rem !important; } + + .px-md-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-md-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-md-8 { + padding: 3rem !important; } + + .pt-md-8 { + padding-top: 3rem !important; } + + .pr-md-8 { + padding-right: 3rem !important; } + + .pb-md-8 { + padding-bottom: 3rem !important; } + + .pl-md-8 { + padding-left: 3rem !important; } + + .px-md-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-md-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-md-9 { + padding: 3.5rem !important; } + + .pt-md-9 { + padding-top: 3.5rem !important; } + + .pr-md-9 { + padding-right: 3.5rem !important; } + + .pb-md-9 { + padding-bottom: 3.5rem !important; } + + .pl-md-9 { + padding-left: 3.5rem !important; } + + .px-md-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-md-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-md-10 { + padding: 4rem !important; } + + .pt-md-10 { + padding-top: 4rem !important; } + + .pr-md-10 { + padding-right: 4rem !important; } + + .pb-md-10 { + padding-bottom: 4rem !important; } + + .pl-md-10 { + padding-left: 4rem !important; } + + .px-md-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-md-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 66.5rem) { + .p-lg-0 { + padding: 0 !important; } + + .pt-lg-0 { + padding-top: 0 !important; } + + .pr-lg-0 { + padding-right: 0 !important; } + + .pb-lg-0 { + padding-bottom: 0 !important; } + + .pl-lg-0 { + padding-left: 0 !important; } + + .px-lg-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-lg-1 { + padding: 0.25rem !important; } + + .pt-lg-1 { + padding-top: 0.25rem !important; } + + .pr-lg-1 { + padding-right: 0.25rem !important; } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; } + + .pl-lg-1 { + padding-left: 0.25rem !important; } + + .px-lg-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-lg-2 { + padding: 0.5rem !important; } + + .pt-lg-2 { + padding-top: 0.5rem !important; } + + .pr-lg-2 { + padding-right: 0.5rem !important; } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; } + + .pl-lg-2 { + padding-left: 0.5rem !important; } + + .px-lg-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-lg-3 { + padding: 0.75rem !important; } + + .pt-lg-3 { + padding-top: 0.75rem !important; } + + .pr-lg-3 { + padding-right: 0.75rem !important; } + + .pb-lg-3 { + padding-bottom: 0.75rem !important; } + + .pl-lg-3 { + padding-left: 0.75rem !important; } + + .px-lg-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-lg-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-lg-4 { + padding: 1rem !important; } + + .pt-lg-4 { + padding-top: 1rem !important; } + + .pr-lg-4 { + padding-right: 1rem !important; } + + .pb-lg-4 { + padding-bottom: 1rem !important; } + + .pl-lg-4 { + padding-left: 1rem !important; } + + .px-lg-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-lg-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-lg-5 { + padding: 1.5rem !important; } + + .pt-lg-5 { + padding-top: 1.5rem !important; } + + .pr-lg-5 { + padding-right: 1.5rem !important; } + + .pb-lg-5 { + padding-bottom: 1.5rem !important; } + + .pl-lg-5 { + padding-left: 1.5rem !important; } + + .px-lg-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-lg-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-lg-6 { + padding: 2rem !important; } + + .pt-lg-6 { + padding-top: 2rem !important; } + + .pr-lg-6 { + padding-right: 2rem !important; } + + .pb-lg-6 { + padding-bottom: 2rem !important; } + + .pl-lg-6 { + padding-left: 2rem !important; } + + .px-lg-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-lg-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-lg-7 { + padding: 2.5rem !important; } + + .pt-lg-7 { + padding-top: 2.5rem !important; } + + .pr-lg-7 { + padding-right: 2.5rem !important; } + + .pb-lg-7 { + padding-bottom: 2.5rem !important; } + + .pl-lg-7 { + padding-left: 2.5rem !important; } + + .px-lg-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-lg-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-lg-8 { + padding: 3rem !important; } + + .pt-lg-8 { + padding-top: 3rem !important; } + + .pr-lg-8 { + padding-right: 3rem !important; } + + .pb-lg-8 { + padding-bottom: 3rem !important; } + + .pl-lg-8 { + padding-left: 3rem !important; } + + .px-lg-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-lg-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-lg-9 { + padding: 3.5rem !important; } + + .pt-lg-9 { + padding-top: 3.5rem !important; } + + .pr-lg-9 { + padding-right: 3.5rem !important; } + + .pb-lg-9 { + padding-bottom: 3.5rem !important; } + + .pl-lg-9 { + padding-left: 3.5rem !important; } + + .px-lg-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-lg-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-lg-10 { + padding: 4rem !important; } + + .pt-lg-10 { + padding-top: 4rem !important; } + + .pr-lg-10 { + padding-right: 4rem !important; } + + .pb-lg-10 { + padding-bottom: 4rem !important; } + + .pl-lg-10 { + padding-left: 4rem !important; } + + .px-lg-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-lg-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 87.5rem) { + .p-xl-0 { + padding: 0 !important; } + + .pt-xl-0 { + padding-top: 0 !important; } + + .pr-xl-0 { + padding-right: 0 !important; } + + .pb-xl-0 { + padding-bottom: 0 !important; } + + .pl-xl-0 { + padding-left: 0 !important; } + + .px-xl-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-xl-1 { + padding: 0.25rem !important; } + + .pt-xl-1 { + padding-top: 0.25rem !important; } + + .pr-xl-1 { + padding-right: 0.25rem !important; } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; } + + .pl-xl-1 { + padding-left: 0.25rem !important; } + + .px-xl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-xl-2 { + padding: 0.5rem !important; } + + .pt-xl-2 { + padding-top: 0.5rem !important; } + + .pr-xl-2 { + padding-right: 0.5rem !important; } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; } + + .pl-xl-2 { + padding-left: 0.5rem !important; } + + .px-xl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-xl-3 { + padding: 0.75rem !important; } + + .pt-xl-3 { + padding-top: 0.75rem !important; } + + .pr-xl-3 { + padding-right: 0.75rem !important; } + + .pb-xl-3 { + padding-bottom: 0.75rem !important; } + + .pl-xl-3 { + padding-left: 0.75rem !important; } + + .px-xl-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-xl-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-xl-4 { + padding: 1rem !important; } + + .pt-xl-4 { + padding-top: 1rem !important; } + + .pr-xl-4 { + padding-right: 1rem !important; } + + .pb-xl-4 { + padding-bottom: 1rem !important; } + + .pl-xl-4 { + padding-left: 1rem !important; } + + .px-xl-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-xl-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-xl-5 { + padding: 1.5rem !important; } + + .pt-xl-5 { + padding-top: 1.5rem !important; } + + .pr-xl-5 { + padding-right: 1.5rem !important; } + + .pb-xl-5 { + padding-bottom: 1.5rem !important; } + + .pl-xl-5 { + padding-left: 1.5rem !important; } + + .px-xl-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-xl-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-xl-6 { + padding: 2rem !important; } + + .pt-xl-6 { + padding-top: 2rem !important; } + + .pr-xl-6 { + padding-right: 2rem !important; } + + .pb-xl-6 { + padding-bottom: 2rem !important; } + + .pl-xl-6 { + padding-left: 2rem !important; } + + .px-xl-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-xl-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-xl-7 { + padding: 2.5rem !important; } + + .pt-xl-7 { + padding-top: 2.5rem !important; } + + .pr-xl-7 { + padding-right: 2.5rem !important; } + + .pb-xl-7 { + padding-bottom: 2.5rem !important; } + + .pl-xl-7 { + padding-left: 2.5rem !important; } + + .px-xl-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-xl-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-xl-8 { + padding: 3rem !important; } + + .pt-xl-8 { + padding-top: 3rem !important; } + + .pr-xl-8 { + padding-right: 3rem !important; } + + .pb-xl-8 { + padding-bottom: 3rem !important; } + + .pl-xl-8 { + padding-left: 3rem !important; } + + .px-xl-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-xl-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-xl-9 { + padding: 3.5rem !important; } + + .pt-xl-9 { + padding-top: 3.5rem !important; } + + .pr-xl-9 { + padding-right: 3.5rem !important; } + + .pb-xl-9 { + padding-bottom: 3.5rem !important; } + + .pl-xl-9 { + padding-left: 3.5rem !important; } + + .px-xl-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-xl-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-xl-10 { + padding: 4rem !important; } + + .pt-xl-10 { + padding-top: 4rem !important; } + + .pr-xl-10 { + padding-right: 4rem !important; } + + .pb-xl-10 { + padding-bottom: 4rem !important; } + + .pl-xl-10 { + padding-left: 4rem !important; } + + .px-xl-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-xl-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media print { + .site-footer, + .site-button, + #edit-this-page, + #back-to-top, + .site-nav, + .main-header { + display: none !important; } + + .side-bar { + width: 100%; + height: auto; + border-right: 0 !important; } + + .site-header { + border-bottom: 1px solid #eeebee; } + + .site-title { + font-size: 1rem !important; + font-weight: 700 !important; } + + .text-small { + font-size: 8pt !important; } + + pre.highlight { + border: 1px solid #eeebee; } + + .main { + max-width: none; + margin-left: 0; } } +a.skip-to-main { + left: -999px; + position: absolute; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + z-index: -999; } + +a.skip-to-main:focus, +a.skip-to-main:active { + color: #d41b2c; + background-color: #fff; + left: auto; + top: auto; + width: 30%; + height: auto; + overflow: auto; + margin: 10px 35%; + padding: 5px; + border-radius: 15px; + border: 4px solid #5e41d0; + text-align: center; + font-size: 1.2em; + z-index: 999; } + +div.opaque { + background-color: #fff; } + +p.warning, blockquote.warning { + background: rgba(247, 126, 126, 0.2); + border-left: 4px solid #dd2e2e; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: .8rem; } + p.warning::before, blockquote.warning::before { + color: #dd2e2e; + content: "Warning"; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + p.warning > .warning-title, blockquote.warning > .warning-title { + color: #dd2e2e; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + +p.warning-title, blockquote.warning-title { + background: rgba(247, 126, 126, 0.2); + border-left: 4px solid #dd2e2e; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: .8rem; } + p.warning-title > p:first-child, blockquote.warning-title > p:first-child { + margin-top: 0; + margin-bottom: 0; + color: #dd2e2e; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + +blockquote.warning { + margin-left: 0; + margin-right: 0; } + blockquote.warning > p:first-child { + margin-top: 0; } + blockquote.warning > p:last-child { + margin-bottom: 0; } + +blockquote.warning-title { + margin-left: 0; + margin-right: 0; } + blockquote.warning-title > p:nth-child(2) { + margin-top: 0; } + blockquote.warning-title > p:last-child { + margin-bottom: 0; } + +.announcement, .main-content .module, +.module { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07), 0 4px 14px rgba(0, 0, 0, 0.05); + margin: 1rem -1rem; + display: flex; + flex-direction: column; + min-width: 0; + padding: 0 1rem; + position: relative; + word-wrap: break-word; } + @media (min-width: 50rem) { + .announcement, .main-content .module, + .module { + border-radius: 4px; + margin: 1rem 0; } } + .announcement > :first-child, .main-content .module > :first-child, + .module > :first-child { + border-top: none !important; } + .announcement > :last-child, .main-content .module > :last-child, + .module > :last-child { + border-bottom: none !important; } + .announcement .label, + .module .label, .announcement .staffer .staffer-pronouns, .staffer .announcement .staffer-pronouns, + .module .staffer .staffer-pronouns, + .staffer .module .staffer-pronouns, .announcement div.highlighter-rouge[overlay]::after, + .module div.highlighter-rouge[overlay]::after { + border-radius: 4px; + margin-left: 0; + user-select: none; } + +.main-content .module h1:first-child, +.main-content .module h2:first-child, +.main-content .module h3:first-child, +.main-content .module h4:first-child, +.main-content .module h5:first-child, +.main-content .module h6:first-child, +.module h1:first-child, +.module h2:first-child, +.module h3:first-child, +.module h4:first-child, +.module h5:first-child, +.module h6:first-child { + margin-top: 1rem; } +.main-content .module > dl, +.module > dl { + border-bottom: 1px solid #eeebee; + border-top: 1px solid #eeebee; + display: grid; + grid-template-columns: max-content 1fr; + margin: 0.5rem -1rem; } + .main-content .module > dl:first-child, + .module > dl:first-child { + margin-top: 0; } + .main-content .module > dl:last-child, + .module > dl:last-child { + margin-bottom: 0; } + @media (min-width: 66.5rem) { + .main-content .module > dl, + .module > dl { + grid-template-columns: 1fr 7fr; } } + .main-content .module > dl > dt, + .main-content .module > dl > dd, + .main-content .module > dl > dt, + .module > dl > dt, + .main-content .module > dl > dd, + .module > dl > dd { + margin: 0; + padding: 0.5rem; } + @media (min-width: 31.25rem) { + .main-content .module > dl > dt, + .main-content .module > dl > dd, + .main-content .module > dl > dt, + .module > dl > dt, + .main-content .module > dl > dd, + .module > dl > dd { + padding: 0.5rem 1rem; } } + .main-content .module > dl > dt, + .module > dl > dt { + border-top: 1px solid #eeebee; + font-weight: normal; + text-align: right; } + .main-content .module > dl > dt + dd, + .module > dl > dt + dd { + border-top: 1px solid #eeebee; } + .main-content .module > dl > dt:first-child, + .module > dl > dt:first-child { + border-top: none; } + .main-content .module > dl > dt:first-child + dd, + .module > dl > dt:first-child + dd { + border-top: none; } + .main-content .module > dl > dt::after, + .module > dl > dt::after { + content: ":"; } + .main-content .module > dl > dd + dd, + .module > dl > dd + dd { + padding-top: 0; } + .main-content .module > dl > dd ol, .main-content .module > dl > dd ul, .main-content .module > dl > dd dl, + .module > dl > dd ol, + .module > dl > dd ul, + .module > dl > dd dl { + margin: 0; } + .main-content .module > dl > dd dl, + .module > dl > dd dl { + display: flex; + flex-direction: column; } + @media (min-width: 31.25rem) { + .main-content .module > dl > dd dl, + .module > dl > dd dl { + flex-direction: row; } } + .main-content .module > dl > dd dl dt, + .module > dl > dd dl dt { + flex: 0 0 62.5%; + margin: 0; } + .main-content .module > dl > dd dl dd, + .module > dl > dd dl dd { + margin: 0; } + +.schedule td { + min-width: 0; + padding: 0; + vertical-align: top; } +.schedule .note { + font-style: italic; } +.schedule .holiday { + background-color: #efefef; + padding: 5px; } +.schedule .assignment { + border: 1px dashed red; + padding: 5px; } +.schedule .day { + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; + padding: 4px; } +.schedule .week-label { + min-width: 80px; + vertical-align: middle; + padding: 4px; + text-align: center; } +.schedule .content { + flex-grow: 100; } +.schedule .date { + font-weight: bolder; } + +.bldschedule { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07), 0 4px 14px rgba(0, 0, 0, 0.05); + margin: 1rem -1rem; + overflow-x: scroll; + position: relative; } + @media (min-width: 50rem) { + .bldschedule { + border-radius: 4px; + margin: 1rem 0; } } + .bldschedule li::before { + display: none; } + .bldschedule ul.schedule-timeline, + .bldschedule ul.schedule-group, + .bldschedule ul.schedule-events { + margin-top: 0; + padding-left: 0; } + .bldschedule ul.schedule-timeline { + margin: 40px auto 0; + position: absolute; + width: 100%; } + .bldschedule .schedule-time { + color: #959396; + height: 40px; + margin: 0; + padding: 0.5rem; + position: relative; } + .bldschedule .schedule-time::after { + background-color: #eeebee; + content: ''; + height: 1px; + left: 0; + position: absolute; + top: 0; + width: 100%; } + .bldschedule .schedule-group { + display: flex; + margin-bottom: 0; + position: relative; } + .bldschedule .schedule-day { + border-left: 1px solid #eeebee; + flex: 1 0 0; + margin: 0; + min-width: 130px; } + .bldschedule .schedule-day:first-of-type { + border-left: 0; } + .bldschedule h2.schedule-header { + align-items: center; + display: flex; + font-size: 18px !important; + height: 40px; + justify-content: center; + margin: 0; } + .bldschedule .schedule-events { + display: flex; + padding: 0; + position: relative; } + .bldschedule .schedule-event { + background-color: #959396; + border-radius: 4px; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), inset 0 -3px 0 rgba(0, 0, 0, 0.2); + color: #fff; + float: left; + height: 100%; + margin: 0; + padding: 0.25rem 0.5rem; + position: absolute; + width: 100%; } + .bldschedule .schedule-event.lecture { + background-color: #959396; } + .bldschedule .schedule-event.section { + background-color: #7253ed; } + .bldschedule .schedule-event.office-hours { + background-color: #2c84fa; } + +.staffer { + display: flex; + margin: 1rem; } + .staffer .staffer-image { + border-radius: 50%; + height: 100px; + margin-right: 1rem; } + .staffer p, + .staffer .staffer-name { + margin: 0.25rem !important; } + +a abbr[title] { + border-bottom: none; } + +abbr[title] { + text-decoration: none; } + +code { + font-size: 14px; + padding: 0.2em 0.4em; + border: none; } + +div.highlighter-rouge[overlay] { + position: relative; } + div.highlighter-rouge[overlay]::after { + background-color: #fff; + border-radius: 4px; + bottom: 0.5rem; + content: attr(overlay); + position: absolute; + right: 0; + user-select: none; } + +details { + margin: 0 40px 1em; } + +h1, h2, h3, h4, h5, h6 { + align-items: center; + display: flex; } + +iframe, +summary { + max-width: 100%; } + +.main-content-wrap { + max-width: 50rem; + margin: auto; } + +.main-content a { + overflow-wrap: anywhere; + white-space: normal; } +.main-content dl { + display: block; + grid-template-columns: none; } +.main-content dt { + font-weight: 700; + text-align: start; } + .main-content dt::after { + content: normal; } +.main-content dd { + font-weight: normal; } + .main-content dd + dt { + margin-top: 1em; } +.main-content .katex { + font-size: 1.1em; } + +[style*="--aspect-ratio"] > :first-child { + width: 100%; } + +[style*="--aspect-ratio"] > img { + height: auto; } + +@supports (--custom: property) { + [style*="--aspect-ratio"] { + position: relative; } + + [style*="--aspect-ratio"]::before { + content: ""; + display: block; + padding-bottom: calc(100% / (var(--aspect-ratio))); } + + [style*="--add-height"]::before { + padding-bottom: calc(100% / (var(--aspect-ratio)) + (var(--add-height))); } + + [style*="--aspect-ratio"] > :first-child { + position: absolute; + top: 0; + left: 0; + height: 100%; } } +.mermaid .label, .mermaid .staffer .staffer-pronouns, .staffer .mermaid .staffer-pronouns, .mermaid div.highlighter-rouge[overlay]::after { + text-transform: none; } diff --git a/assets/css/just-the-docs-head-nav.css b/assets/css/just-the-docs-head-nav.css new file mode 100644 index 00000000..a86c6c44 --- /dev/null +++ b/assets/css/just-the-docs-head-nav.css @@ -0,0 +1,3 @@ + +.site-nav ul li a { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%); } diff --git a/assets/css/just-the-docs-light.css b/assets/css/just-the-docs-light.css new file mode 100644 index 00000000..f3ba675f --- /dev/null +++ b/assets/css/just-the-docs-light.css @@ -0,0 +1,6736 @@ +@charset "UTF-8"; +.highlight, +pre.highlight { + background: #f9f9f9; + color: #383942; } + +.highlight pre { + background: #f9f9f9; } + +.highlight .hll { + background: #f9f9f9; } + +.highlight .c { + color: #9fa0a6; + font-style: italic; } + +.highlight .err { + color: #fff; + background-color: #e05151; } + +.highlight .k { + color: #a625a4; } + +.highlight .l { + color: #50a04f; } + +.highlight .n { + color: #383942; } + +.highlight .o { + color: #383942; } + +.highlight .p { + color: #383942; } + +.highlight .cm { + color: #9fa0a6; + font-style: italic; } + +.highlight .cp { + color: #9fa0a6; + font-style: italic; } + +.highlight .c1 { + color: #9fa0a6; + font-style: italic; } + +.highlight .cs { + color: #9fa0a6; + font-style: italic; } + +.highlight .ge { + font-style: italic; } + +.highlight .gs { + font-weight: 700; } + +.highlight .kc { + color: #a625a4; } + +.highlight .kd { + color: #a625a4; } + +.highlight .kn { + color: #a625a4; } + +.highlight .kp { + color: #a625a4; } + +.highlight .kr { + color: #a625a4; } + +.highlight .kt { + color: #a625a4; } + +.highlight .ld { + color: #50a04f; } + +.highlight .m { + color: #b66a00; } + +.highlight .s { + color: #50a04f; } + +.highlight .na { + color: #b66a00; } + +.highlight .nb { + color: #ca7601; } + +.highlight .nc { + color: #ca7601; } + +.highlight .no { + color: #ca7601; } + +.highlight .nd { + color: #ca7601; } + +.highlight .ni { + color: #ca7601; } + +.highlight .ne { + color: #ca7601; } + +.highlight .nf { + color: #383942; } + +.highlight .nl { + color: #ca7601; } + +.highlight .nn { + color: #383942; } + +.highlight .nx { + color: #383942; } + +.highlight .py { + color: #ca7601; } + +.highlight .nt { + color: #e35549; } + +.highlight .nv { + color: #ca7601; } + +.highlight .ow { + font-weight: 700; } + +.highlight .w { + color: #f8f8f2; } + +.highlight .mf { + color: #b66a00; } + +.highlight .mh { + color: #b66a00; } + +.highlight .mi { + color: #b66a00; } + +.highlight .mo { + color: #b66a00; } + +.highlight .sb { + color: #50a04f; } + +.highlight .sc { + color: #50a04f; } + +.highlight .sd { + color: #50a04f; } + +.highlight .s2 { + color: #50a04f; } + +.highlight .se { + color: #50a04f; } + +.highlight .sh { + color: #50a04f; } + +.highlight .si { + color: #50a04f; } + +.highlight .sx { + color: #50a04f; } + +.highlight .sr { + color: #0083bb; } + +.highlight .s1 { + color: #50a04f; } + +.highlight .ss { + color: #0083bb; } + +.highlight .bp { + color: #ca7601; } + +.highlight .vc { + color: #ca7601; } + +.highlight .vg { + color: #ca7601; } + +.highlight .vi { + color: #e35549; } + +.highlight .il { + color: #b66a00; } + +.highlight .gu { + color: #75715e; } + +.highlight .gd { + color: #e05151; } + +.highlight .gi { + color: #43d089; } + +.highlight .language-json .w + .s2 { + color: #e35549; } + +.highlight .language-json .kc { + color: #0083bb; } + +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +/* Document + ========================================================================== */ +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ +html { + line-height: 1.15; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ } + +/* Sections + ========================================================================== */ +/** + * Remove the margin in all browsers. + */ +body { + margin: 0; } + +/** + * Render the `main` element consistently in IE. + */ +main { + display: block; } + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ +h1 { + font-size: 2em; + margin: 0.67em 0; } + +/* Grouping content + ========================================================================== */ +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ +hr { + box-sizing: content-box; + /* 1 */ + height: 0; + /* 1 */ + overflow: visible; + /* 2 */ } + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ +pre { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ } + +/* Text-level semantics + ========================================================================== */ +/** + * Remove the gray background on active links in IE 10. + */ +a { + background-color: transparent; } + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ +abbr[title] { + border-bottom: none; + /* 1 */ + text-decoration: underline; + /* 2 */ + text-decoration: underline dotted; + /* 2 */ } + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ +b, +strong { + font-weight: bolder; } + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ +code, +kbd, +samp { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ } + +/** + * Add the correct font size in all browsers. + */ +small { + font-size: 80%; } + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } + +sub { + bottom: -0.25em; } + +sup { + top: -0.5em; } + +/* Embedded content + ========================================================================== */ +/** + * Remove the border on images inside links in IE 10. + */ +img { + border-style: none; } + +/* Forms + ========================================================================== */ +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: 1.15; + /* 1 */ + margin: 0; + /* 2 */ } + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ +button, +input { + /* 1 */ + overflow: visible; } + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ +button, +select { + /* 1 */ + text-transform: none; } + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; } + +/** + * Remove the inner border and padding in Firefox. + */ +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; } + +/** + * Restore the focus styles unset by the previous rule. + */ +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; } + +/** + * Correct the padding in Firefox. + */ +fieldset { + padding: 0.35em 0.75em 0.625em; } + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ +legend { + box-sizing: border-box; + /* 1 */ + color: inherit; + /* 2 */ + display: table; + /* 1 */ + max-width: 100%; + /* 1 */ + padding: 0; + /* 3 */ + white-space: normal; + /* 1 */ } + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ +progress { + vertical-align: baseline; } + +/** + * Remove the default vertical scrollbar in IE 10+. + */ +textarea { + overflow: auto; } + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ } + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; } + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ +[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ } + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ } + +/* Interactive + ========================================================================== */ +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ +details { + display: block; } + +/* + * Add the correct display in all browsers. + */ +summary { + display: list-item; } + +/* Misc + ========================================================================== */ +/** + * Add the correct display in IE 10+. + */ +template { + display: none; } + +/** + * Add the correct display in IE 10. + */ +[hidden] { + display: none; } + +:root { + color-scheme: light; } + +* { + box-sizing: border-box; } + +html { + font-size: 0.875rem !important; + scroll-behavior: smooth; } + @media (min-width: 31.25rem) { + html { + font-size: 1rem !important; } } + +body { + font-family: system-ui, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, sans-serif, "Segoe UI Emoji"; + font-size: inherit; + line-height: 1.4; + color: #5c5962; + background-color: #fff; + overflow-wrap: break-word; } + +ol, +ul, +dl, +pre, +address, +blockquote, +table, +div, +hr, +form, +fieldset, +noscript .table-wrapper { + margin-top: 0; } + +h1, +h2, +h3, +h4, +h5, +h6, +#toctitle { + margin-top: 0; + margin-bottom: 1em; + font-weight: 500; + line-height: 1.25; + color: #27262b; } + +p { + margin-top: 1em; + margin-bottom: 1em; } + +a { + color: #7253ed; + text-decoration: none; } + +a:not([class]) { + text-decoration: underline; + text-decoration-color: #eeebee; + text-underline-offset: 2px; } + a:not([class]):hover { + text-decoration-color: rgba(114, 83, 237, 0.45); } + +code { + font-family: "SFMono-Regular", menlo, consolas, monospace; + font-size: 0.75em; + line-height: 1.4; } + +figure, +pre { + margin: 0; } + +li { + margin: 0.25em 0; } + +img { + max-width: 100%; + height: auto; } + +hr { + height: 1px; + padding: 0; + margin: 2rem 0; + background-color: #eeebee; + border: 0; } + +blockquote { + margin: 10px 0; + margin-block-start: 0; + margin-inline-start: 0; + padding-left: 1rem; + border-left: 3px solid #eeebee; } + +.side-bar { + z-index: 0; + display: flex; + flex-wrap: wrap; + background-color: #f5f6fa; } + @media (min-width: 50rem) { + .side-bar { + flex-flow: column nowrap; + position: fixed; + width: 15.5rem; + height: 100%; + border-right: 1px solid #eeebee; + align-items: flex-end; } } + @media (min-width: 66.5rem) { + .side-bar { + width: calc((100% - 66.5rem) / 2 + 16.5rem); + min-width: 16.5rem; } } + +@media (min-width: 50rem) { + .main { + position: relative; + max-width: 50rem; + margin-left: 15.5rem; } } +@media (min-width: 66.5rem) { + .main { + margin-left: Max(16.5rem, calc((100% - 66.5rem) / 2 + 16.5rem)); } } + +.main-content-wrap { + padding-right: 1rem; + padding-left: 1rem; + padding-top: 1rem; + padding-bottom: 1rem; } + @media (min-width: 50rem) { + .main-content-wrap { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 50rem) { + .main-content-wrap { + padding-top: 2rem; + padding-bottom: 2rem; } } + +.main-header { + z-index: 0; + display: none; + background-color: #f5f6fa; } + @media (min-width: 50rem) { + .main-header { + display: flex; + justify-content: space-between; + height: 3.75rem; + background-color: #fff; + border-bottom: 1px solid #eeebee; } } + .main-header.nav-open { + display: block; } + @media (min-width: 50rem) { + .main-header.nav-open { + display: flex; } } + +.site-nav, +.site-header, +.site-footer { + width: 100%; } + @media (min-width: 66.5rem) { + .site-nav, + .site-header, + .site-footer { + width: 16.5rem; } } + +.site-nav { + display: none; } + .site-nav.nav-open { + display: block; } + @media (min-width: 50rem) { + .site-nav { + display: block; + padding-top: 3rem; + padding-bottom: 1rem; + overflow-y: auto; + flex: 1 1 auto; } } + +.site-header { + display: flex; + min-height: 3.75rem; + align-items: center; } + @media (min-width: 50rem) { + .site-header { + height: 3.75rem; + max-height: 3.75rem; + border-bottom: 1px solid #eeebee; } } + +.site-title { + padding-right: 1rem; + padding-left: 1rem; + flex-grow: 1; + display: flex; + height: 100%; + align-items: center; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #27262b; + font-size: 1.125rem !important; } + @media (min-width: 50rem) { + .site-title { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 31.25rem) { + .site-title { + font-size: 1.5rem !important; + line-height: 1.25; } } + @media (min-width: 50rem) { + .site-title { + padding-top: 0.5rem; + padding-bottom: 0.5rem; } } + +.site-button { + display: flex; + height: 100%; + padding: 1rem; + align-items: center; } + +@media (min-width: 50rem) { + .site-header .site-button { + display: none; } } +.site-title:hover { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%); } + +.site-button:hover { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%); } + +body { + position: relative; + padding-bottom: 4rem; + overflow-y: scroll; } + @media (min-width: 50rem) { + body { + position: static; + padding-bottom: 0; } } + +.site-footer { + padding-right: 1rem; + padding-left: 1rem; + position: absolute; + bottom: 0; + left: 0; + padding-top: 1rem; + padding-bottom: 1rem; + color: #959396; + font-size: 0.6875rem !important; } + @media (min-width: 50rem) { + .site-footer { + padding-right: 2rem; + padding-left: 2rem; } } + @media (min-width: 31.25rem) { + .site-footer { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) { + .site-footer { + position: static; + justify-self: end; } } + +.icon { + width: 1.5rem; + height: 1.5rem; + color: #7253ed; } + +.main-content { + line-height: 1.6; } + .main-content ol, + .main-content ul, + .main-content dl, + .main-content pre, + .main-content address, + .main-content blockquote, + .main-content .table-wrapper { + margin-top: 0.5em; } + .main-content a { + overflow: hidden; + text-overflow: ellipsis; } + .main-content ul, + .main-content ol { + padding-left: 1.5em; } + .main-content li .highlight { + margin-top: 0.25rem; } + .main-content ol { + list-style-type: none; + counter-reset: step-counter; } + .main-content ol > li { + position: relative; } + .main-content ol > li::before { + position: absolute; + top: 0.2em; + left: -1.6em; + color: #959396; + content: counter(step-counter); + counter-increment: step-counter; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .main-content ol > li::before { + font-size: 0.875rem !important; } } + @media (min-width: 31.25rem) { + .main-content ol > li::before { + top: 0.11em; } } + .main-content ol > li ol { + counter-reset: sub-counter; } + .main-content ol > li ol > li::before { + content: counter(sub-counter,lower-alpha); + counter-increment: sub-counter; } + .main-content ul { + list-style: none; } + .main-content ul > li::before { + position: absolute; + margin-left: -1.4em; + color: #959396; + content: "•"; } + .main-content .task-list-item::before { + content: ""; } + .main-content .task-list-item-checkbox { + margin-right: 0.6em; + margin-left: -1.4em; } + .main-content hr + * { + margin-top: 0; } + .main-content h1:first-of-type { + margin-top: 0.5em; } + .main-content dl { + display: grid; + grid-template: auto / 10em 1fr; } + .main-content dt, + .main-content dd { + margin: 0.25em 0; } + .main-content dt { + grid-column: 1; + font-weight: 500; + text-align: right; } + .main-content dt::after { + content: ":"; } + .main-content dd { + grid-column: 2; + margin-bottom: 0; + margin-left: 1em; } + .main-content dd blockquote:first-child, + .main-content dd div:first-child, + .main-content dd dl:first-child, + .main-content dd dt:first-child, + .main-content dd h1:first-child, + .main-content dd h2:first-child, + .main-content dd h3:first-child, + .main-content dd h4:first-child, + .main-content dd h5:first-child, + .main-content dd h6:first-child, + .main-content dd li:first-child, + .main-content dd ol:first-child, + .main-content dd p:first-child, + .main-content dd pre:first-child, + .main-content dd table:first-child, + .main-content dd ul:first-child, + .main-content dd .table-wrapper:first-child { + margin-top: 0; } + .main-content dd dl:first-child dt:first-child, + .main-content dd dl:first-child dd:nth-child(2), + .main-content ol dl:first-child dt:first-child, + .main-content ol dl:first-child dd:nth-child(2), + .main-content ul dl:first-child dt:first-child, + .main-content ul dl:first-child dd:nth-child(2) { + margin-top: 0; } + .main-content .anchor-heading { + position: absolute; + right: -1rem; + width: 1.5rem; + height: 100%; + padding-right: 0.25rem; + padding-left: 0.25rem; + overflow: visible; } + @media (min-width: 50rem) { + .main-content .anchor-heading { + right: auto; + left: -1.5rem; } } + .main-content .anchor-heading svg { + display: inline-block; + width: 100%; + height: 100%; + color: #7253ed; + visibility: hidden; } + .main-content .anchor-heading:hover svg, + .main-content .anchor-heading:focus svg, + .main-content h1:hover > .anchor-heading svg, + .main-content h2:hover > .anchor-heading svg, + .main-content h3:hover > .anchor-heading svg, + .main-content h4:hover > .anchor-heading svg, + .main-content h5:hover > .anchor-heading svg, + .main-content h6:hover > .anchor-heading svg { + visibility: visible; } + .main-content summary { + cursor: pointer; } + .main-content h1, + .main-content h2, + .main-content h3, + .main-content h4, + .main-content h5, + .main-content h6, + .main-content #toctitle { + position: relative; + margin-top: 1.5em; + margin-bottom: 0.25em; } + .main-content h1 + table, + .main-content h1 + .table-wrapper, + .main-content h1 + .code-example, + .main-content h1 + .highlighter-rouge, + .main-content h1 + .sectionbody .listingblock, + .main-content h2 + table, + .main-content h2 + .table-wrapper, + .main-content h2 + .code-example, + .main-content h2 + .highlighter-rouge, + .main-content h2 + .sectionbody .listingblock, + .main-content h3 + table, + .main-content h3 + .table-wrapper, + .main-content h3 + .code-example, + .main-content h3 + .highlighter-rouge, + .main-content h3 + .sectionbody .listingblock, + .main-content h4 + table, + .main-content h4 + .table-wrapper, + .main-content h4 + .code-example, + .main-content h4 + .highlighter-rouge, + .main-content h4 + .sectionbody .listingblock, + .main-content h5 + table, + .main-content h5 + .table-wrapper, + .main-content h5 + .code-example, + .main-content h5 + .highlighter-rouge, + .main-content h5 + .sectionbody .listingblock, + .main-content h6 + table, + .main-content h6 + .table-wrapper, + .main-content h6 + .code-example, + .main-content h6 + .highlighter-rouge, + .main-content h6 + .sectionbody .listingblock, + .main-content #toctitle + table, + .main-content #toctitle + .table-wrapper, + .main-content #toctitle + .code-example, + .main-content #toctitle + .highlighter-rouge, + .main-content #toctitle + .sectionbody .listingblock { + margin-top: 1em; } + .main-content h1 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h2 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h3 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h4 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h5 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content h6 + p:not(.label):not(div.highlighter-rouge[overlay]::after), + .main-content #toctitle + p:not(.label):not(div.highlighter-rouge[overlay]::after) { + margin-top: 0; } + .main-content > h1:first-child, + .main-content > h2:first-child, + .main-content > h3:first-child, + .main-content > h4:first-child, + .main-content > h5:first-child, + .main-content > h6:first-child, + .main-content > .sect1:first-child > h2, + .main-content > .sect2:first-child > h3, + .main-content > .sect3:first-child > h4, + .main-content > .sect4:first-child > h5, + .main-content > .sect5:first-child > h6 { + margin-top: 0.5rem; } + +.nav-list { + padding: 0; + margin-top: 0; + margin-bottom: 0; + list-style: none; } + .nav-list .nav-list-item { + font-size: 0.875rem !important; + position: relative; + margin: 0; } + @media (min-width: 31.25rem) { + .nav-list .nav-list-item { + font-size: 1rem !important; } } + @media (min-width: 50rem) { + .nav-list .nav-list-item { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .nav-list .nav-list-item { + font-size: 0.875rem !important; } } + + .nav-list .nav-list-item .nav-list-link { + display: block; + min-height: 3rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + line-height: 2.5rem; + padding-right: 3rem; + padding-left: 1rem; } + @media (min-width: 50rem) { + .nav-list .nav-list-item .nav-list-link { + min-height: 2rem; + line-height: 1.5rem; + padding-right: 2rem; + padding-left: 2rem; } } + .nav-list .nav-list-item .nav-list-link.external > svg { + width: 1rem; + height: 1rem; + vertical-align: text-bottom; } + .nav-list .nav-list-item .nav-list-link.active { + font-weight: 600; + text-decoration: none; } + .nav-list .nav-list-item .nav-list-link:hover, .nav-list .nav-list-item .nav-list-link.active { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%); } + .nav-list .nav-list-item .nav-list-expander { + position: absolute; + right: 0; + width: 3rem; + height: 3rem; + padding: 0.75rem; + color: #7253ed; } + @media (min-width: 50rem) { + .nav-list .nav-list-item .nav-list-expander { + width: 2rem; + height: 2rem; + padding: 0.5rem; } } + .nav-list .nav-list-item .nav-list-expander:hover { + background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%); } + .nav-list .nav-list-item .nav-list-expander svg { + transform: rotate(90deg); } + .nav-list .nav-list-item > .nav-list { + display: none; + padding-left: 0.75rem; + list-style: none; } + .nav-list .nav-list-item > .nav-list .nav-list-item { + position: relative; } + .nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-link { + color: #5c5962; } + .nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-expander { + color: #5c5962; } + .nav-list .nav-list-item.active > .nav-list-expander svg { + transform: rotate(-90deg); } + .nav-list .nav-list-item.active > .nav-list { + display: block; } + +.nav-category { + padding: 0.5rem 1rem; + font-weight: 600; + text-align: start; + text-transform: uppercase; + border-bottom: 1px solid #eeebee; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .nav-category { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) { + .nav-category { + padding: 0.5rem 2rem; + margin-top: 1rem; + text-align: start; } + .nav-category:first-child { + margin-top: 0; } } + +.nav-list.nav-category-list > .nav-list-item { + margin: 0; } + .nav-list.nav-category-list > .nav-list-item > .nav-list { + padding: 0; } + .nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-link { + color: #7253ed; } + .nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-expander { + color: #7253ed; } + +.aux-nav { + height: 100%; + overflow-x: auto; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .aux-nav { + font-size: 0.75rem !important; } } + .aux-nav .aux-nav-list { + display: flex; + height: 100%; + padding: 0; + margin: 0; + list-style: none; } + .aux-nav .aux-nav-list-item { + display: inline-block; + height: 100%; + padding: 0; + margin: 0; } + @media (min-width: 50rem) { + .aux-nav { + padding-right: 1rem; } } + +@media (min-width: 50rem) { + .breadcrumb-nav { + margin-top: -1rem; } } + +.breadcrumb-nav-list { + padding-left: 0; + margin-bottom: 0.75rem; + list-style: none; } + +.breadcrumb-nav-list-item { + display: table-cell; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .breadcrumb-nav-list-item { + font-size: 0.75rem !important; } } + .breadcrumb-nav-list-item::before { + display: none; } + .breadcrumb-nav-list-item::after { + display: inline-block; + margin-right: 0.5rem; + margin-left: 0.5rem; + color: #959396; + content: "/"; } + .breadcrumb-nav-list-item:last-child::after { + content: ""; } + +h1, +.text-alpha { + font-size: 2rem !important; + line-height: 1.25; + font-weight: 300; } + @media (min-width: 31.25rem) { + h1, + .text-alpha { + font-size: 2.25rem !important; } } + +h2, +.text-beta, +#toctitle { + font-size: 1.125rem !important; } + @media (min-width: 31.25rem) { + h2, + .text-beta, + #toctitle { + font-size: 1.5rem !important; + line-height: 1.25; } } + +h3, +.text-gamma, +.announcement h1, +.announcement h2 { + font-size: 1rem !important; } + @media (min-width: 31.25rem) { + h3, + .text-gamma, + .announcement h1, + .announcement h2 { + font-size: 1.125rem !important; } } + +h4, +.text-delta { + font-size: 0.6875rem !important; + font-weight: 400; + text-transform: uppercase; + letter-spacing: 0.1em; } + @media (min-width: 31.25rem) { + h4, + .text-delta { + font-size: 0.75rem !important; } } + +h4 code { + text-transform: none; } + +h5, +.text-epsilon, +.announcement .announcement-meta { + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + h5, + .text-epsilon, + .announcement .announcement-meta { + font-size: 0.875rem !important; } } + +h6, +.text-zeta { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + h6, + .text-zeta { + font-size: 0.75rem !important; } } + +.text-small { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .text-small { + font-size: 0.75rem !important; } } + +.text-mono { + font-family: "SFMono-Regular", menlo, consolas, monospace !important; } + +.text-left { + text-align: left !important; } + +.text-center { + text-align: center !important; } + +.text-right { + text-align: right !important; } + +.label:not(g), .staffer .staffer-pronouns:not(g), div.highlighter-rouge[overlay]:not(g)::after, +.label-blue:not(g) { + display: inline-block; + padding: 0.16em 0.56em; + margin-right: 0.5rem; + margin-left: 0.5rem; + color: #fff; + text-transform: uppercase; + vertical-align: middle; + background-color: #2869e6; + font-size: 0.6875rem !important; + border-radius: 12px; } + @media (min-width: 31.25rem) { + .label:not(g), .staffer .staffer-pronouns:not(g), div.highlighter-rouge[overlay]:not(g)::after, + .label-blue:not(g) { + font-size: 0.75rem !important; } } + +.label-green:not(g) { + background-color: #009c7b; } + +.label-purple:not(g) { + background-color: #5e41d0; } + +.label-red:not(g) { + background-color: #e94c4c; } + +.label-yellow:not(g) { + color: #44434d; + background-color: #f7d12e; } + +.btn, summary { + display: inline-block; + box-sizing: border-box; + padding: 0.3em 1em; + margin: 0; + font-family: inherit; + font-size: inherit; + font-weight: 500; + line-height: 1.5; + color: #7253ed; + text-decoration: none; + vertical-align: baseline; + cursor: pointer; + background-color: #f7f7f7; + border-width: 0; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + appearance: none; } + .btn:focus, summary:focus { + text-decoration: none; + outline: none; + box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn:focus:hover, summary:focus:hover, .btn.selected:focus, summary.selected:focus { + box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn:hover, summary:hover, .btn.zeroclipboard-is-hover, summary.zeroclipboard-is-hover { + color: #6a4aec; } + .btn:hover, summary:hover, .btn:active, summary:active, .btn.zeroclipboard-is-hover, summary.zeroclipboard-is-hover, .btn.zeroclipboard-is-active, summary.zeroclipboard-is-active { + text-decoration: none; + background-color: #f4f4f4; } + .btn:active, summary:active, .btn.selected, summary.selected, .btn.zeroclipboard-is-active, summary.zeroclipboard-is-active { + background-color: #efefef; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn.selected:hover, summary.selected:hover { + background-color: #cfcfcf; } + .btn:disabled, summary:disabled, .btn:disabled:hover, summary:disabled:hover, .btn.disabled, summary.disabled, .btn.disabled:hover, summary.disabled:hover { + color: rgba(102, 102, 102, 0.5); + cursor: default; + background-color: rgba(229, 229, 229, 0.5); + background-image: none; + box-shadow: none; } + +.btn-outline, summary { + color: #7253ed; + background: transparent; + box-shadow: inset 0 0 0 2px #e6e1e8; } + .btn-outline:hover, summary:hover, .btn-outline:active, summary:active, .btn-outline.zeroclipboard-is-hover, summary.zeroclipboard-is-hover, .btn-outline.zeroclipboard-is-active, summary.zeroclipboard-is-active { + color: #6341eb; + text-decoration: none; + background-color: transparent; + box-shadow: inset 0 0 0 3px #e6e1e8; } + .btn-outline:focus, summary:focus { + text-decoration: none; + outline: none; + box-shadow: inset 0 0 0 2px #5c5962, 0 0 0 3px rgba(0, 0, 255, 0.25); } + .btn-outline:focus:hover, summary:focus:hover, .btn-outline.selected:focus, summary.selected:focus { + box-shadow: inset 0 0 0 2px #5c5962; } + +.btn-primary { + color: #fff; + background-color: #5739ce; + background-image: linear-gradient(#6f55d5, #5739ce); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-primary:hover, .btn-primary.zeroclipboard-is-hover { + color: #fff; + background-color: #5132cb; + background-image: linear-gradient(#6549d2, #5132cb); } + .btn-primary:active, .btn-primary.selected, .btn-primary.zeroclipboard-is-active { + background-color: #4f31c6; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-primary.selected:hover { + background-color: #472cb2; } + +.btn-purple { + color: #fff; + background-color: #5739ce; + background-image: linear-gradient(#6f55d5, #5739ce); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-purple:hover, .btn-purple.zeroclipboard-is-hover { + color: #fff; + background-color: #5132cb; + background-image: linear-gradient(#6549d2, #5132cb); } + .btn-purple:active, .btn-purple.selected, .btn-purple.zeroclipboard-is-active { + background-color: #4f31c6; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-purple.selected:hover { + background-color: #472cb2; } + +.btn-blue { + color: #fff; + background-color: #227efa; + background-image: linear-gradient(#4593fb, #227efa); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-blue:hover, .btn-blue.zeroclipboard-is-hover { + color: #fff; + background-color: #1878fa; + background-image: linear-gradient(#368afa, #1878fa); } + .btn-blue:active, .btn-blue.selected, .btn-blue.zeroclipboard-is-active { + background-color: #1375f9; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-blue.selected:hover { + background-color: #0669ed; } + +.btn-green { + color: #fff; + background-color: #10ac7d; + background-image: linear-gradient(#13cc95, #10ac7d); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12); } + .btn-green:hover, .btn-green.zeroclipboard-is-hover { + color: #fff; + background-color: #0fa276; + background-image: linear-gradient(#12be8b, #0fa276); } + .btn-green:active, .btn-green.selected, .btn-green.zeroclipboard-is-active { + background-color: #0f9e73; + background-image: none; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); } + .btn-green.selected:hover { + background-color: #0d8662; } + +.btn-reset { + background: none; + border: none; + margin: 0; + text-align: inherit; + font: inherit; + border-radius: 0; + appearance: none; } + +.search { + position: relative; + z-index: 2; + flex-grow: 1; + height: 4rem; + padding: 0.5rem; + transition: padding linear 200ms; } + @media (min-width: 50rem) { + .search { + position: relative !important; + width: auto !important; + height: 100% !important; + padding: 0; + transition: none; } } + +.search-input-wrap { + position: relative; + z-index: 1; + height: 3rem; + overflow: hidden; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + transition: height linear 200ms; } + @media (min-width: 50rem) { + .search-input-wrap { + position: absolute; + width: 100%; + max-width: 33.5rem; + height: 100% !important; + border-radius: 0; + box-shadow: none; + transition: width ease 400ms; } } + +.search-input { + position: absolute; + width: 100%; + height: 100%; + padding: 0.5rem 1rem 0.5rem 2.5rem; + font-size: 1rem; + color: #5c5962; + background-color: #fff; + border-top: 0; + border-right: 0; + border-bottom: 0; + border-left: 0; + border-radius: 0; } + @media (min-width: 50rem) { + .search-input { + padding: 0.5rem 1rem 0.5rem 3.5rem; + font-size: 0.875rem; + background-color: #fff; + transition: padding-left linear 200ms; } } + .search-input:focus { + outline: 0; } + .search-input:focus + .search-label .search-icon { + color: #7253ed; } + +.search-label { + position: absolute; + display: flex; + height: 100%; + padding-left: 1rem; } + @media (min-width: 50rem) { + .search-label { + padding-left: 2rem; + transition: padding-left linear 200ms; } } + .search-label .search-icon { + width: 1.2rem; + height: 1.2rem; + align-self: center; + color: #959396; } + +.search-results { + position: absolute; + left: 0; + display: none; + width: 100%; + max-height: calc(100% - 4rem); + overflow-y: auto; + background-color: #fff; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } + @media (min-width: 50rem) { + .search-results { + top: 100%; + width: 33.5rem; + max-height: calc(100vh - 200%) !important; } } + +.search-results-list { + padding-left: 0; + margin-bottom: 0.25rem; + list-style: none; + font-size: 0.875rem !important; } + @media (min-width: 31.25rem) { + .search-results-list { + font-size: 1rem !important; } } + @media (min-width: 50rem) { + .search-results-list { + font-size: 0.75rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .search-results-list { + font-size: 0.875rem !important; } } + +.search-results-list-item { + padding: 0; + margin: 0; } + +.search-result { + display: block; + padding: 0.25rem 0.75rem; } + .search-result:hover, .search-result.active { + background-color: #ebedf5; } + +.search-result-title { + display: block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; } + @media (min-width: 31.25rem) { + .search-result-title { + display: inline-block; + width: 40%; + padding-right: 0.5rem; + vertical-align: top; } } + +.search-result-doc { + display: flex; + align-items: center; + word-wrap: break-word; } + .search-result-doc.search-result-doc-parent { + opacity: 0.5; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.875rem !important; } } + @media (min-width: 50rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.6875rem !important; } } + @media (min-width: 50rem) and (min-width: 31.25rem) { + .search-result-doc.search-result-doc-parent { + font-size: 0.75rem !important; } } + + .search-result-doc .search-result-icon { + width: 1rem; + height: 1rem; + margin-right: 0.5rem; + color: #7253ed; + flex-shrink: 0; } + .search-result-doc .search-result-doc-title { + overflow: auto; } + +.search-result-section { + margin-left: 1.5rem; + word-wrap: break-word; } + +.search-result-rel-url { + display: block; + margin-left: 1.5rem; + overflow: hidden; + color: #959396; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.5625rem !important; } + @media (min-width: 31.25rem) { + .search-result-rel-url { + font-size: 0.625rem !important; } } + +.search-result-previews { + display: block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + margin-left: 0.5rem; + color: #959396; + word-wrap: break-word; + border-left: 1px solid; + border-left-color: #eeebee; + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .search-result-previews { + font-size: 0.75rem !important; } } + @media (min-width: 31.25rem) { + .search-result-previews { + display: inline-block; + width: 60%; + padding-left: 0.5rem; + margin-left: 0; + vertical-align: top; } } + +.search-result-preview + .search-result-preview { + margin-top: 0.25rem; } + +.search-result-highlight { + font-weight: bold; } + +.search-no-result { + padding: 0.5rem 0.75rem; + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .search-no-result { + font-size: 0.875rem !important; } } + +.search-button { + position: fixed; + right: 1rem; + bottom: 1rem; + display: flex; + width: 3.5rem; + height: 3.5rem; + background-color: #fff; + border: 1px solid rgba(114, 83, 237, 0.3); + border-radius: 1.75rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + align-items: center; + justify-content: center; } + +.search-overlay { + position: fixed; + top: 0; + left: 0; + z-index: 1; + width: 0; + height: 0; + background-color: rgba(0, 0, 0, 0.3); + opacity: 0; + transition: opacity ease 400ms, width 0s 400ms, height 0s 400ms; } + +.search-active .search { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 0; } +.search-active .search-input-wrap { + height: 4rem; + border-radius: 0; } + @media (min-width: 50rem) { + .search-active .search-input-wrap { + width: 33.5rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } } +.search-active .search-input { + background-color: #fff; } + @media (min-width: 50rem) { + .search-active .search-input { + padding-left: 2.3rem; } } +@media (min-width: 50rem) { + .search-active .search-label { + padding-left: 0.6rem; } } +.search-active .search-results { + display: block; } +.search-active .search-overlay { + width: 100%; + height: 100%; + opacity: 1; + transition: opacity ease 400ms, width 0s, height 0s; } +@media (min-width: 50rem) { + .search-active .main { + position: fixed; + right: 0; + left: 0; } } +.search-active .main-header { + padding-top: 4rem; } + @media (min-width: 50rem) { + .search-active .main-header { + padding-top: 0; } } + +.table-wrapper { + display: block; + width: 100%; + max-width: 100%; + margin-bottom: 1.5rem; + overflow-x: auto; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); } + +table { + display: table; + min-width: 100%; + border-collapse: separate; } + +th, +td { + font-size: 0.75rem !important; + min-width: 7.5rem; + padding: 0.5rem 0.75rem; + background-color: #fff; + border-bottom: 1px solid rgba(238, 235, 238, 0.5); + border-left: 1px solid #eeebee; } + @media (min-width: 31.25rem) { + th, + td { + font-size: 0.875rem !important; } } + th:first-of-type, + td:first-of-type { + border-left: 0; } + +tbody tr:last-of-type th, +tbody tr:last-of-type td { + border-bottom: 0; } +tbody tr:last-of-type td { + padding-bottom: 0.75rem; } + +thead th { + border-bottom: 1px solid #eeebee; } + +:not(pre, figure) > code { + padding: 0.2em 0.15em; + font-weight: 400; + background-color: #f5f6fa; + border: 1px solid #eeebee; + border-radius: 4px; } + +a:visited code { + border-color: #eeebee; } + +div.highlighter-rouge, +div.listingblock > div.content, +figure.highlight { + margin-top: 0; + margin-bottom: 0.75rem; + background-color: #f5f6fa; + border-radius: 4px; + box-shadow: none; + -webkit-overflow-scrolling: touch; + position: relative; + padding: 0; } + div.highlighter-rouge > button, + div.listingblock > div.content > button, + figure.highlight > button { + width: 0.75rem; + opacity: 0; + position: absolute; + top: 0; + right: 0; + border: 0.75rem solid #f5f6fa; + background-color: #f5f6fa; + color: #5c5962; + box-sizing: content-box; } + div.highlighter-rouge > button svg, + div.listingblock > div.content > button svg, + figure.highlight > button svg { + fill: #5c5962; } + div.highlighter-rouge > button:active, + div.listingblock > div.content > button:active, + figure.highlight > button:active { + text-decoration: none; + outline: none; + opacity: 1; } + div.highlighter-rouge > button:focus, + div.listingblock > div.content > button:focus, + figure.highlight > button:focus { + opacity: 1; } + div.highlighter-rouge:hover > button, + div.listingblock > div.content:hover > button, + figure.highlight:hover > button { + cursor: copy; + opacity: 1; } + +div.highlighter-rouge div.highlight { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } +div.highlighter-rouge pre.highlight, +div.highlighter-rouge code { + padding: 0; + margin: 0; + border: 0; } + +div.listingblock { + margin-top: 0; + margin-bottom: 0.75rem; } + div.listingblock div.content { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } + div.listingblock div.content > pre, + div.listingblock code { + padding: 0; + margin: 0; + border: 0; } + +figure.highlight pre, +figure.highlight :not(pre) > code { + overflow-x: auto; + padding: 0.75rem; + margin: 0; + border: 0; } + +.highlight .table-wrapper { + padding: 0.75rem 0; + margin: 0; + border: 0; + box-shadow: none; } + .highlight .table-wrapper td, + .highlight .table-wrapper pre { + font-size: 0.6875rem !important; + min-width: 0; + padding: 0; + background-color: #f5f6fa; + border: 0; } + @media (min-width: 31.25rem) { + .highlight .table-wrapper td, + .highlight .table-wrapper pre { + font-size: 0.75rem !important; } } + .highlight .table-wrapper td.gl { + width: 1em; + padding-right: 0.75rem; + padding-left: 0.75rem; } + .highlight .table-wrapper pre { + margin: 0; + line-height: 2; } + +.code-example, +.listingblock > .title { + padding: 0.75rem; + margin-bottom: 0.75rem; + overflow: auto; + border: 1px solid #eeebee; + border-radius: 4px; } + .code-example + .highlighter-rouge, + .code-example + .sectionbody .listingblock, + .code-example + .content, + .code-example + figure.highlight, + .listingblock > .title + .highlighter-rouge, + .listingblock > .title + .sectionbody .listingblock, + .listingblock > .title + .content, + .listingblock > .title + figure.highlight { + position: relative; + margin-top: -1rem; + border-right: 1px solid #eeebee; + border-bottom: 1px solid #eeebee; + border-left: 1px solid #eeebee; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +code.language-mermaid { + padding: 0; + background-color: inherit; + border: 0; } + +.highlight, +pre.highlight { + background: #f5f6fa; + color: #5c5962; } + +.highlight pre { + background: #f5f6fa; } + +.text-grey-dk-000, .staffer .staffer-meta { + color: #959396 !important; } + +.text-grey-dk-100, .staffer .staffer-pronouns, div.highlighter-rouge[overlay]::after { + color: #5c5962 !important; } + +.text-grey-dk-200 { + color: #44434d !important; } + +.text-grey-dk-250 { + color: #302d36 !important; } + +.text-grey-dk-300 { + color: #27262b !important; } + +.text-grey-lt-000 { + color: #f5f6fa !important; } + +.text-grey-lt-100 { + color: #eeebee !important; } + +.text-grey-lt-200 { + color: #ecebed !important; } + +.text-grey-lt-300 { + color: #e6e1e8 !important; } + +.text-blue-000 { + color: #2c84fa !important; } + +.text-blue-100 { + color: #2869e6 !important; } + +.text-blue-200 { + color: #264caf !important; } + +.text-blue-300 { + color: #183385 !important; } + +.text-green-000 { + color: #41d693 !important; } + +.text-green-100 { + color: #11b584 !important; } + +.text-green-200 { + color: #009c7b !important; } + +.text-green-300 { + color: #026e57 !important; } + +.text-purple-000 { + color: #7253ed !important; } + +.text-purple-100 { + color: #5e41d0 !important; } + +.text-purple-200 { + color: #4e26af !important; } + +.text-purple-300 { + color: #381885 !important; } + +.text-yellow-000 { + color: #ffeb82 !important; } + +.text-yellow-100 { + color: #fadf50 !important; } + +.text-yellow-200 { + color: #f7d12e !important; } + +.text-yellow-300 { + color: #e7af06 !important; } + +.text-red-000 { + color: #f77e7e !important; } + +.text-red-100 { + color: #f96e65 !important; } + +.text-red-200 { + color: #e94c4c !important; } + +.text-red-300 { + color: #dd2e2e !important; } + +.bg-grey-dk-000 { + background-color: #959396 !important; } + +.bg-grey-dk-100 { + background-color: #5c5962 !important; } + +.bg-grey-dk-200 { + background-color: #44434d !important; } + +.bg-grey-dk-250 { + background-color: #302d36 !important; } + +.bg-grey-dk-300 { + background-color: #27262b !important; } + +.bg-grey-lt-000 { + background-color: #f5f6fa !important; } + +.bg-grey-lt-100 { + background-color: #eeebee !important; } + +.bg-grey-lt-200, .staffer .staffer-pronouns { + background-color: #ecebed !important; } + +.bg-grey-lt-300 { + background-color: #e6e1e8 !important; } + +.bg-blue-000 { + background-color: #2c84fa !important; } + +.bg-blue-100 { + background-color: #2869e6 !important; } + +.bg-blue-200 { + background-color: #264caf !important; } + +.bg-blue-300 { + background-color: #183385 !important; } + +.bg-green-000 { + background-color: #41d693 !important; } + +.bg-green-100 { + background-color: #11b584 !important; } + +.bg-green-200 { + background-color: #009c7b !important; } + +.bg-green-300 { + background-color: #026e57 !important; } + +.bg-purple-000 { + background-color: #7253ed !important; } + +.bg-purple-100 { + background-color: #5e41d0 !important; } + +.bg-purple-200 { + background-color: #4e26af !important; } + +.bg-purple-300 { + background-color: #381885 !important; } + +.bg-yellow-000 { + background-color: #ffeb82 !important; } + +.bg-yellow-100 { + background-color: #fadf50 !important; } + +.bg-yellow-200 { + background-color: #f7d12e !important; } + +.bg-yellow-300 { + background-color: #e7af06 !important; } + +.bg-red-000 { + background-color: #f77e7e !important; } + +.bg-red-100 { + background-color: #f96e65 !important; } + +.bg-red-200 { + background-color: #e94c4c !important; } + +.bg-red-300 { + background-color: #dd2e2e !important; } + +.d-block { + display: block !important; } + +.d-flex { + display: flex !important; } + +.d-inline { + display: inline !important; } + +.d-inline-block { + display: inline-block !important; } + +.d-none { + display: none !important; } + +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 20rem) { + .d-xs-block { + display: block !important; } + + .d-xs-flex { + display: flex !important; } + + .d-xs-inline { + display: inline !important; } + + .d-xs-inline-block { + display: inline-block !important; } + + .d-xs-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 31.25rem) { + .d-sm-block { + display: block !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 50rem) { + .d-md-block { + display: block !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 66.5rem) { + .d-lg-block { + display: block !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +@media (min-width: 87.5rem) { + .d-xl-block { + display: block !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-none { + display: none !important; } } +.float-left { + float: left !important; } + +.float-right { + float: right !important; } + +.flex-justify-start { + justify-content: flex-start !important; } + +.flex-justify-end { + justify-content: flex-end !important; } + +.flex-justify-between { + justify-content: space-between !important; } + +.flex-justify-around { + justify-content: space-around !important; } + +.v-align-baseline { + vertical-align: baseline !important; } + +.v-align-bottom { + vertical-align: bottom !important; } + +.v-align-middle { + vertical-align: middle !important; } + +.v-align-text-bottom { + vertical-align: text-bottom !important; } + +.v-align-text-top { + vertical-align: text-top !important; } + +.v-align-top { + vertical-align: top !important; } + +.fs-1 { + font-size: 0.5625rem !important; } + @media (min-width: 31.25rem) { + .fs-1 { + font-size: 0.625rem !important; } } + +.fs-2, .bldschedule .schedule-time, .bldschedule .schedule-event .time, +.bldschedule .schedule-event .location { + font-size: 0.6875rem !important; } + @media (min-width: 31.25rem) { + .fs-2, .bldschedule .schedule-time, .bldschedule .schedule-event .time, + .bldschedule .schedule-event .location { + font-size: 0.75rem !important; } } + +.fs-3, .bldschedule .schedule-event .name { + font-size: 0.75rem !important; } + @media (min-width: 31.25rem) { + .fs-3, .bldschedule .schedule-event .name { + font-size: 0.875rem !important; } } + +.fs-4 { + font-size: 0.875rem !important; } + @media (min-width: 31.25rem) { + .fs-4 { + font-size: 1rem !important; } } + +.fs-5 { + font-size: 1rem !important; } + @media (min-width: 31.25rem) { + .fs-5 { + font-size: 1.125rem !important; } } + +.fs-6 { + font-size: 1.125rem !important; } + @media (min-width: 31.25rem) { + .fs-6 { + font-size: 1.5rem !important; + line-height: 1.25; } } + +.fs-7 { + font-size: 1.5rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-7 { + font-size: 2rem !important; } } + +.fs-8 { + font-size: 2rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-8 { + font-size: 2.25rem !important; } } + +.fs-9 { + font-size: 2.25rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-9 { + font-size: 2.625rem !important; } } + +.fs-10 { + font-size: 2.625rem !important; + line-height: 1.25; } + @media (min-width: 31.25rem) { + .fs-10 { + font-size: 3rem !important; } } + +.fw-300 { + font-weight: 300 !important; } + +.fw-400 { + font-weight: 400 !important; } + +.fw-500 { + font-weight: 500 !important; } + +.fw-700, .bldschedule .schedule-event .name { + font-weight: 700 !important; } + +.lh-0 { + line-height: 0 !important; } + +.lh-default { + line-height: 1.4; } + +.lh-tight { + line-height: 1.25; } + +.ls-5 { + letter-spacing: 0.05em !important; } + +.ls-10 { + letter-spacing: 0.1em !important; } + +.ls-0 { + letter-spacing: 0 !important; } + +.text-uppercase { + text-transform: uppercase !important; } + +.list-style-none { + padding: 0 !important; + margin: 0 !important; + list-style: none !important; } + .list-style-none li::before { + display: none !important; } + +.mx-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-0 { + margin: 0 !important; } + +.mt-0 { + margin-top: 0 !important; } + +.mr-0 { + margin-right: 0 !important; } + +.mb-0 { + margin-bottom: 0 !important; } + +.ml-0 { + margin-left: 0 !important; } + +.mx-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + +.my-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + +.mxn-0 { + margin-right: -0 !important; + margin-left: -0 !important; } + +.mx-0-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-1 { + margin: 0.25rem !important; } + +.mt-1 { + margin-top: 0.25rem !important; } + +.mr-1 { + margin-right: 0.25rem !important; } + +.mb-1 { + margin-bottom: 0.25rem !important; } + +.ml-1 { + margin-left: 0.25rem !important; } + +.mx-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + +.my-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + +.mxn-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } + +.mx-1-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-2 { + margin: 0.5rem !important; } + +.mt-2 { + margin-top: 0.5rem !important; } + +.mr-2 { + margin-right: 0.5rem !important; } + +.mb-2 { + margin-bottom: 0.5rem !important; } + +.ml-2 { + margin-left: 0.5rem !important; } + +.mx-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + +.my-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + +.mxn-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } + +.mx-2-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-3 { + margin: 0.75rem !important; } + +.mt-3 { + margin-top: 0.75rem !important; } + +.mr-3 { + margin-right: 0.75rem !important; } + +.mb-3 { + margin-bottom: 0.75rem !important; } + +.ml-3 { + margin-left: 0.75rem !important; } + +.mx-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + +.my-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + +.mxn-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } + +.mx-3-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-4 { + margin: 1rem !important; } + +.mt-4 { + margin-top: 1rem !important; } + +.mr-4 { + margin-right: 1rem !important; } + +.mb-4 { + margin-bottom: 1rem !important; } + +.ml-4 { + margin-left: 1rem !important; } + +.mx-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + +.my-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + +.mxn-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } + +.mx-4-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-5 { + margin: 1.5rem !important; } + +.mt-5 { + margin-top: 1.5rem !important; } + +.mr-5 { + margin-right: 1.5rem !important; } + +.mb-5 { + margin-bottom: 1.5rem !important; } + +.ml-5 { + margin-left: 1.5rem !important; } + +.mx-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + +.my-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + +.mxn-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } + +.mx-5-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-6 { + margin: 2rem !important; } + +.mt-6 { + margin-top: 2rem !important; } + +.mr-6 { + margin-right: 2rem !important; } + +.mb-6 { + margin-bottom: 2rem !important; } + +.ml-6 { + margin-left: 2rem !important; } + +.mx-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + +.my-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + +.mxn-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } + +.mx-6-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-7 { + margin: 2.5rem !important; } + +.mt-7 { + margin-top: 2.5rem !important; } + +.mr-7 { + margin-right: 2.5rem !important; } + +.mb-7 { + margin-bottom: 2.5rem !important; } + +.ml-7 { + margin-left: 2.5rem !important; } + +.mx-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + +.my-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + +.mxn-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } + +.mx-7-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-8 { + margin: 3rem !important; } + +.mt-8 { + margin-top: 3rem !important; } + +.mr-8 { + margin-right: 3rem !important; } + +.mb-8 { + margin-bottom: 3rem !important; } + +.ml-8 { + margin-left: 3rem !important; } + +.mx-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + +.my-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + +.mxn-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } + +.mx-8-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-9 { + margin: 3.5rem !important; } + +.mt-9 { + margin-top: 3.5rem !important; } + +.mr-9 { + margin-right: 3.5rem !important; } + +.mb-9 { + margin-bottom: 3.5rem !important; } + +.ml-9 { + margin-left: 3.5rem !important; } + +.mx-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + +.my-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + +.mxn-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } + +.mx-9-auto { + margin-right: auto !important; + margin-left: auto !important; } + +.m-10 { + margin: 4rem !important; } + +.mt-10 { + margin-top: 4rem !important; } + +.mr-10 { + margin-right: 4rem !important; } + +.mb-10 { + margin-bottom: 4rem !important; } + +.ml-10 { + margin-left: 4rem !important; } + +.mx-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + +.my-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + +.mxn-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } + +.mx-10-auto { + margin-right: auto !important; + margin-left: auto !important; } + +@media (min-width: 20rem) { + .m-xs-0 { + margin: 0 !important; } + + .mt-xs-0 { + margin-top: 0 !important; } + + .mr-xs-0 { + margin-right: 0 !important; } + + .mb-xs-0 { + margin-bottom: 0 !important; } + + .ml-xs-0 { + margin-left: 0 !important; } + + .mx-xs-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-xs-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-xs-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 20rem) { + .m-xs-1 { + margin: 0.25rem !important; } + + .mt-xs-1 { + margin-top: 0.25rem !important; } + + .mr-xs-1 { + margin-right: 0.25rem !important; } + + .mb-xs-1 { + margin-bottom: 0.25rem !important; } + + .ml-xs-1 { + margin-left: 0.25rem !important; } + + .mx-xs-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-xs-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-xs-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 20rem) { + .m-xs-2 { + margin: 0.5rem !important; } + + .mt-xs-2 { + margin-top: 0.5rem !important; } + + .mr-xs-2 { + margin-right: 0.5rem !important; } + + .mb-xs-2 { + margin-bottom: 0.5rem !important; } + + .ml-xs-2 { + margin-left: 0.5rem !important; } + + .mx-xs-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-xs-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-xs-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-3 { + margin: 0.75rem !important; } + + .mt-xs-3 { + margin-top: 0.75rem !important; } + + .mr-xs-3 { + margin-right: 0.75rem !important; } + + .mb-xs-3 { + margin-bottom: 0.75rem !important; } + + .ml-xs-3 { + margin-left: 0.75rem !important; } + + .mx-xs-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-xs-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-xs-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 20rem) { + .m-xs-4 { + margin: 1rem !important; } + + .mt-xs-4 { + margin-top: 1rem !important; } + + .mr-xs-4 { + margin-right: 1rem !important; } + + .mb-xs-4 { + margin-bottom: 1rem !important; } + + .ml-xs-4 { + margin-left: 1rem !important; } + + .mx-xs-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-xs-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-xs-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 20rem) { + .m-xs-5 { + margin: 1.5rem !important; } + + .mt-xs-5 { + margin-top: 1.5rem !important; } + + .mr-xs-5 { + margin-right: 1.5rem !important; } + + .mb-xs-5 { + margin-bottom: 1.5rem !important; } + + .ml-xs-5 { + margin-left: 1.5rem !important; } + + .mx-xs-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-xs-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-xs-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-6 { + margin: 2rem !important; } + + .mt-xs-6 { + margin-top: 2rem !important; } + + .mr-xs-6 { + margin-right: 2rem !important; } + + .mb-xs-6 { + margin-bottom: 2rem !important; } + + .ml-xs-6 { + margin-left: 2rem !important; } + + .mx-xs-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-xs-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-xs-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 20rem) { + .m-xs-7 { + margin: 2.5rem !important; } + + .mt-xs-7 { + margin-top: 2.5rem !important; } + + .mr-xs-7 { + margin-right: 2.5rem !important; } + + .mb-xs-7 { + margin-bottom: 2.5rem !important; } + + .ml-xs-7 { + margin-left: 2.5rem !important; } + + .mx-xs-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-xs-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-xs-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-8 { + margin: 3rem !important; } + + .mt-xs-8 { + margin-top: 3rem !important; } + + .mr-xs-8 { + margin-right: 3rem !important; } + + .mb-xs-8 { + margin-bottom: 3rem !important; } + + .ml-xs-8 { + margin-left: 3rem !important; } + + .mx-xs-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-xs-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-xs-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 20rem) { + .m-xs-9 { + margin: 3.5rem !important; } + + .mt-xs-9 { + margin-top: 3.5rem !important; } + + .mr-xs-9 { + margin-right: 3.5rem !important; } + + .mb-xs-9 { + margin-bottom: 3.5rem !important; } + + .ml-xs-9 { + margin-left: 3.5rem !important; } + + .mx-xs-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-xs-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-xs-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 20rem) { + .m-xs-10 { + margin: 4rem !important; } + + .mt-xs-10 { + margin-top: 4rem !important; } + + .mr-xs-10 { + margin-right: 4rem !important; } + + .mb-xs-10 { + margin-bottom: 4rem !important; } + + .ml-xs-10 { + margin-left: 4rem !important; } + + .mx-xs-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-xs-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-xs-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-0 { + margin: 0 !important; } + + .mt-sm-0 { + margin-top: 0 !important; } + + .mr-sm-0 { + margin-right: 0 !important; } + + .mb-sm-0 { + margin-bottom: 0 !important; } + + .ml-sm-0 { + margin-left: 0 !important; } + + .mx-sm-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-sm-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 31.25rem) { + .m-sm-1 { + margin: 0.25rem !important; } + + .mt-sm-1 { + margin-top: 0.25rem !important; } + + .mr-sm-1 { + margin-right: 0.25rem !important; } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; } + + .ml-sm-1 { + margin-left: 0.25rem !important; } + + .mx-sm-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-sm-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-2 { + margin: 0.5rem !important; } + + .mt-sm-2 { + margin-top: 0.5rem !important; } + + .mr-sm-2 { + margin-right: 0.5rem !important; } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; } + + .ml-sm-2 { + margin-left: 0.5rem !important; } + + .mx-sm-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-sm-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-3 { + margin: 0.75rem !important; } + + .mt-sm-3 { + margin-top: 0.75rem !important; } + + .mr-sm-3 { + margin-right: 0.75rem !important; } + + .mb-sm-3 { + margin-bottom: 0.75rem !important; } + + .ml-sm-3 { + margin-left: 0.75rem !important; } + + .mx-sm-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-sm-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-sm-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-4 { + margin: 1rem !important; } + + .mt-sm-4 { + margin-top: 1rem !important; } + + .mr-sm-4 { + margin-right: 1rem !important; } + + .mb-sm-4 { + margin-bottom: 1rem !important; } + + .ml-sm-4 { + margin-left: 1rem !important; } + + .mx-sm-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-sm-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-sm-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-5 { + margin: 1.5rem !important; } + + .mt-sm-5 { + margin-top: 1.5rem !important; } + + .mr-sm-5 { + margin-right: 1.5rem !important; } + + .mb-sm-5 { + margin-bottom: 1.5rem !important; } + + .ml-sm-5 { + margin-left: 1.5rem !important; } + + .mx-sm-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-sm-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-sm-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-6 { + margin: 2rem !important; } + + .mt-sm-6 { + margin-top: 2rem !important; } + + .mr-sm-6 { + margin-right: 2rem !important; } + + .mb-sm-6 { + margin-bottom: 2rem !important; } + + .ml-sm-6 { + margin-left: 2rem !important; } + + .mx-sm-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-sm-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-sm-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-7 { + margin: 2.5rem !important; } + + .mt-sm-7 { + margin-top: 2.5rem !important; } + + .mr-sm-7 { + margin-right: 2.5rem !important; } + + .mb-sm-7 { + margin-bottom: 2.5rem !important; } + + .ml-sm-7 { + margin-left: 2.5rem !important; } + + .mx-sm-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-sm-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-sm-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-8 { + margin: 3rem !important; } + + .mt-sm-8 { + margin-top: 3rem !important; } + + .mr-sm-8 { + margin-right: 3rem !important; } + + .mb-sm-8 { + margin-bottom: 3rem !important; } + + .ml-sm-8 { + margin-left: 3rem !important; } + + .mx-sm-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-sm-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-sm-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-9 { + margin: 3.5rem !important; } + + .mt-sm-9 { + margin-top: 3.5rem !important; } + + .mr-sm-9 { + margin-right: 3.5rem !important; } + + .mb-sm-9 { + margin-bottom: 3.5rem !important; } + + .ml-sm-9 { + margin-left: 3.5rem !important; } + + .mx-sm-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-sm-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-sm-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 31.25rem) { + .m-sm-10 { + margin: 4rem !important; } + + .mt-sm-10 { + margin-top: 4rem !important; } + + .mr-sm-10 { + margin-right: 4rem !important; } + + .mb-sm-10 { + margin-bottom: 4rem !important; } + + .ml-sm-10 { + margin-left: 4rem !important; } + + .mx-sm-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-sm-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-sm-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 50rem) { + .m-md-0 { + margin: 0 !important; } + + .mt-md-0 { + margin-top: 0 !important; } + + .mr-md-0 { + margin-right: 0 !important; } + + .mb-md-0 { + margin-bottom: 0 !important; } + + .ml-md-0 { + margin-left: 0 !important; } + + .mx-md-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-md-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 50rem) { + .m-md-1 { + margin: 0.25rem !important; } + + .mt-md-1 { + margin-top: 0.25rem !important; } + + .mr-md-1 { + margin-right: 0.25rem !important; } + + .mb-md-1 { + margin-bottom: 0.25rem !important; } + + .ml-md-1 { + margin-left: 0.25rem !important; } + + .mx-md-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-md-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 50rem) { + .m-md-2 { + margin: 0.5rem !important; } + + .mt-md-2 { + margin-top: 0.5rem !important; } + + .mr-md-2 { + margin-right: 0.5rem !important; } + + .mb-md-2 { + margin-bottom: 0.5rem !important; } + + .ml-md-2 { + margin-left: 0.5rem !important; } + + .mx-md-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-md-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 50rem) { + .m-md-3 { + margin: 0.75rem !important; } + + .mt-md-3 { + margin-top: 0.75rem !important; } + + .mr-md-3 { + margin-right: 0.75rem !important; } + + .mb-md-3 { + margin-bottom: 0.75rem !important; } + + .ml-md-3 { + margin-left: 0.75rem !important; } + + .mx-md-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-md-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-md-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 50rem) { + .m-md-4 { + margin: 1rem !important; } + + .mt-md-4 { + margin-top: 1rem !important; } + + .mr-md-4 { + margin-right: 1rem !important; } + + .mb-md-4 { + margin-bottom: 1rem !important; } + + .ml-md-4 { + margin-left: 1rem !important; } + + .mx-md-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-md-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-md-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 50rem) { + .m-md-5 { + margin: 1.5rem !important; } + + .mt-md-5 { + margin-top: 1.5rem !important; } + + .mr-md-5 { + margin-right: 1.5rem !important; } + + .mb-md-5 { + margin-bottom: 1.5rem !important; } + + .ml-md-5 { + margin-left: 1.5rem !important; } + + .mx-md-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-md-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-md-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 50rem) { + .m-md-6 { + margin: 2rem !important; } + + .mt-md-6 { + margin-top: 2rem !important; } + + .mr-md-6 { + margin-right: 2rem !important; } + + .mb-md-6 { + margin-bottom: 2rem !important; } + + .ml-md-6 { + margin-left: 2rem !important; } + + .mx-md-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-md-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-md-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 50rem) { + .m-md-7 { + margin: 2.5rem !important; } + + .mt-md-7 { + margin-top: 2.5rem !important; } + + .mr-md-7 { + margin-right: 2.5rem !important; } + + .mb-md-7 { + margin-bottom: 2.5rem !important; } + + .ml-md-7 { + margin-left: 2.5rem !important; } + + .mx-md-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-md-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-md-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 50rem) { + .m-md-8 { + margin: 3rem !important; } + + .mt-md-8 { + margin-top: 3rem !important; } + + .mr-md-8 { + margin-right: 3rem !important; } + + .mb-md-8 { + margin-bottom: 3rem !important; } + + .ml-md-8 { + margin-left: 3rem !important; } + + .mx-md-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-md-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-md-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 50rem) { + .m-md-9 { + margin: 3.5rem !important; } + + .mt-md-9 { + margin-top: 3.5rem !important; } + + .mr-md-9 { + margin-right: 3.5rem !important; } + + .mb-md-9 { + margin-bottom: 3.5rem !important; } + + .ml-md-9 { + margin-left: 3.5rem !important; } + + .mx-md-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-md-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-md-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 50rem) { + .m-md-10 { + margin: 4rem !important; } + + .mt-md-10 { + margin-top: 4rem !important; } + + .mr-md-10 { + margin-right: 4rem !important; } + + .mb-md-10 { + margin-bottom: 4rem !important; } + + .ml-md-10 { + margin-left: 4rem !important; } + + .mx-md-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-md-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-md-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-0 { + margin: 0 !important; } + + .mt-lg-0 { + margin-top: 0 !important; } + + .mr-lg-0 { + margin-right: 0 !important; } + + .mb-lg-0 { + margin-bottom: 0 !important; } + + .ml-lg-0 { + margin-left: 0 !important; } + + .mx-lg-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-lg-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 66.5rem) { + .m-lg-1 { + margin: 0.25rem !important; } + + .mt-lg-1 { + margin-top: 0.25rem !important; } + + .mr-lg-1 { + margin-right: 0.25rem !important; } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; } + + .ml-lg-1 { + margin-left: 0.25rem !important; } + + .mx-lg-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-lg-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-2 { + margin: 0.5rem !important; } + + .mt-lg-2 { + margin-top: 0.5rem !important; } + + .mr-lg-2 { + margin-right: 0.5rem !important; } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; } + + .ml-lg-2 { + margin-left: 0.5rem !important; } + + .mx-lg-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-lg-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-3 { + margin: 0.75rem !important; } + + .mt-lg-3 { + margin-top: 0.75rem !important; } + + .mr-lg-3 { + margin-right: 0.75rem !important; } + + .mb-lg-3 { + margin-bottom: 0.75rem !important; } + + .ml-lg-3 { + margin-left: 0.75rem !important; } + + .mx-lg-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-lg-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-lg-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-4 { + margin: 1rem !important; } + + .mt-lg-4 { + margin-top: 1rem !important; } + + .mr-lg-4 { + margin-right: 1rem !important; } + + .mb-lg-4 { + margin-bottom: 1rem !important; } + + .ml-lg-4 { + margin-left: 1rem !important; } + + .mx-lg-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-lg-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-lg-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-5 { + margin: 1.5rem !important; } + + .mt-lg-5 { + margin-top: 1.5rem !important; } + + .mr-lg-5 { + margin-right: 1.5rem !important; } + + .mb-lg-5 { + margin-bottom: 1.5rem !important; } + + .ml-lg-5 { + margin-left: 1.5rem !important; } + + .mx-lg-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-lg-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-lg-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-6 { + margin: 2rem !important; } + + .mt-lg-6 { + margin-top: 2rem !important; } + + .mr-lg-6 { + margin-right: 2rem !important; } + + .mb-lg-6 { + margin-bottom: 2rem !important; } + + .ml-lg-6 { + margin-left: 2rem !important; } + + .mx-lg-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-lg-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-lg-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-7 { + margin: 2.5rem !important; } + + .mt-lg-7 { + margin-top: 2.5rem !important; } + + .mr-lg-7 { + margin-right: 2.5rem !important; } + + .mb-lg-7 { + margin-bottom: 2.5rem !important; } + + .ml-lg-7 { + margin-left: 2.5rem !important; } + + .mx-lg-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-lg-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-lg-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-8 { + margin: 3rem !important; } + + .mt-lg-8 { + margin-top: 3rem !important; } + + .mr-lg-8 { + margin-right: 3rem !important; } + + .mb-lg-8 { + margin-bottom: 3rem !important; } + + .ml-lg-8 { + margin-left: 3rem !important; } + + .mx-lg-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-lg-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-lg-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-9 { + margin: 3.5rem !important; } + + .mt-lg-9 { + margin-top: 3.5rem !important; } + + .mr-lg-9 { + margin-right: 3.5rem !important; } + + .mb-lg-9 { + margin-bottom: 3.5rem !important; } + + .ml-lg-9 { + margin-left: 3.5rem !important; } + + .mx-lg-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-lg-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-lg-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 66.5rem) { + .m-lg-10 { + margin: 4rem !important; } + + .mt-lg-10 { + margin-top: 4rem !important; } + + .mr-lg-10 { + margin-right: 4rem !important; } + + .mb-lg-10 { + margin-bottom: 4rem !important; } + + .ml-lg-10 { + margin-left: 4rem !important; } + + .mx-lg-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-lg-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-lg-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-0 { + margin: 0 !important; } + + .mt-xl-0 { + margin-top: 0 !important; } + + .mr-xl-0 { + margin-right: 0 !important; } + + .mb-xl-0 { + margin-bottom: 0 !important; } + + .ml-xl-0 { + margin-left: 0 !important; } + + .mx-xl-0 { + margin-right: 0 !important; + margin-left: 0 !important; } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } + + .mxn-xl-0 { + margin-right: -0 !important; + margin-left: -0 !important; } } +@media (min-width: 87.5rem) { + .m-xl-1 { + margin: 0.25rem !important; } + + .mt-xl-1 { + margin-top: 0.25rem !important; } + + .mr-xl-1 { + margin-right: 0.25rem !important; } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; } + + .ml-xl-1 { + margin-left: 0.25rem !important; } + + .mx-xl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } + + .mxn-xl-1 { + margin-right: -0.25rem !important; + margin-left: -0.25rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-2 { + margin: 0.5rem !important; } + + .mt-xl-2 { + margin-top: 0.5rem !important; } + + .mr-xl-2 { + margin-right: 0.5rem !important; } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; } + + .ml-xl-2 { + margin-left: 0.5rem !important; } + + .mx-xl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } + + .mxn-xl-2 { + margin-right: -0.5rem !important; + margin-left: -0.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-3 { + margin: 0.75rem !important; } + + .mt-xl-3 { + margin-top: 0.75rem !important; } + + .mr-xl-3 { + margin-right: 0.75rem !important; } + + .mb-xl-3 { + margin-bottom: 0.75rem !important; } + + .ml-xl-3 { + margin-left: 0.75rem !important; } + + .mx-xl-3 { + margin-right: 0.75rem !important; + margin-left: 0.75rem !important; } + + .my-xl-3 { + margin-top: 0.75rem !important; + margin-bottom: 0.75rem !important; } + + .mxn-xl-3 { + margin-right: -0.75rem !important; + margin-left: -0.75rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-4 { + margin: 1rem !important; } + + .mt-xl-4 { + margin-top: 1rem !important; } + + .mr-xl-4 { + margin-right: 1rem !important; } + + .mb-xl-4 { + margin-bottom: 1rem !important; } + + .ml-xl-4 { + margin-left: 1rem !important; } + + .mx-xl-4 { + margin-right: 1rem !important; + margin-left: 1rem !important; } + + .my-xl-4 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } + + .mxn-xl-4 { + margin-right: -1rem !important; + margin-left: -1rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-5 { + margin: 1.5rem !important; } + + .mt-xl-5 { + margin-top: 1.5rem !important; } + + .mr-xl-5 { + margin-right: 1.5rem !important; } + + .mb-xl-5 { + margin-bottom: 1.5rem !important; } + + .ml-xl-5 { + margin-left: 1.5rem !important; } + + .mx-xl-5 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } + + .my-xl-5 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } + + .mxn-xl-5 { + margin-right: -1.5rem !important; + margin-left: -1.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-6 { + margin: 2rem !important; } + + .mt-xl-6 { + margin-top: 2rem !important; } + + .mr-xl-6 { + margin-right: 2rem !important; } + + .mb-xl-6 { + margin-bottom: 2rem !important; } + + .ml-xl-6 { + margin-left: 2rem !important; } + + .mx-xl-6 { + margin-right: 2rem !important; + margin-left: 2rem !important; } + + .my-xl-6 { + margin-top: 2rem !important; + margin-bottom: 2rem !important; } + + .mxn-xl-6 { + margin-right: -2rem !important; + margin-left: -2rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-7 { + margin: 2.5rem !important; } + + .mt-xl-7 { + margin-top: 2.5rem !important; } + + .mr-xl-7 { + margin-right: 2.5rem !important; } + + .mb-xl-7 { + margin-bottom: 2.5rem !important; } + + .ml-xl-7 { + margin-left: 2.5rem !important; } + + .mx-xl-7 { + margin-right: 2.5rem !important; + margin-left: 2.5rem !important; } + + .my-xl-7 { + margin-top: 2.5rem !important; + margin-bottom: 2.5rem !important; } + + .mxn-xl-7 { + margin-right: -2.5rem !important; + margin-left: -2.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-8 { + margin: 3rem !important; } + + .mt-xl-8 { + margin-top: 3rem !important; } + + .mr-xl-8 { + margin-right: 3rem !important; } + + .mb-xl-8 { + margin-bottom: 3rem !important; } + + .ml-xl-8 { + margin-left: 3rem !important; } + + .mx-xl-8 { + margin-right: 3rem !important; + margin-left: 3rem !important; } + + .my-xl-8 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } + + .mxn-xl-8 { + margin-right: -3rem !important; + margin-left: -3rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-9 { + margin: 3.5rem !important; } + + .mt-xl-9 { + margin-top: 3.5rem !important; } + + .mr-xl-9 { + margin-right: 3.5rem !important; } + + .mb-xl-9 { + margin-bottom: 3.5rem !important; } + + .ml-xl-9 { + margin-left: 3.5rem !important; } + + .mx-xl-9 { + margin-right: 3.5rem !important; + margin-left: 3.5rem !important; } + + .my-xl-9 { + margin-top: 3.5rem !important; + margin-bottom: 3.5rem !important; } + + .mxn-xl-9 { + margin-right: -3.5rem !important; + margin-left: -3.5rem !important; } } +@media (min-width: 87.5rem) { + .m-xl-10 { + margin: 4rem !important; } + + .mt-xl-10 { + margin-top: 4rem !important; } + + .mr-xl-10 { + margin-right: 4rem !important; } + + .mb-xl-10 { + margin-bottom: 4rem !important; } + + .ml-xl-10 { + margin-left: 4rem !important; } + + .mx-xl-10 { + margin-right: 4rem !important; + margin-left: 4rem !important; } + + .my-xl-10 { + margin-top: 4rem !important; + margin-bottom: 4rem !important; } + + .mxn-xl-10 { + margin-right: -4rem !important; + margin-left: -4rem !important; } } +.p-0 { + padding: 0 !important; } + +.pt-0 { + padding-top: 0 !important; } + +.pr-0 { + padding-right: 0 !important; } + +.pb-0 { + padding-bottom: 0 !important; } + +.pl-0 { + padding-left: 0 !important; } + +.px-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + +.py-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + +.p-1 { + padding: 0.25rem !important; } + +.pt-1 { + padding-top: 0.25rem !important; } + +.pr-1 { + padding-right: 0.25rem !important; } + +.pb-1 { + padding-bottom: 0.25rem !important; } + +.pl-1 { + padding-left: 0.25rem !important; } + +.px-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + +.py-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + +.p-2 { + padding: 0.5rem !important; } + +.pt-2 { + padding-top: 0.5rem !important; } + +.pr-2 { + padding-right: 0.5rem !important; } + +.pb-2 { + padding-bottom: 0.5rem !important; } + +.pl-2 { + padding-left: 0.5rem !important; } + +.px-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + +.py-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + +.p-3 { + padding: 0.75rem !important; } + +.pt-3 { + padding-top: 0.75rem !important; } + +.pr-3 { + padding-right: 0.75rem !important; } + +.pb-3 { + padding-bottom: 0.75rem !important; } + +.pl-3 { + padding-left: 0.75rem !important; } + +.px-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + +.py-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + +.p-4 { + padding: 1rem !important; } + +.pt-4 { + padding-top: 1rem !important; } + +.pr-4 { + padding-right: 1rem !important; } + +.pb-4 { + padding-bottom: 1rem !important; } + +.pl-4 { + padding-left: 1rem !important; } + +.px-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + +.py-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + +.p-5 { + padding: 1.5rem !important; } + +.pt-5 { + padding-top: 1.5rem !important; } + +.pr-5 { + padding-right: 1.5rem !important; } + +.pb-5 { + padding-bottom: 1.5rem !important; } + +.pl-5 { + padding-left: 1.5rem !important; } + +.px-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + +.py-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + +.p-6 { + padding: 2rem !important; } + +.pt-6 { + padding-top: 2rem !important; } + +.pr-6 { + padding-right: 2rem !important; } + +.pb-6 { + padding-bottom: 2rem !important; } + +.pl-6 { + padding-left: 2rem !important; } + +.px-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + +.py-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + +.p-7 { + padding: 2.5rem !important; } + +.pt-7 { + padding-top: 2.5rem !important; } + +.pr-7 { + padding-right: 2.5rem !important; } + +.pb-7 { + padding-bottom: 2.5rem !important; } + +.pl-7 { + padding-left: 2.5rem !important; } + +.px-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + +.py-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + +.p-8 { + padding: 3rem !important; } + +.pt-8 { + padding-top: 3rem !important; } + +.pr-8 { + padding-right: 3rem !important; } + +.pb-8 { + padding-bottom: 3rem !important; } + +.pl-8 { + padding-left: 3rem !important; } + +.px-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + +.py-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + +.p-9 { + padding: 3.5rem !important; } + +.pt-9 { + padding-top: 3.5rem !important; } + +.pr-9 { + padding-right: 3.5rem !important; } + +.pb-9 { + padding-bottom: 3.5rem !important; } + +.pl-9 { + padding-left: 3.5rem !important; } + +.px-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + +.py-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + +.p-10 { + padding: 4rem !important; } + +.pt-10 { + padding-top: 4rem !important; } + +.pr-10 { + padding-right: 4rem !important; } + +.pb-10 { + padding-bottom: 4rem !important; } + +.pl-10 { + padding-left: 4rem !important; } + +.px-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + +.py-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } + +@media (min-width: 20rem) { + .p-xs-0 { + padding: 0 !important; } + + .pt-xs-0 { + padding-top: 0 !important; } + + .pr-xs-0 { + padding-right: 0 !important; } + + .pb-xs-0 { + padding-bottom: 0 !important; } + + .pl-xs-0 { + padding-left: 0 !important; } + + .px-xs-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-xs-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-xs-1 { + padding: 0.25rem !important; } + + .pt-xs-1 { + padding-top: 0.25rem !important; } + + .pr-xs-1 { + padding-right: 0.25rem !important; } + + .pb-xs-1 { + padding-bottom: 0.25rem !important; } + + .pl-xs-1 { + padding-left: 0.25rem !important; } + + .px-xs-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-xs-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-xs-2 { + padding: 0.5rem !important; } + + .pt-xs-2 { + padding-top: 0.5rem !important; } + + .pr-xs-2 { + padding-right: 0.5rem !important; } + + .pb-xs-2 { + padding-bottom: 0.5rem !important; } + + .pl-xs-2 { + padding-left: 0.5rem !important; } + + .px-xs-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-xs-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-xs-3 { + padding: 0.75rem !important; } + + .pt-xs-3 { + padding-top: 0.75rem !important; } + + .pr-xs-3 { + padding-right: 0.75rem !important; } + + .pb-xs-3 { + padding-bottom: 0.75rem !important; } + + .pl-xs-3 { + padding-left: 0.75rem !important; } + + .px-xs-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-xs-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-xs-4 { + padding: 1rem !important; } + + .pt-xs-4 { + padding-top: 1rem !important; } + + .pr-xs-4 { + padding-right: 1rem !important; } + + .pb-xs-4 { + padding-bottom: 1rem !important; } + + .pl-xs-4 { + padding-left: 1rem !important; } + + .px-xs-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-xs-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-xs-5 { + padding: 1.5rem !important; } + + .pt-xs-5 { + padding-top: 1.5rem !important; } + + .pr-xs-5 { + padding-right: 1.5rem !important; } + + .pb-xs-5 { + padding-bottom: 1.5rem !important; } + + .pl-xs-5 { + padding-left: 1.5rem !important; } + + .px-xs-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-xs-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-xs-6 { + padding: 2rem !important; } + + .pt-xs-6 { + padding-top: 2rem !important; } + + .pr-xs-6 { + padding-right: 2rem !important; } + + .pb-xs-6 { + padding-bottom: 2rem !important; } + + .pl-xs-6 { + padding-left: 2rem !important; } + + .px-xs-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-xs-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-xs-7 { + padding: 2.5rem !important; } + + .pt-xs-7 { + padding-top: 2.5rem !important; } + + .pr-xs-7 { + padding-right: 2.5rem !important; } + + .pb-xs-7 { + padding-bottom: 2.5rem !important; } + + .pl-xs-7 { + padding-left: 2.5rem !important; } + + .px-xs-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-xs-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-xs-8 { + padding: 3rem !important; } + + .pt-xs-8 { + padding-top: 3rem !important; } + + .pr-xs-8 { + padding-right: 3rem !important; } + + .pb-xs-8 { + padding-bottom: 3rem !important; } + + .pl-xs-8 { + padding-left: 3rem !important; } + + .px-xs-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-xs-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-xs-9 { + padding: 3.5rem !important; } + + .pt-xs-9 { + padding-top: 3.5rem !important; } + + .pr-xs-9 { + padding-right: 3.5rem !important; } + + .pb-xs-9 { + padding-bottom: 3.5rem !important; } + + .pl-xs-9 { + padding-left: 3.5rem !important; } + + .px-xs-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-xs-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-xs-10 { + padding: 4rem !important; } + + .pt-xs-10 { + padding-top: 4rem !important; } + + .pr-xs-10 { + padding-right: 4rem !important; } + + .pb-xs-10 { + padding-bottom: 4rem !important; } + + .pl-xs-10 { + padding-left: 4rem !important; } + + .px-xs-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-xs-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 31.25rem) { + .p-sm-0 { + padding: 0 !important; } + + .pt-sm-0 { + padding-top: 0 !important; } + + .pr-sm-0 { + padding-right: 0 !important; } + + .pb-sm-0 { + padding-bottom: 0 !important; } + + .pl-sm-0 { + padding-left: 0 !important; } + + .px-sm-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-sm-1 { + padding: 0.25rem !important; } + + .pt-sm-1 { + padding-top: 0.25rem !important; } + + .pr-sm-1 { + padding-right: 0.25rem !important; } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; } + + .pl-sm-1 { + padding-left: 0.25rem !important; } + + .px-sm-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-sm-2 { + padding: 0.5rem !important; } + + .pt-sm-2 { + padding-top: 0.5rem !important; } + + .pr-sm-2 { + padding-right: 0.5rem !important; } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; } + + .pl-sm-2 { + padding-left: 0.5rem !important; } + + .px-sm-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-sm-3 { + padding: 0.75rem !important; } + + .pt-sm-3 { + padding-top: 0.75rem !important; } + + .pr-sm-3 { + padding-right: 0.75rem !important; } + + .pb-sm-3 { + padding-bottom: 0.75rem !important; } + + .pl-sm-3 { + padding-left: 0.75rem !important; } + + .px-sm-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-sm-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-sm-4 { + padding: 1rem !important; } + + .pt-sm-4 { + padding-top: 1rem !important; } + + .pr-sm-4 { + padding-right: 1rem !important; } + + .pb-sm-4 { + padding-bottom: 1rem !important; } + + .pl-sm-4 { + padding-left: 1rem !important; } + + .px-sm-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-sm-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-sm-5 { + padding: 1.5rem !important; } + + .pt-sm-5 { + padding-top: 1.5rem !important; } + + .pr-sm-5 { + padding-right: 1.5rem !important; } + + .pb-sm-5 { + padding-bottom: 1.5rem !important; } + + .pl-sm-5 { + padding-left: 1.5rem !important; } + + .px-sm-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-sm-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-sm-6 { + padding: 2rem !important; } + + .pt-sm-6 { + padding-top: 2rem !important; } + + .pr-sm-6 { + padding-right: 2rem !important; } + + .pb-sm-6 { + padding-bottom: 2rem !important; } + + .pl-sm-6 { + padding-left: 2rem !important; } + + .px-sm-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-sm-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-sm-7 { + padding: 2.5rem !important; } + + .pt-sm-7 { + padding-top: 2.5rem !important; } + + .pr-sm-7 { + padding-right: 2.5rem !important; } + + .pb-sm-7 { + padding-bottom: 2.5rem !important; } + + .pl-sm-7 { + padding-left: 2.5rem !important; } + + .px-sm-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-sm-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-sm-8 { + padding: 3rem !important; } + + .pt-sm-8 { + padding-top: 3rem !important; } + + .pr-sm-8 { + padding-right: 3rem !important; } + + .pb-sm-8 { + padding-bottom: 3rem !important; } + + .pl-sm-8 { + padding-left: 3rem !important; } + + .px-sm-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-sm-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-sm-9 { + padding: 3.5rem !important; } + + .pt-sm-9 { + padding-top: 3.5rem !important; } + + .pr-sm-9 { + padding-right: 3.5rem !important; } + + .pb-sm-9 { + padding-bottom: 3.5rem !important; } + + .pl-sm-9 { + padding-left: 3.5rem !important; } + + .px-sm-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-sm-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-sm-10 { + padding: 4rem !important; } + + .pt-sm-10 { + padding-top: 4rem !important; } + + .pr-sm-10 { + padding-right: 4rem !important; } + + .pb-sm-10 { + padding-bottom: 4rem !important; } + + .pl-sm-10 { + padding-left: 4rem !important; } + + .px-sm-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-sm-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 50rem) { + .p-md-0 { + padding: 0 !important; } + + .pt-md-0 { + padding-top: 0 !important; } + + .pr-md-0 { + padding-right: 0 !important; } + + .pb-md-0 { + padding-bottom: 0 !important; } + + .pl-md-0 { + padding-left: 0 !important; } + + .px-md-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-md-1 { + padding: 0.25rem !important; } + + .pt-md-1 { + padding-top: 0.25rem !important; } + + .pr-md-1 { + padding-right: 0.25rem !important; } + + .pb-md-1 { + padding-bottom: 0.25rem !important; } + + .pl-md-1 { + padding-left: 0.25rem !important; } + + .px-md-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-md-2 { + padding: 0.5rem !important; } + + .pt-md-2 { + padding-top: 0.5rem !important; } + + .pr-md-2 { + padding-right: 0.5rem !important; } + + .pb-md-2 { + padding-bottom: 0.5rem !important; } + + .pl-md-2 { + padding-left: 0.5rem !important; } + + .px-md-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-md-3 { + padding: 0.75rem !important; } + + .pt-md-3 { + padding-top: 0.75rem !important; } + + .pr-md-3 { + padding-right: 0.75rem !important; } + + .pb-md-3 { + padding-bottom: 0.75rem !important; } + + .pl-md-3 { + padding-left: 0.75rem !important; } + + .px-md-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-md-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-md-4 { + padding: 1rem !important; } + + .pt-md-4 { + padding-top: 1rem !important; } + + .pr-md-4 { + padding-right: 1rem !important; } + + .pb-md-4 { + padding-bottom: 1rem !important; } + + .pl-md-4 { + padding-left: 1rem !important; } + + .px-md-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-md-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-md-5 { + padding: 1.5rem !important; } + + .pt-md-5 { + padding-top: 1.5rem !important; } + + .pr-md-5 { + padding-right: 1.5rem !important; } + + .pb-md-5 { + padding-bottom: 1.5rem !important; } + + .pl-md-5 { + padding-left: 1.5rem !important; } + + .px-md-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-md-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-md-6 { + padding: 2rem !important; } + + .pt-md-6 { + padding-top: 2rem !important; } + + .pr-md-6 { + padding-right: 2rem !important; } + + .pb-md-6 { + padding-bottom: 2rem !important; } + + .pl-md-6 { + padding-left: 2rem !important; } + + .px-md-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-md-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-md-7 { + padding: 2.5rem !important; } + + .pt-md-7 { + padding-top: 2.5rem !important; } + + .pr-md-7 { + padding-right: 2.5rem !important; } + + .pb-md-7 { + padding-bottom: 2.5rem !important; } + + .pl-md-7 { + padding-left: 2.5rem !important; } + + .px-md-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-md-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-md-8 { + padding: 3rem !important; } + + .pt-md-8 { + padding-top: 3rem !important; } + + .pr-md-8 { + padding-right: 3rem !important; } + + .pb-md-8 { + padding-bottom: 3rem !important; } + + .pl-md-8 { + padding-left: 3rem !important; } + + .px-md-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-md-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-md-9 { + padding: 3.5rem !important; } + + .pt-md-9 { + padding-top: 3.5rem !important; } + + .pr-md-9 { + padding-right: 3.5rem !important; } + + .pb-md-9 { + padding-bottom: 3.5rem !important; } + + .pl-md-9 { + padding-left: 3.5rem !important; } + + .px-md-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-md-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-md-10 { + padding: 4rem !important; } + + .pt-md-10 { + padding-top: 4rem !important; } + + .pr-md-10 { + padding-right: 4rem !important; } + + .pb-md-10 { + padding-bottom: 4rem !important; } + + .pl-md-10 { + padding-left: 4rem !important; } + + .px-md-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-md-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 66.5rem) { + .p-lg-0 { + padding: 0 !important; } + + .pt-lg-0 { + padding-top: 0 !important; } + + .pr-lg-0 { + padding-right: 0 !important; } + + .pb-lg-0 { + padding-bottom: 0 !important; } + + .pl-lg-0 { + padding-left: 0 !important; } + + .px-lg-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-lg-1 { + padding: 0.25rem !important; } + + .pt-lg-1 { + padding-top: 0.25rem !important; } + + .pr-lg-1 { + padding-right: 0.25rem !important; } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; } + + .pl-lg-1 { + padding-left: 0.25rem !important; } + + .px-lg-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-lg-2 { + padding: 0.5rem !important; } + + .pt-lg-2 { + padding-top: 0.5rem !important; } + + .pr-lg-2 { + padding-right: 0.5rem !important; } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; } + + .pl-lg-2 { + padding-left: 0.5rem !important; } + + .px-lg-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-lg-3 { + padding: 0.75rem !important; } + + .pt-lg-3 { + padding-top: 0.75rem !important; } + + .pr-lg-3 { + padding-right: 0.75rem !important; } + + .pb-lg-3 { + padding-bottom: 0.75rem !important; } + + .pl-lg-3 { + padding-left: 0.75rem !important; } + + .px-lg-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-lg-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-lg-4 { + padding: 1rem !important; } + + .pt-lg-4 { + padding-top: 1rem !important; } + + .pr-lg-4 { + padding-right: 1rem !important; } + + .pb-lg-4 { + padding-bottom: 1rem !important; } + + .pl-lg-4 { + padding-left: 1rem !important; } + + .px-lg-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-lg-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-lg-5 { + padding: 1.5rem !important; } + + .pt-lg-5 { + padding-top: 1.5rem !important; } + + .pr-lg-5 { + padding-right: 1.5rem !important; } + + .pb-lg-5 { + padding-bottom: 1.5rem !important; } + + .pl-lg-5 { + padding-left: 1.5rem !important; } + + .px-lg-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-lg-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-lg-6 { + padding: 2rem !important; } + + .pt-lg-6 { + padding-top: 2rem !important; } + + .pr-lg-6 { + padding-right: 2rem !important; } + + .pb-lg-6 { + padding-bottom: 2rem !important; } + + .pl-lg-6 { + padding-left: 2rem !important; } + + .px-lg-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-lg-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-lg-7 { + padding: 2.5rem !important; } + + .pt-lg-7 { + padding-top: 2.5rem !important; } + + .pr-lg-7 { + padding-right: 2.5rem !important; } + + .pb-lg-7 { + padding-bottom: 2.5rem !important; } + + .pl-lg-7 { + padding-left: 2.5rem !important; } + + .px-lg-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-lg-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-lg-8 { + padding: 3rem !important; } + + .pt-lg-8 { + padding-top: 3rem !important; } + + .pr-lg-8 { + padding-right: 3rem !important; } + + .pb-lg-8 { + padding-bottom: 3rem !important; } + + .pl-lg-8 { + padding-left: 3rem !important; } + + .px-lg-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-lg-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-lg-9 { + padding: 3.5rem !important; } + + .pt-lg-9 { + padding-top: 3.5rem !important; } + + .pr-lg-9 { + padding-right: 3.5rem !important; } + + .pb-lg-9 { + padding-bottom: 3.5rem !important; } + + .pl-lg-9 { + padding-left: 3.5rem !important; } + + .px-lg-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-lg-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-lg-10 { + padding: 4rem !important; } + + .pt-lg-10 { + padding-top: 4rem !important; } + + .pr-lg-10 { + padding-right: 4rem !important; } + + .pb-lg-10 { + padding-bottom: 4rem !important; } + + .pl-lg-10 { + padding-left: 4rem !important; } + + .px-lg-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-lg-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media (min-width: 87.5rem) { + .p-xl-0 { + padding: 0 !important; } + + .pt-xl-0 { + padding-top: 0 !important; } + + .pr-xl-0 { + padding-right: 0 !important; } + + .pb-xl-0 { + padding-bottom: 0 !important; } + + .pl-xl-0 { + padding-left: 0 !important; } + + .px-xl-0 { + padding-right: 0 !important; + padding-left: 0 !important; } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } + + .p-xl-1 { + padding: 0.25rem !important; } + + .pt-xl-1 { + padding-top: 0.25rem !important; } + + .pr-xl-1 { + padding-right: 0.25rem !important; } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; } + + .pl-xl-1 { + padding-left: 0.25rem !important; } + + .px-xl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } + + .p-xl-2 { + padding: 0.5rem !important; } + + .pt-xl-2 { + padding-top: 0.5rem !important; } + + .pr-xl-2 { + padding-right: 0.5rem !important; } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; } + + .pl-xl-2 { + padding-left: 0.5rem !important; } + + .px-xl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } + + .p-xl-3 { + padding: 0.75rem !important; } + + .pt-xl-3 { + padding-top: 0.75rem !important; } + + .pr-xl-3 { + padding-right: 0.75rem !important; } + + .pb-xl-3 { + padding-bottom: 0.75rem !important; } + + .pl-xl-3 { + padding-left: 0.75rem !important; } + + .px-xl-3 { + padding-right: 0.75rem !important; + padding-left: 0.75rem !important; } + + .py-xl-3 { + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; } + + .p-xl-4 { + padding: 1rem !important; } + + .pt-xl-4 { + padding-top: 1rem !important; } + + .pr-xl-4 { + padding-right: 1rem !important; } + + .pb-xl-4 { + padding-bottom: 1rem !important; } + + .pl-xl-4 { + padding-left: 1rem !important; } + + .px-xl-4 { + padding-right: 1rem !important; + padding-left: 1rem !important; } + + .py-xl-4 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } + + .p-xl-5 { + padding: 1.5rem !important; } + + .pt-xl-5 { + padding-top: 1.5rem !important; } + + .pr-xl-5 { + padding-right: 1.5rem !important; } + + .pb-xl-5 { + padding-bottom: 1.5rem !important; } + + .pl-xl-5 { + padding-left: 1.5rem !important; } + + .px-xl-5 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } + + .py-xl-5 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } + + .p-xl-6 { + padding: 2rem !important; } + + .pt-xl-6 { + padding-top: 2rem !important; } + + .pr-xl-6 { + padding-right: 2rem !important; } + + .pb-xl-6 { + padding-bottom: 2rem !important; } + + .pl-xl-6 { + padding-left: 2rem !important; } + + .px-xl-6 { + padding-right: 2rem !important; + padding-left: 2rem !important; } + + .py-xl-6 { + padding-top: 2rem !important; + padding-bottom: 2rem !important; } + + .p-xl-7 { + padding: 2.5rem !important; } + + .pt-xl-7 { + padding-top: 2.5rem !important; } + + .pr-xl-7 { + padding-right: 2.5rem !important; } + + .pb-xl-7 { + padding-bottom: 2.5rem !important; } + + .pl-xl-7 { + padding-left: 2.5rem !important; } + + .px-xl-7 { + padding-right: 2.5rem !important; + padding-left: 2.5rem !important; } + + .py-xl-7 { + padding-top: 2.5rem !important; + padding-bottom: 2.5rem !important; } + + .p-xl-8 { + padding: 3rem !important; } + + .pt-xl-8 { + padding-top: 3rem !important; } + + .pr-xl-8 { + padding-right: 3rem !important; } + + .pb-xl-8 { + padding-bottom: 3rem !important; } + + .pl-xl-8 { + padding-left: 3rem !important; } + + .px-xl-8 { + padding-right: 3rem !important; + padding-left: 3rem !important; } + + .py-xl-8 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } + + .p-xl-9 { + padding: 3.5rem !important; } + + .pt-xl-9 { + padding-top: 3.5rem !important; } + + .pr-xl-9 { + padding-right: 3.5rem !important; } + + .pb-xl-9 { + padding-bottom: 3.5rem !important; } + + .pl-xl-9 { + padding-left: 3.5rem !important; } + + .px-xl-9 { + padding-right: 3.5rem !important; + padding-left: 3.5rem !important; } + + .py-xl-9 { + padding-top: 3.5rem !important; + padding-bottom: 3.5rem !important; } + + .p-xl-10 { + padding: 4rem !important; } + + .pt-xl-10 { + padding-top: 4rem !important; } + + .pr-xl-10 { + padding-right: 4rem !important; } + + .pb-xl-10 { + padding-bottom: 4rem !important; } + + .pl-xl-10 { + padding-left: 4rem !important; } + + .px-xl-10 { + padding-right: 4rem !important; + padding-left: 4rem !important; } + + .py-xl-10 { + padding-top: 4rem !important; + padding-bottom: 4rem !important; } } +@media print { + .site-footer, + .site-button, + #edit-this-page, + #back-to-top, + .site-nav, + .main-header { + display: none !important; } + + .side-bar { + width: 100%; + height: auto; + border-right: 0 !important; } + + .site-header { + border-bottom: 1px solid #eeebee; } + + .site-title { + font-size: 1rem !important; + font-weight: 700 !important; } + + .text-small { + font-size: 8pt !important; } + + pre.highlight { + border: 1px solid #eeebee; } + + .main { + max-width: none; + margin-left: 0; } } +a.skip-to-main { + left: -999px; + position: absolute; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + z-index: -999; } + +a.skip-to-main:focus, +a.skip-to-main:active { + color: #7253ed; + background-color: #fff; + left: auto; + top: auto; + width: 30%; + height: auto; + overflow: auto; + margin: 10px 35%; + padding: 5px; + border-radius: 15px; + border: 4px solid #5e41d0; + text-align: center; + font-size: 1.2em; + z-index: 999; } + +div.opaque { + background-color: #fff; } + +p.warning, blockquote.warning { + background: rgba(247, 126, 126, 0.2); + border-left: 4px solid #dd2e2e; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: .8rem; } + p.warning::before, blockquote.warning::before { + color: #dd2e2e; + content: "Warning"; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + p.warning > .warning-title, blockquote.warning > .warning-title { + color: #dd2e2e; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + +p.warning-title, blockquote.warning-title { + background: rgba(247, 126, 126, 0.2); + border-left: 4px solid #dd2e2e; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08); + padding: .8rem; } + p.warning-title > p:first-child, blockquote.warning-title > p:first-child { + margin-top: 0; + margin-bottom: 0; + color: #dd2e2e; + display: block; + font-weight: bold; + text-transform: uppercase; + font-size: .75em; + padding-bottom: .125rem; } + +blockquote.warning { + margin-left: 0; + margin-right: 0; } + blockquote.warning > p:first-child { + margin-top: 0; } + blockquote.warning > p:last-child { + margin-bottom: 0; } + +blockquote.warning-title { + margin-left: 0; + margin-right: 0; } + blockquote.warning-title > p:nth-child(2) { + margin-top: 0; } + blockquote.warning-title > p:last-child { + margin-bottom: 0; } + +.announcement, .main-content .module, +.module { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07), 0 4px 14px rgba(0, 0, 0, 0.05); + margin: 1rem -1rem; + display: flex; + flex-direction: column; + min-width: 0; + padding: 0 1rem; + position: relative; + word-wrap: break-word; } + @media (min-width: 50rem) { + .announcement, .main-content .module, + .module { + border-radius: 4px; + margin: 1rem 0; } } + .announcement > :first-child, .main-content .module > :first-child, + .module > :first-child { + border-top: none !important; } + .announcement > :last-child, .main-content .module > :last-child, + .module > :last-child { + border-bottom: none !important; } + .announcement .label, + .module .label, .announcement .staffer .staffer-pronouns, .staffer .announcement .staffer-pronouns, + .module .staffer .staffer-pronouns, + .staffer .module .staffer-pronouns, .announcement div.highlighter-rouge[overlay]::after, + .module div.highlighter-rouge[overlay]::after { + border-radius: 4px; + margin-left: 0; + user-select: none; } + +.main-content .module h1:first-child, +.main-content .module h2:first-child, +.main-content .module h3:first-child, +.main-content .module h4:first-child, +.main-content .module h5:first-child, +.main-content .module h6:first-child, +.module h1:first-child, +.module h2:first-child, +.module h3:first-child, +.module h4:first-child, +.module h5:first-child, +.module h6:first-child { + margin-top: 1rem; } +.main-content .module > dl, +.module > dl { + border-bottom: 1px solid #eeebee; + border-top: 1px solid #eeebee; + display: grid; + grid-template-columns: max-content 1fr; + margin: 0.5rem -1rem; } + .main-content .module > dl:first-child, + .module > dl:first-child { + margin-top: 0; } + .main-content .module > dl:last-child, + .module > dl:last-child { + margin-bottom: 0; } + @media (min-width: 66.5rem) { + .main-content .module > dl, + .module > dl { + grid-template-columns: 1fr 7fr; } } + .main-content .module > dl > dt, + .main-content .module > dl > dd, + .main-content .module > dl > dt, + .module > dl > dt, + .main-content .module > dl > dd, + .module > dl > dd { + margin: 0; + padding: 0.5rem; } + @media (min-width: 31.25rem) { + .main-content .module > dl > dt, + .main-content .module > dl > dd, + .main-content .module > dl > dt, + .module > dl > dt, + .main-content .module > dl > dd, + .module > dl > dd { + padding: 0.5rem 1rem; } } + .main-content .module > dl > dt, + .module > dl > dt { + border-top: 1px solid #eeebee; + font-weight: normal; + text-align: right; } + .main-content .module > dl > dt + dd, + .module > dl > dt + dd { + border-top: 1px solid #eeebee; } + .main-content .module > dl > dt:first-child, + .module > dl > dt:first-child { + border-top: none; } + .main-content .module > dl > dt:first-child + dd, + .module > dl > dt:first-child + dd { + border-top: none; } + .main-content .module > dl > dt::after, + .module > dl > dt::after { + content: ":"; } + .main-content .module > dl > dd + dd, + .module > dl > dd + dd { + padding-top: 0; } + .main-content .module > dl > dd ol, .main-content .module > dl > dd ul, .main-content .module > dl > dd dl, + .module > dl > dd ol, + .module > dl > dd ul, + .module > dl > dd dl { + margin: 0; } + .main-content .module > dl > dd dl, + .module > dl > dd dl { + display: flex; + flex-direction: column; } + @media (min-width: 31.25rem) { + .main-content .module > dl > dd dl, + .module > dl > dd dl { + flex-direction: row; } } + .main-content .module > dl > dd dl dt, + .module > dl > dd dl dt { + flex: 0 0 62.5%; + margin: 0; } + .main-content .module > dl > dd dl dd, + .module > dl > dd dl dd { + margin: 0; } + +.schedule td { + min-width: 0; + padding: 0; + vertical-align: top; } +.schedule .note { + font-style: italic; } +.schedule .holiday { + background-color: #efefef; + padding: 5px; } +.schedule .assignment { + border: 1px dashed red; + padding: 5px; } +.schedule .day { + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; + padding: 4px; } +.schedule .week-label { + min-width: 80px; + vertical-align: middle; + padding: 4px; + text-align: center; } +.schedule .content { + flex-grow: 100; } +.schedule .date { + font-weight: bolder; } + +.bldschedule { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07), 0 4px 14px rgba(0, 0, 0, 0.05); + margin: 1rem -1rem; + overflow-x: scroll; + position: relative; } + @media (min-width: 50rem) { + .bldschedule { + border-radius: 4px; + margin: 1rem 0; } } + .bldschedule li::before { + display: none; } + .bldschedule ul.schedule-timeline, + .bldschedule ul.schedule-group, + .bldschedule ul.schedule-events { + margin-top: 0; + padding-left: 0; } + .bldschedule ul.schedule-timeline { + margin: 40px auto 0; + position: absolute; + width: 100%; } + .bldschedule .schedule-time { + color: #959396; + height: 40px; + margin: 0; + padding: 0.5rem; + position: relative; } + .bldschedule .schedule-time::after { + background-color: #eeebee; + content: ''; + height: 1px; + left: 0; + position: absolute; + top: 0; + width: 100%; } + .bldschedule .schedule-group { + display: flex; + margin-bottom: 0; + position: relative; } + .bldschedule .schedule-day { + border-left: 1px solid #eeebee; + flex: 1 0 0; + margin: 0; + min-width: 130px; } + .bldschedule .schedule-day:first-of-type { + border-left: 0; } + .bldschedule h2.schedule-header { + align-items: center; + display: flex; + font-size: 18px !important; + height: 40px; + justify-content: center; + margin: 0; } + .bldschedule .schedule-events { + display: flex; + padding: 0; + position: relative; } + .bldschedule .schedule-event { + background-color: #959396; + border-radius: 4px; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), inset 0 -3px 0 rgba(0, 0, 0, 0.2); + color: #fff; + float: left; + height: 100%; + margin: 0; + padding: 0.25rem 0.5rem; + position: absolute; + width: 100%; } + .bldschedule .schedule-event.lecture { + background-color: #959396; } + .bldschedule .schedule-event.section { + background-color: #7253ed; } + .bldschedule .schedule-event.office-hours { + background-color: #2c84fa; } + +.staffer { + display: flex; + margin: 1rem; } + .staffer .staffer-image { + border-radius: 50%; + height: 100px; + margin-right: 1rem; } + .staffer p, + .staffer .staffer-name { + margin: 0.25rem !important; } + +a abbr[title] { + border-bottom: none; } + +abbr[title] { + text-decoration: none; } + +code { + font-size: 14px; + padding: 0.2em 0.4em; + border: none; } + +div.highlighter-rouge[overlay] { + position: relative; } + div.highlighter-rouge[overlay]::after { + background-color: #fff; + border-radius: 4px; + bottom: 0.5rem; + content: attr(overlay); + position: absolute; + right: 0; + user-select: none; } + +details { + margin: 0 40px 1em; } + +h1, h2, h3, h4, h5, h6 { + align-items: center; + display: flex; } + +iframe, +summary { + max-width: 100%; } + +.main-content-wrap { + max-width: 50rem; + margin: auto; } + +.main-content a { + overflow-wrap: anywhere; + white-space: normal; } +.main-content dl { + display: block; + grid-template-columns: none; } +.main-content dt { + font-weight: 700; + text-align: start; } + .main-content dt::after { + content: normal; } +.main-content dd { + font-weight: normal; } + .main-content dd + dt { + margin-top: 1em; } +.main-content .katex { + font-size: 1.1em; } + +[style*="--aspect-ratio"] > :first-child { + width: 100%; } + +[style*="--aspect-ratio"] > img { + height: auto; } + +@supports (--custom: property) { + [style*="--aspect-ratio"] { + position: relative; } + + [style*="--aspect-ratio"]::before { + content: ""; + display: block; + padding-bottom: calc(100% / (var(--aspect-ratio))); } + + [style*="--add-height"]::before { + padding-bottom: calc(100% / (var(--aspect-ratio)) + (var(--add-height))); } + + [style*="--aspect-ratio"] > :first-child { + position: absolute; + top: 0; + left: 0; + height: 100%; } } +.mermaid .label, .mermaid .staffer .staffer-pronouns, .staffer .mermaid .staffer-pronouns, .mermaid div.highlighter-rouge[overlay]::after { + text-transform: none; } diff --git a/assets/images/project_showcase/git-fodder.txt b/assets/images/project_showcase/git-fodder.txt new file mode 100644 index 00000000..7b57bd29 --- /dev/null +++ b/assets/images/project_showcase/git-fodder.txt @@ -0,0 +1 @@ +some text diff --git a/assets/images/staff/anurag.jpg b/assets/images/staff/anurag.jpg new file mode 100644 index 00000000..1d5d795c Binary files /dev/null and b/assets/images/staff/anurag.jpg differ diff --git a/assets/images/staff/bell-sq.jpg b/assets/images/staff/bell-sq.jpg new file mode 100644 index 00000000..67b67d38 Binary files /dev/null and b/assets/images/staff/bell-sq.jpg differ diff --git a/assets/images/staff/bhupesh_digambar_patil.jpg b/assets/images/staff/bhupesh_digambar_patil.jpg new file mode 100644 index 00000000..77bbf3cf Binary files /dev/null and b/assets/images/staff/bhupesh_digambar_patil.jpg differ diff --git a/assets/images/staff/bhutta.jpg b/assets/images/staff/bhutta.jpg new file mode 100644 index 00000000..e542df4e Binary files /dev/null and b/assets/images/staff/bhutta.jpg differ diff --git a/assets/images/staff/harikrishnan_unnikrishna_pillai.jpeg b/assets/images/staff/harikrishnan_unnikrishna_pillai.jpeg new file mode 100644 index 00000000..88c3d84a Binary files /dev/null and b/assets/images/staff/harikrishnan_unnikrishna_pillai.jpeg differ diff --git a/assets/images/staff/hongyi_zhang.jpg b/assets/images/staff/hongyi_zhang.jpg new file mode 100644 index 00000000..e064e420 Binary files /dev/null and b/assets/images/staff/hongyi_zhang.jpg differ diff --git a/assets/images/staff/james_perretta.png b/assets/images/staff/james_perretta.png new file mode 100644 index 00000000..1117fa8c Binary files /dev/null and b/assets/images/staff/james_perretta.png differ diff --git a/assets/images/staff/kevin_jin.jpg b/assets/images/staff/kevin_jin.jpg new file mode 100644 index 00000000..b8174db9 Binary files /dev/null and b/assets/images/staff/kevin_jin.jpg differ diff --git a/assets/images/staff/ming-ho_yee.jpg b/assets/images/staff/ming-ho_yee.jpg new file mode 100644 index 00000000..8646ec10 Binary files /dev/null and b/assets/images/staff/ming-ho_yee.jpg differ diff --git a/assets/images/staff/muskaan_nandu.jpg b/assets/images/staff/muskaan_nandu.jpg new file mode 100644 index 00000000..0cc34dc8 Binary files /dev/null and b/assets/images/staff/muskaan_nandu.jpg differ diff --git a/assets/images/staff/prajwal_mathad.jpeg b/assets/images/staff/prajwal_mathad.jpeg new file mode 100644 index 00000000..6e8d5d7f Binary files /dev/null and b/assets/images/staff/prajwal_mathad.jpeg differ diff --git a/assets/images/staff/rumjhum_singru.jpeg b/assets/images/staff/rumjhum_singru.jpeg new file mode 100644 index 00000000..ecd24741 Binary files /dev/null and b/assets/images/staff/rumjhum_singru.jpeg differ diff --git a/assets/images/staff/saideep_samineni.jpg b/assets/images/staff/saideep_samineni.jpg new file mode 100644 index 00000000..73784793 Binary files /dev/null and b/assets/images/staff/saideep_samineni.jpg differ diff --git a/assets/images/staff/shashank_joshi.jpg b/assets/images/staff/shashank_joshi.jpg new file mode 100644 index 00000000..a945b3a1 Binary files /dev/null and b/assets/images/staff/shashank_joshi.jpg differ diff --git a/assets/images/staff/sindhu_krovvidi.jpg b/assets/images/staff/sindhu_krovvidi.jpg new file mode 100644 index 00000000..511f6294 Binary files /dev/null and b/assets/images/staff/sindhu_krovvidi.jpg differ diff --git a/assets/images/staff/sushruth.jpg b/assets/images/staff/sushruth.jpg new file mode 100644 index 00000000..947ac977 Binary files /dev/null and b/assets/images/staff/sushruth.jpg differ diff --git a/assets/images/staff/wand175-cropped-2.jpg b/assets/images/staff/wand175-cropped-2.jpg new file mode 100644 index 00000000..ccc45b9d Binary files /dev/null and b/assets/images/staff/wand175-cropped-2.jpg differ diff --git a/assets/js/just-the-docs.js b/assets/js/just-the-docs.js new file mode 100644 index 00000000..d7c756a6 --- /dev/null +++ b/assets/js/just-the-docs.js @@ -0,0 +1,572 @@ +(function (jtd, undefined) { + +// Event handling + +jtd.addEvent = function(el, type, handler) { + if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); +} +jtd.removeEvent = function(el, type, handler) { + if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler); +} +jtd.onReady = function(ready) { + // in case the document is already rendered + if (document.readyState!='loading') ready(); + // modern browsers + else if (document.addEventListener) document.addEventListener('DOMContentLoaded', ready); + // IE <= 8 + else document.attachEvent('onreadystatechange', function(){ + if (document.readyState=='complete') ready(); + }); +} + +// Show/hide mobile menu + +function initNav() { + jtd.addEvent(document, 'click', function(e){ + var target = e.target; + while (target && !(target.classList && target.classList.contains('nav-list-expander'))) { + target = target.parentNode; + } + if (target) { + e.preventDefault(); + target.ariaPressed = target.parentNode.classList.toggle('active'); + } + }); + + const siteNav = document.getElementById('site-nav'); + const mainHeader = document.getElementById('main-header'); + const menuButton = document.getElementById('menu-button'); + + disableHeadStyleSheets(); + + jtd.addEvent(menuButton, 'click', function(e){ + e.preventDefault(); + + if (menuButton.classList.toggle('nav-open')) { + siteNav.classList.add('nav-open'); + mainHeader.classList.add('nav-open'); + menuButton.ariaPressed = true; + } else { + siteNav.classList.remove('nav-open'); + mainHeader.classList.remove('nav-open'); + menuButton.ariaPressed = false; + } + }); +} + +// The element is assumed to include the following stylesheets: +// - a to /assets/css/just-the-docs-head-nav.css, +// with id 'jtd-head-nav-stylesheet' +// - a + + + + + + + + + + + + + + + + +Assignments | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+ + + + +
+

Table of contents

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/assignments/ip1.html b/assignments/ip1.html new file mode 100644 index 00000000..3bc0d619 --- /dev/null +++ b/assignments/ip1.html @@ -0,0 +1,693 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Individual Project 1 | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +

+ + + + + Individual Project 1 + + Due Thursday February 1, 11:00am EST + + + +

+ + + +

Welcome aboard to the Covey.Town team! We’re glad that you’re here and ready to join our development team as a new software engineer. +We’re building an open source virtual meeting application, and are very happy to see that we have so many new developers who can help make this application a reality. +By the end of the semester, you’ll be able to propose, design, implement and test a new feature for our project. +We understand that some of you may have some web development experience, but don’t expect that most of you do, and hence, have created an individual project to help you get up to speed with our existing codebase and development environment.

+ +

Covey.Town is a web application that consists of some code that runs in each client’s web browser, and also code that runs on a server. +Users join the application in a “town”: a 2D arcade-style map with different rooms to explore. +Each town is also a video call: when two players get close to each other, they can see and hear each other; there is also a text chat available within the town. +In Winter of 2021, our lead software engineer, Avery, developed a prototype for Covey.Town, and since then, hundreds of students in this class have built on that codebase. +The most recent class-wide effort, in Fall 2023, added a concept called Game Areas, allowing players to play games within a special part of the town. +Students implemented a single game, Tic-Tac-Toe, as a proof-of-concept for this feature.

+ +

The objective for this semester’s individual project is to extend this new GameArea abstraction, implementing Connect Four as a new game that can be played within a GameArea. +This implementation effort will be split across two deliverables. In this first deliverable, you will implement and test the core backend components for this feature, and in the second deliverable, you will implement and test the frontend components.

+

+ + + Change Log + + +

+ +
    +
  • 1/9/2024: Fixed a typo in handout (status in the ConnectFourGame constructor should be WAITING_FOR_PLAYERS, not WAITING_TO_START), removed reference to shared directory in tsconfig.json.
  • +
  • 1/12/2024: Added a detail in the spec of startGame method for determining the firstPlayer given a prior game to remove any ambiguity (The first player of the game will be the other color of the last game, if at least one player from the previous game joins the game and they get the same color that they were in the previous game).
  • +
  • 1/17/2024: Add clarification to _leave method
  • +
  • 1/18/2024: Add clarification to handleCommand method
  • +
  • 1/23/2024: As explained on Piazza, due to an autograder bug, the submission deadline has been extended to Monday January 29
  • +
  • 1/24/2024: The autograder bug has been fixed, and the submission deadline has been extrended to Thursday February 1
  • +
+

+ + + Objectives of this assignment + + +

+ +

The objectives of this assignment are to:

+
    +
  • Get you familiar with the basics of TypeScript, VSCode, and the project codebase
  • +
  • Learn how to read and write code in TypeScript
  • +
  • Translate high-level requirements into code
  • +
  • Learn how to write unit tests with Jest
  • +
+

+ + + Getting started with this assignment + + +

+ + +

Before you begin, be sure to check that you have NodeJS 18.x installed, along with VSCode. We have provided a tutorial on setting up a development environment for this class +Start by downloading the starter code. Extract the archive and run npm ci to fetch the dependencies. Please extract the entire archive instead of copying files over.

+ +

You may see the following warnings:

+ +

image

+ +

You can safely ignore these warnings: the assignment works with npm 10, and the vulnerabilities are not relevant for this assignment. (Do not run npm audit fix or npm audit fix --force as this may break your copy of the handout code. If this happens to you, please restart with a fresh copy of the handout.)

+ +

Avery has provided you with some very basic sanity tests that you can extend for testing your implementation as you go. You can run these tests with the command npm test ConnectFour (note that many tests are expected to fail until you have begun to implement the assignment).

+

+ + + Submission Instructions & Grading + + +

+ +

You will submit your assignment to the instance of Autograder.io running at neu.autograder.io. +Navigate to neu.autograder.io in your web browser, click the “Sign in” button, and log in with your Northeastern account. +You should then see the course listed on the neu.autograder.io home page. +The autograder will be open by Wed., Jan 10. We will publish an announcement indicating when it is open.

+ +

All students who were enrolled in the class on 1/7 were added to the roster on Autograder.io. +If you do not see the course listed on Autograder.io, please contact the instructors immediately via email or Piazza post so that we can add you. +If you haven’t been added to the course roster yet, you can access the assignment submission page at this direct link.

+ +

To submit your assignment: run the command npm run zip in the top-level directory of the handout. This will produce a file called covey-town-townService.zip. Submit that zip file on Autograder.io.

+ +

This submission will be scored out of 100 points, 90 of which will be automatically awarded by the grading script, with the remaining 10 manually awarded by the course staff.

+ +

Your code will automatically be evaluated for linter errors and warnings. Submissions that have any linter errors will automatically receive a grade of 0. Do not wait to run the linter until the last minute. To check for linter errors, run the command npm run lint from the terminal. The handout contains the same eslint configuration that is used by our grading script.

+ +

Your code will be automatically evaluated for functional correctness by a test suite that expands on the core tests that are distributed in the handout. +Your tests will be automatically evaluated for functional correctness by a process that will inject bugs into our reference solution. +This process proceeds as follows:

+
    +
  1. Your tests will be run against our (correct) reference solution. If any of your tests fail (i.e., a “false positive”), you will receive no credit for your test cases.
  2. +
  3. Your tests will be run against a sequence of injected bugs, and you will be awarded points for each bug your tests detect. Note: Each submission will be graded against the same set of injected bugs (repeated submissions will not receive new/different injected bugs).
  4. +
+ +

You will receive limited feedback on which injected bugs you do or do not find (e.g., “Your tests detected 5 bugs”). +You will additionally be able to request up to 2 automatic hints per task (e.g., tests for joining the game, tests for leaving the game) per submission. +These hints will provide additional guidance on what to test based on which bugs your tests did not detect. +Please take a moment to provide feedback on these hints through the autograder’s interface.

+ +

The autograding script will impose a strict rate limit of 5 submissions per 24 hours. +This submission limit resets at 11am EST. +Submissions that contain linter errors and warnings will not count towards that limit. +This limit exists to encourage you to start early on this assignment: students generally report that assignments like this take between 3-20 hours. +We strongly encourage you to lint and test your submission on your local development machine, and not rely on Autograder.io for providing grading feedback - relying on Autograder.io is a very slow feedback loop. +If you start early, you will be able to take full advantage of the resources that we provide to help you succeed: office hours, discussion on Piazza — and the ability to have a greater total number of submission attempts and hints.

+ +

Your code will be manually evaluated for conformance to our course style guide. This manual evaluation will account for 10% of your total grade on this assignment. We will manually evaluate your code for style on the following rubric:

+ +

To receive all 10 points:

+
    +
  • All new names (e.g. for local variables, methods, and properties) follow the naming conventions defined in our style guide
  • +
  • There are no unused local variables
  • +
  • All public properties and methods (other than getters, setters, and constructors) are documented with JSDoc-style comments that describes what the property/method does, as defined in our style guide
  • +
  • The code and tests that you write generally follows the design principles discussed in week one. In particular, your design does not have duplicated code that could have been refactored into a shared method.
  • +
+ +

We will review your code and note each violation of this rubric. We will deduct two points for each violation, up to a maximum of deducting all 10 style points.

+

+ + + Overview of Relevant Classes + + +

+ + + + +
+%%{init: { 'theme':'forest', } }%% +classDiagram + class Game { + +GameState state + +GameInstanceID id + ~Player[] _players + ~GameResult _result + + join(player: Player) + + leave(player: Player) + ~ _join(player: Player) + ~ _leave(player: Player) + + applyMove(move: GameMove) + + } + class GameArea { + ~Game _game + ~GameResult _history + } + class ConnectFourGame { + + } + class ConnectFourGameArea { + } + class GameResult { + +GameInstanceID gameID + +Map scores + } + GameArea o-- GameResult + ConnectFourGame ..|> Game + ConnectFourGameArea ..|> GameArea + ConnectFourGameArea o-- ConnectFourGame + GameArea o-- Game +
+ +

State diagram for game status:

+
+%%{init: { 'theme':'forest', } }%% +flowchart TD + + I([New Game])--> A + A[WAITING_FOR_PLAYERS] --> |1 player joins| A + A --> |2nd player joins| B + B --> |A player leaves| A + B[WAITING_TO_START] --> |1 player clicks ready|B + B --> |Other player clicks ready| C + C[IN_PROGRESS] --> |Game Ends or a player leaves| D + D[OVER] --> |1 player clicks 'new game'| B +
+

+ + + Implementation Tasks + + +

+ +

This deliverable has four parts; each part will be graded on its own rubric. You should complete the assignment one part at a time, in the order presented here.

+ +

General Requirements: Implement your code only in the files specified: src/town/games/ConnectFourGame.ts, src/town/games/ConnectFourGame.test.ts and src/town/games/ConnectFourGameArea.ts. You should not install any additional dependencies. The autograder will ignore any other files that you modify, and will not install any dependencies that you add to the project.

+

+ + + Task 1: Joining and Leaving the ConnectFourGame (10 points) + + +

+ +

The ConnectFourGame class extends the base Game class, implementing the semantics of the game Connect Four. Avery has provided a definition for the types that will be used to represent the state of a ConnectFourGame - ConnectFourGameState. That type definition is reproduced below:

+ +
/**
+ * Type for the state of a ConnectFour game.
+ * The state of the game is represented as a list of moves, and the playerIDs of the players (red and yellow)
+ */
+export interface ConnectFourGameState extends WinnableGameState {
+  // The moves in this game
+  moves: ReadonlyArray<ConnectFourMove>;
+  // The playerID of the red player, if any
+  red?: PlayerID;
+  // The playerID of the yellow player, if any
+  yellow?: PlayerID;
+  // Whether the red player is ready to start the game
+  redReady?: boolean;
+  // Whether the yellow player is ready to start the game
+  yellowReady?: boolean;
+  // The color of the player who will make the first move
+  firstPlayer: ConnectFourColor;
+}
+
+/**
+ * Type for a move in ConnectFour
+ * Columns are numbered 0-6, with 0 being the leftmost column
+ * Rows are numbered 0-5, with 0 being the top row
+ */
+export interface ConnectFourMove {
+  gamePiece: ConnectFourColor;
+  col: ConnectFourColIndex;
+  row: ConnectFourRowIndex;
+}
+
+/**
+ * Row indices in ConnectFour start at the top of the board and go down
+ */
+export type ConnectFourRowIndex = 0 | 1 | 2 | 3 | 4 | 5;
+/**
+ * Column indices in ConnectFour start at the left of the board and go right
+ */
+export type ConnectFourColIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6;
+
+export type ConnectFourColor = 'Red' | 'Yellow';
+ +

Your first task is to implement the _join, startGame and _leave methods of ConnectFourGame. To implement these methods, you should not need to read any other parts of the codebase besides Game.ts and ConnectFourGame.ts. You might find it useful or necessary to modify the constructor of ConnectFourGame to initialize the state of the game, and to add private instance variables and/or helper methods.

+ +
+

Clarification

+ +

_leave method: If the game is in progress, the game will be over after a player leaves. +A winning player is decided according to the game rules of Connect Four. +Leaving a game that leaves the game status to “OVER” would be considered a “forfeit” by the player who left the game. +A new game must be initialized after the game is over and a game object is not “re-used” once it is “IN_PROGRESS”. +Think about a ConnectFourGame object or instance as a single game.

+ +

About tests: If you are not sure about the specification of a method, you can always use tests as a method to validate your assumptions.

+
+ +
+ View the specification for these methods + +
+
  /**
+   * Joins a player to the game.
+   * - Assigns the player to a color (red or yellow). If the player was in the prior game, then attempts
+   * to reuse the same color if it is not in use. Otherwise, assigns the player to the first
+   * available color (red, then yellow).
+   * - If both players are now assigned, updates the game status to WAITING_TO_START.
+   *
+   * @throws InvalidParametersError if the player is already in the game (PLAYER_ALREADY_IN_GAME_MESSAGE)
+   * @throws InvalidParametersError if the game is full (GAME_FULL_MESSAGE)
+   *
+   * @param player the player to join the game
+   */
+  protected _join(player: Player): void
+
+  /**
+   * Indicates that a player is ready to start the game.
+   *
+   * Updates the game state to indicate that the player is ready to start the game.
+   *
+   * If both players are ready, the game will start.
+   *
+   * The first player (red or yellow) is determined as follows:
+   *   - If neither player was in the last game in this area (or there was no prior game), the first player is red.
+   *   - If at least one player was in the last game in this area and they get the same color that they were in the last game, then the first player will be the other color from last game.
+   *   - If a player from the last game *left* the game and then joined this one, they will be treated as a new player (not given the same color by preference).
+   *
+   * @throws InvalidParametersError if the player is not in the game (PLAYER_NOT_IN_GAME_MESSAGE)
+   * @throws InvalidParametersError if the game is not in the WAITING_TO_START state (GAME_NOT_STARTABLE_MESSAGE)
+   *
+   * @param player The player who is ready to start the game
+   */
+  public startGame(player: Player): void 
+
+  /**
+   * Removes a player from the game.
+   * Updates the game's state to reflect the player leaving.
+   *
+   * If the game state is currently "IN_PROGRESS", updates the game's status to OVER and sets the winner to the other player.
+   *
+   * If the game state is currently "WAITING_TO_START", updates the game's status to WAITING_FOR_PLAYERS.
+   *
+   * If the game state is currently "WAITING_FOR_PLAYERS" or "OVER", the game state is unchanged.
+   *
+   * @param player The player to remove from the game
+   * @throws InvalidParametersError if the player is not in the game (PLAYER_NOT_IN_GAME_MESSAGE)
+   */
+  protected _leave(player: Player): void
+
+ +
+ +

Testing: Avery has provided you with some very simple (and incomplete) tests for _join. You can run these tests by running the command npx jest --watch ConnectFourGame.test, which will automatically re-run the tests as you update the file (note that tests for applyMove will also run - but you can ignore those at this point). As you read and understand the specification, you should extend the existing test suite, adding tests to cover the entire specification. Please implement these additional tests in the file src/town/games/ConnectFourGame.test.ts.

+ +

Grading for implementation tasks:

+
    +
  • _join: 2 points
  • +
  • startGame: 1 points
  • +
  • _leave: 1 points
  • +
+ +

Grading for testing tasks:

+
    +
  • _join: 2 points
  • +
  • startGame: 2 points
  • +
  • _leave: 2 points
  • +
+

+ + + Task 2: Connect Four Game Semantics (70 points total) + + +

+ +

The next (and largest) task for this deliverable is to implement the method ConnectFourGame.applyMove, which applies a player’s move to the game. This method is responsible for validating the move, and updating the game state to reflect the move. Given the complexity of this method, you should anticipate creating (at least one) private, helper method to implement its logic.

+ +
+ View the specification for this method + +
+
  /**
+   * Applies a move to the game.
+   * Uses the player's ID to determine which color they are playing as (ignores move.gamePiece).
+   *
+   * Validates the move, and if it is valid, applies it to the game state.
+   *
+   * If the move ends the game, updates the game state to reflect the end of the game,
+   * setting the status to "OVER" and the winner to the player who won (or "undefined" if it was a tie)
+   *
+   * @param move The move to attempt to apply
+   *
+   * @throws InvalidParametersError if the game is not in progress (GAME_NOT_IN_PROGRESS_MESSAGE)
+   * @throws InvalidParametersError if the player is not in the game (PLAYER_NOT_IN_GAME_MESSAGE)
+   * @throws INvalidParametersError if the move is not the player's turn (MOVE_NOT_YOUR_TURN_MESSAGE)
+   * @throws InvalidParametersError if the move is invalid per the rules of Connect Four (BOARD_POSITION_NOT_VALID_MESSAGE)
+   *
+   */
+  public applyMove(move: GameMove<ConnectFourMove>): void
+
+ +
+ +

Grading for implementation tasks:

+
    +
  • Correctly validating who is the first player: 5 points
  • +
  • Applying moves: 7 points
  • +
  • Checking for invalid moves: 12 points
  • +
  • Checking for game-ending moves: 10 points
  • +
+ +

Grading for testing tasks:

+
    +
  • Correctly validating who is the first player: 5 points
  • +
  • Permitting valid moves: 8 points
  • +
  • Checking for invalid moves: 13 points
  • +
  • Handling game-ending moves: 10 points
  • +
+

+ + + Task 3: Implement the ConnectFourGameArea (10 points total) + + +

+ +

The ConnectFourGameArea receives InteractableCommands from players who enter the area on their client. The main responsibility of this class is to interpet those commands, dispatching them as appropriate to the ConnectFourGame instance that it manages. Your final task is to implement the handleCommand method of ConnectFourGameArea.

+ +

There are four types of commands that the ConnectFourGameArea will receive, which map directly to the three methods of ConnectFourGame that you implemented in the previous task.

+ +

Avery has provided a complete test suite for handleCommand - you do not need to write any additional tests.

+ +
+

Clarification

+ +

Invalid commands regarding when a “game is not in progress” refers to the game instance itself (Does the game exist?). +It does not refer to the game status (state) being “IN_PROGRESS”.

+ +

Hint: Read through the GameArea class to understand the difference between a game instance and a game state (ConnectFourGame).

+
+ +
+ View the specification for this methods + +
+
  /**
+   * Handle a command from a player in this game area.
+   * Supported commands:
+   * - JoinGame (joins the game `this._game`, or creates a new one if none is in progress)
+   * - StartGame (indicates that the player is ready to start the game)
+   * - GameMove (applies a move to the game)
+   * - LeaveGame (leaves the game)
+   *
+   * If the command ended the game, records the outcome in this._history
+   * If the command is successful (does not throw an error), calls this._emitAreaChanged (necessary
+   * to notify any listeners of a state update, including any change to history)
+   * If the command is unsuccessful (throws an error), the error is propagated to the caller
+   *
+   * @see InteractableCommand
+   *
+   * @param command command to handle
+   * @param player player making the request
+   * @returns response to the command, @see InteractableCommandResponse
+   * @throws InvalidParametersError if the command is not supported or is invalid.
+   * Invalid commands:
+   * - GameMove, StartGame and LeaveGame: if the game is not in progress (GAME_NOT_IN_PROGRESS_MESSAGE) or if the game ID does not match the game in progress (GAME_ID_MISSMATCH_MESSAGE)
+   * - Any command besides JoinGame, GameMove, StartGame and LeaveGame: INVALID_COMMAND_MESSAGE
+   */
+  public handleCommand<CommandType extends InteractableCommand>(
+    command: CommandType,
+    player: Player,
+  ): InteractableCommandReturnType<CommandType>
+
+ +
+ +

Grading for implementation tasks:

+
    +
  • Handling JoinGame: 2 points
  • +
  • Handling GameMove: 2 points
  • +
  • Handling LeaveGame: 2 points
  • +
  • Handling StartGame: 2 points
  • +
  • Handling invalid commands: 2 point
  • +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/assignments/ip2.html b/assignments/ip2.html new file mode 100644 index 00000000..a49c4ecf --- /dev/null +++ b/assignments/ip2.html @@ -0,0 +1,741 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Individual Project 2 | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +

+ + + + + Individual Project 2 + + Due Wednesday February 21, 11:00am EST + + + +

+ + + +

The objective for this semester’s individual project is to extend this new GameArea abstraction, implementing Connect Four as a new game that can be played within a GameArea. +This implementation effort will be split across two deliverables. In this second deliverable, you will implement and the core frontend components.

+

+ + + Objectives of this assignment + + +

+ +

The objectives of this assignment are to:

+
    +
  • Investigate and understand a large, existing codebase
  • +
  • Write new TypeScript code that uses asynchronous operations
  • +
  • Write test cases that utilize mocks and spies
  • +
  • Write React components and hooks that make use of state
  • +
+

+ + + Changelog + + +

+ +
    +
  • Feb 11: Fixed the mock initializer in GamesArea.test.tsx - this was causing some student submissions to fail with an error about the incorrect interactableID being found. The handout is updated with the test, and it can also be directly downloaded from here.
  • +
  • Feb 8: 🤦 If you had the pre-Feb-7 handout, note that Lines 627 and 709 should read expect(screen.getByText('Waiting for players to press start', { exact: false })).toBeInTheDocument();
  • +
  • Feb 7: Updated handout to fix typo in ConnectFourArea.test.tsx. Line 627 should read expect(screen.getByText('Waiting for players to press start', { exact: false })).toBeInTheDocument();
  • +
  • Feb 6: Fixed typo in the list of files in “General Requirements” (ConnectFourAreaController.ts.ts -> ConnectFourAreaController.ts)
  • +
+

+ + + Getting started with this assignment + + +

+ +

Start by downloading the starter code. Extract the archive and run npm install to fetch the dependencies.

+ +

System-level dependencies: The libraries used for React require some native binaries to be installed – code written and compiled for your computer (not JavaScript). If you run into issues with npm install not succeeding, please try installing the following libraries using either Homebrew (if on Mac), apt-get, or your favorite other package manager: pixman, cairo, pkgconfig and pango. For example, on a Mac, after installing Homebrew, run brew install pixman cairo pkgconfig pango. You should not continue with the installation until this succeeds. On Windows: Students have reported seeing the failure error /bin/bash: node: command not found upon npm install in the frontend directory. If you encounter this error, please try to delete the node_modules directory and re-run npm install in the frontend directory from a bash shell instead of a windows command prompt.

+ +

Node 16 vs 18 +For this assignment (and the team project), you should use NodeJS 16 and NPM 9. You can check your version of NodeJS by running node -v and your version of NPM by running npm -v. We had gotten over-eager at the start of the semester, updating our tutorial on installing a development environment to NodeJS 18. Several of us got stuck in “dependency hell” with one of the transitive dependencies of the project, node-gyp on various versions of Node 18. All who tested it reported success on Node 16. If you run into failures running npm install, and have Node 18, please try downgrading to Node 16 (easiest accomplished using nvm and the command nvm install 16 followed optionally by nvm alias default 16 to make this the default). If you end up changing versions of Node, you will need to delete the node_modules directory and run npm install again.

+ +

Configuring Jest and VSCode: If you would like to use the built-in Jest test runner for VSCode (where it shows the tests and their status in the sidebar), the easiest way to accomplish this for this project is to open just the “frontend” directory or just the “townService” directory in VSCode - not the top-level “ip2-handout” directory. If you have a quick-fix to make it work with the whole project at once, please feel free to share on Piazza and we will incorportate that here.

+ +

Running the app: We strongly encourage you to interactively experiment as you develop by running the entire application in your development environment. See the instructions in README.md for how to run the app.

+

+ + + Grading + + +

+ +

This submission will be scored out of 200 points, 180 of which will be automatically awarded by the grading script, with the remaining 20 manually awarded by the course staff.

+ +

Your code will automatically be evaluated for linter errors and warnings. Submissions that have any linter errors will automatically receive a grade of 0. Do not wait to run the linter until the last minute. To check for linter errors, run the command npm run lint from the terminal. The handout contains the same eslint configuration that is used by our grading script.

+ +

Your code will be automatically evaluated for functional correctness by a test suite that expands on the core tests that are distributed in the handout. +Your tests will be automatically evaluated for functional correctness by a process that will inject bugs into our reference solution: to receive full marks your tests must detect a minimum number of injected bugs. +Each submission will be graded against the same set of injected bugs (repeated submissions will not receive new/different injected bugs). +You will not receive detailed feedback on which injected bugs you do or do not find.

+ +

The autograding script will impose a strict rate limit of 5 submissions per 24 hours. +Submissions that fail to grade will not count against the quota. +This limit exists to encourage you to start early on this assignment: students generally report that assignments like this take between 3-20 hours. +If you start early, you will be able to take full advantage of the resources that we provide to help you succeed: office hours, discussion on Piazza — and the ability to have a greater total number of submission attempts.

+ +

Your code will be manually evaluated for conformance to our course style guide. This manual evaluation will account for 10% of your total grade on this assignment. We will manually evaluate your code for style on the following rubric:

+ +

To receive all 20 points:

+
    +
  • All new names (e.g. for local variables, methods, and properties) follow the naming conventions defined in our style guide
  • +
  • There are no unused local variables
  • +
  • All public properties and methods (other than getters, setters, and constructors) are documented with JSDoc-style comments that describes what the property/method does, as defined in our style guide
  • +
  • The code and tests that you write generally follows the design principles discussed in week one. In particular, your design does not have duplicated code that could have been refactored into a shared method.
  • +
+ +

We will review your code and note each violation of this rubric. We will deduct four points for each violation, up to a maximum of deducting all 20 style points.

+

+ + + Implementation Tasks + + +

+ +

This deliverable has four parts; each part will be graded on its own rubric. You should complete the assignment one part at a time, in the order presented here.

+ +

General Requirements: Implement your code only in the files specified:

+
    +
  • Task 1: frontend/src/classes/interactable/ConnectFourAreaController.ts
  • +
  • Task 2: frontend/src/components/Town/interactables/GamesArea.tsx
  • +
  • Task 3: frontend/src/components/Town/interactables/ConnectFour/ConnectFourArea.tsx
  • +
  • Task 4: frontend/src/components/Town/ConnectFour/ConnectFourBoard.tsx
  • +
  • Task 5: frontend/src/components/SocialSidebar/InteractableAreaList.tsx and frontend/src/classes/interactable/InteractableAreaController.ts
  • +
+ +

You should not install any additional dependencies. The autograder will ignore any other files that you modify, and will not install any dependencies that you add to the project.

+

+ + + Task 1: Implement the ConnectFourAreaController (60 points) + + +

+ +

The ConnectFourAreaController is a class that is responsible for managing the state of a single ConnectFour game. It is responsible for communicating with the TownService backend. Frontend components will interact with the ConnectFourAreaController to get the current state of the game, and to send commands to the backend to update the game state. The ConnectFourAreaController also will emit events when the state of the game changes, so that frontend components can update their state accordingly.

+ +

ConnectFourAreaController extends the base GameAreaController class. +The base class tracks the game model (this._model), the set of players in the game (this.players), and the set of observers in the game (this.observers). It also provides methods for joining and leaving the game, as well as a base implementation of _updateFrom, which is responsible for updating the game state when the backend notifies the frontend of a change.

+ +

Your first task is to implement each of the properties and methods of the ConnectFourAreaController class (frontend/src/classes/interactable/ConnectFourAreaController.ts). The specification for these properties and methods appears below:

+ +

GitHub Co-pilot: If you haven’t yet used GitHub Copilot, this might be a good time to set it up. It can be very helpful for writing boilerplate code, and especially for proposing implementations of very simple methods. You likely will find it very useful for many of these implementation tasks. To get it for free: 1. Sign up for GitHub’s Student Pack and wait for your student status to be verified, then 2. Install the GitHub Copilot extension for VSCode.

+ +
+ View the specification for these methods + +
+
 /**
+   * Returns the current state of the board.
+   *
+   * The board is a 6x7 array of ConnectFourCell, which is either 'Red', 'Yellow', or undefined.
+   *
+   * The 2-dimensional array is indexed by row and then column, so board[0][0] is the top-left cell,
+   */
+  get board(): ConnectFourCell[][] 
+
+  /**
+   * Returns the player with the 'Red' game piece, if there is one, or undefined otherwise
+   */
+  get red(): PlayerController | undefined 
+
+  /**
+   * Returns the player with the 'Yellow' game piece, if there is one, or undefined otherwise
+   */
+  get yellow(): PlayerController | undefined 
+
+  /**
+   * Returns the player who won the game, if there is one, or undefined otherwise
+   */
+  get winner(): PlayerController | undefined 
+
+  /**
+   * Returns the number of moves that have been made in the game
+   */
+  get moveCount(): number 
+
+  /**
+   * Returns true if it is our turn to make a move, false otherwise
+   */
+  get isOurTurn(): boolean
+
+  /**
+   * Returns true if the current player is in the game, false otherwise
+   */
+  get isPlayer(): boolean 
+
+  /**
+   * Returns the color of the current player's game piece
+   * @throws an error with message PLAYER_NOT_IN_GAME_ERROR if the current player is not in the game
+   */
+  get gamePiece(): ConnectFourColor 
+
+  /**
+   * Returns the status of the game
+   * If there is no game, returns 'WAITING_FOR_PLAYERS'
+   */
+  get status(): GameStatus 
+
+  /**
+   * Returns the player whose turn it is, if the game is in progress
+   * Returns undefined if the game is not in progress
+   *
+   * Follows the same logic as the backend, respecting the firstPlayer field of the gameState
+   */
+  get whoseTurn(): PlayerController | undefined 
+
+  /**
+   * Returns true if the game is empty - no players AND no occupants in the area
+   *
+   */
+  isEmpty(): boolean 
+
+  /**
+   * Returns true if the game is not empty and the game is not waiting for players
+   */
+  public isActive(): boolean 
+
+  /**
+   * Updates the internal state of this ConnectFourAreaController based on the new model.
+   *
+   * Calls super._updateFrom, which updates the occupants of this game area and other
+   * common properties (including this._model)
+   *
+   * If the board has changed, emits a boardChanged event with the new board.
+   * If the board has not changed, does not emit a boardChanged event.
+   *
+   * If the turn has changed, emits a turnChanged event with the new turn (true if our turn, false otherwise)
+   * If the turn has not changed, does not emit a turnChanged event.
+   */
+  protected _updateFrom(newModel: GameArea<ConnectFourGameState>): void 
+
+  /**
+   * Sends a request to the server to start the game.
+   *
+   * If the game is not in the WAITING_TO_START state, throws an error.
+   *
+   * @throws an error with message NO_GAME_STARTABLE if there is no game waiting to start
+   */
+  public async startGame(): Promise<void> 
+
+  /**
+   * Sends a request to the server to place the current player's game piece in the given column.
+   * Calculates the row to place the game piece in based on the current state of the board.
+   * Does not check if the move is valid.
+   *
+   * @throws an error with message NO_GAME_IN_PROGRESS_ERROR if there is no game in progress
+   * @throws an error with message COLUMN_FULL_MESSAGE if the column is full
+   *
+   * @param col Column to place the game piece in
+   */
+  public async makeMove(col: ConnectFourColIndex): Promise<void>
+
+ +
+ +

Testing: Avery has provided you with tests for everything in ConnectFourAreaController. You can run the tests by running the command npx jest ConnectFourAreaController in the frontend directory (for convenience, you may want to use npx jest --watch …).

+ +

The grading script will assign full marks for each implementation task if all of the tests for that task pass. There is no partial credit.

+ +

Grading for implementation tasks:

+
    +
  • All properties and methods besides _updateFrom, startGame and makeMove: 10 points
  • +
  • _updateFrom: 10 points
  • +
  • startGame: 10 points
  • +
  • makeMove: 20 points
  • +
+

+ + + Task 2: GamesArea Component (20 points total) + + +

+ +

In last semester’s individual project, students implemented the TicTacToeArea component. This component was responsible for rendering information about the area, including the list of players observing the game, a leaderboard, and the game itself. Avery decided that rather than copy-pasting this component, it would be better to refactor it into a more general GamesArea component, which could be used to render any game area. This component is located in the file frontend/src/components/Town/interactables/GamesArea.tsx.

+ +

As you work on this component, you might find it helpful to view the code of the old TicTacToeArea component, which included the functionality for rendering the leaderboard and list of observers.

+ +
+ View the specification for this component + +
+
/**
+ * A generic component that renders a game area.
+ *
+ * It uses Chakra-UI components (does not use other GUI widgets)
+ *
+ * It uses the GameAreaController corresponding to the provided interactableID to get the current state of the game. (@see useInteractableAreaController)
+ *
+ * It renders the following:
+ *  - A leaderboard of the game results
+ *  - A list of observers' usernames (in a list with the aria-label 'list of observers in the game')
+ *  - The game area component (either ConnectFourArea or TicTacToeArea). If the game area is NOT a ConnectFourArea or TicTacToeArea, then the message INVALID_GAME_AREA_TYPE_MESSAGE appears within the component
+ *  - A chat channel for the game area (@see ChatChannel.tsx), with the property interactableID set to the interactableID of the game area
+ *
+ */
+function GameArea({ interactableID }: { interactableID: InteractableID }): JSX.Element
+
+ +
+ +

Grading:

+
    +
  • Correctly register the listeners: 10 points
  • +
  • Displaying the leaderboard component: 2 points
  • +
  • List of observers watching game: 2 points
  • +
  • Render the game area component: 2 points
  • +
  • Render the chat channel: 2 points
  • +
+

+ + + Task 3: Connect Four Area (50 points total) + + +

+ +

The next task is to implement the React component that will render the Connect Four game area. This component will show information about the game area, and the current state of the game. It displays the ConnectFourBoard` (which you’ll implement in the next task).

+ +

This component is located in the file frontend/src/components/Town/interactables/ConnectFour/ConnectFourArea.tsx - you should implement component class in this file.

+ +
+ View the specification for this component + +
+
/**
+ * The ConnectFourArea component renders the Connect Four game area.
+ * It renders the current state of the area, optionally allowing the player to join the game.
+ *
+ * It uses Chakra-UI components (does not use other GUI widgets)
+ *
+ * It uses the ConnectFourAreaController to get the current state of the game.
+ * It listens for the 'gameUpdated' and 'gameEnd' events on the controller, and re-renders accordingly.
+ * It subscribes to these events when the component mounts, and unsubscribes when the component unmounts. It also unsubscribes when the gameAreaController changes.
+ *
+ * It renders the following:
+ * - A list of players' usernames (in a list with the aria-label 'list of players in the game', one item for red and one for yellow)
+ *    - If there is no player in the game, the username is '(No player yet!)'
+ *    - List the players as (exactly) `Red: ${username}` and `Yellow: ${username}`
+ * - A message indicating the current game status:
+ *    - If the game is in progress, the message is 'Game in progress, {moveCount} moves in, currently {whoseTurn}'s turn'. If it is currently our player's turn, the message is 'Game in progress, {moveCount} moves in, currently your turn'
+ *    - If the game is in status WAITING_FOR_PLAYERS, the message is 'Waiting for players to join'
+ *    - If the game is in status WAITING_TO_START, the message is 'Waiting for players to press start'
+ *    - If the game is in status OVER, the message is 'Game over'
+ * - If the game is in status WAITING_FOR_PLAYERS or OVER, a button to join the game is displayed, with the text 'Join New Game'
+ *    - Clicking the button calls the joinGame method on the gameAreaController
+ *    - Before calling joinGame method, the button is disabled and has the property isLoading set to true, and is re-enabled when the method call completes
+ *    - If the method call fails, a toast is displayed with the error message as the description of the toast (and status 'error')
+ *    - Once the player joins the game, the button dissapears
+ * - If the game is in status WAITING_TO_START, a button to start the game is displayed, with the text 'Start Game'
+ *    - Clicking the button calls the startGame method on the gameAreaController
+ *    - Before calling startGame method, the button is disabled and has the property isLoading set to true, and is re-enabled when the method call completes
+ *    - If the method call fails, a toast is displayed with the error message as the description of the toast (and status 'error')
+ *    - Once the game starts, the button dissapears
+ * - The ConnectFourBoard component, which is passed the current gameAreaController as a prop (@see ConnectFourBoard.tsx)
+ *
+ * - When the game ends, a toast is displayed with the result of the game:
+ *    - Tie: description 'Game ended in a tie'
+ *    - Our player won: description 'You won!'
+ *    - Our player lost: description 'You lost :('
+ *
+ */
+export default function ConnectFourArea({
+  interactableID,
+}: {
+  interactableID: InteractableID;
+}): JSX.Element 
+
+ +
+ +

You can begin to implement these tasks in whatever order you see fit, but we would suggest completing them in the order shown in the specification.

+ +

There is significant ambiguity in the specification when it comes to exactly how this looks. We will not evaluate your submission on the basis of how closely it looks like our referencence implementation: it need only be functionally correct (as defined by the included test cases).

+ +

Grading for implementation tasks:

+
    +
  • Correctly registering the listeners: 10 points
  • +
  • Join game button: 15 points
  • +
  • Start game button: 15 points
  • +
  • List of players in game: 5 points
  • +
  • Display the game status: 5 points
  • +
+ +

All of the tests are provided in the handout. Run the tests for this task by running the command npx jest ConnectFourArea.test in the frontend directory (for convenience, you may want to use npx jest --watch …). You will also likely find it convenient to run the app in your browser while you work on this task for interactive debugging.

+ +

The grading script will assign full marks for each implementation task if all of the tests for that task pass. There is no partial credit.

+

+ + + Task 4: Implement the ConnectFourBoard (20 points total) + + +

+ +

This task is to implement the ConnectFourBoard component, which will render the actual (interactive) board. It is located in the file frontend/src/components/Town/interactables/ConnectFour/ConnectFourBoard.tsx.

+ +
+ View the specification for this component + +
+
*
+ * A component that renders the ConnectFour board
+ *
+ * Renders the ConnectFour board as a "StyledConnectFourBoard", which consists of "StyledConnectFourSquare"s
+ * (one for each cell in the board, starting from the top left and going left to right, top to bottom).
+ *
+ * Each StyledConnectFourSquare has an aria-label property that describes the cell's position in the board,
+ * formatted as `Cell ${rowIndex},${colIndex} (Red|Yellow|Empty)`.
+ *
+ * The background color of each StyledConnectFourSquare is determined by the value of the cell in the board, either
+ * 'red', 'yellow', or '' (an empty for an empty square).
+ *
+ * The board is re-rendered whenever the board changes, and each cell is re-rendered whenever the value
+ * of that cell changes.
+ *
+ * If the current player is in the game, then each StyledConnectFourSquare is clickable, and clicking
+ * on it will make a move in that column. If there is an error making the move, then a toast will be
+ * displayed with the error message as the description of the toast. If it is not the current player's
+ * turn, then the StyledConnectFourSquare will be disabled.
+ *
+ * @param gameAreaController the controller for the ConnectFour game
+ */
+ export default function ConnectFourBoard({
+  gameAreaController,
+}: ConnectFourGameProps): JSX.Element
+
+ +
+ +

Again, there is significant ambiguity in the specification when it comes to exactly how this looks. We will not evaluate your submission on the basis of how closely it looks like our referencence implementation: it need only be functionally correct (as defined by the included test cases).

+ +

Grading for implementation tasks:

+
    +
  • Drawing the board for observers: 10 points
  • +
  • Drawing the interactive board for players: 10 points
  • +
+ +

All of the tests are provided in the handout. Run the tests for this task by running the command npx jest ConnectFourBoard in the frontend directory (for convenience, you may want to use npx jest --watch …). You will also likely find it convenient to run the app in your browser while you work on this task for interactive debugging.

+ +

The grading script will assign full marks for each implementation task if all of the tests for that task pass. There is no partial credit.

+

+ + + Task 5: Extend the SocialSidebar (30 points total) + + +

+ + +

On the right hand side of the Covey.Town interface, there is a sidebar that displays information about the current town, listing the players in the town and the active ConversationAreas. This sidebar is implemented in the SocialSidebar component, located in the file frontend/src/components/SocialSidebar/SocialSidebar.tsx.

+ +

Given the growth of the Covey.Town codebase, Avery has decided that instead of just listing the active Conversation Areas, it would be better to list all of the active interactables in the town. This will allow the sidebar to list the active Conversation Areas, as well as any active games, viewing areas, or future interactable areas not yet conceived.

+ +

Your specific task is to implement the InteractableAreaList component, which will display a list of all active interactable areas in the town. This component is located in the file frontend/src/components/SocialSidebar/InteractableAreaList.tsx. This component should make use of two React hooks defined in InteractableAreaController.ts - useInteractableAreaOccupants and useInteractableAreaFriendlyName. These hooks are responsible for fetching the list of occupants of an interactable area, and the friendly name of the interactable area, respectively. You will need to implement the useInteractableAreaFriendlyName hook, but will find the other is already completed.

+ +

You might find it useful to view the code of the old ConversationAreasList component.

+ +
+ View the specification for this component and these hooks + +
+
/**
+ * A react component that displays a list of all active interactable areas in the town.
+ * The list is grouped by type of interactable area, with those groups sorted alphabetically
+ * by the type name. If there are any active areas of that type, the type name is shown in an H3,
+ * followed by an ordered list of interactable areas of that type. Within each type, the areas
+ * are sorted first by the number of occupants in the area, and then by the name of the area
+ * (alphanumerically).
+ *
+ * The list of interactable areas is represented as an ordered list, with each list item
+ * containing the name of the area (in an H4 heading), and then a list of the occupants of the area, where
+ * each occupant is shown as a PlayerName component. The list of occupants is in an unordered list.
+ *
+ * @returns A list of all active interactable areas in the town as per above spec
+ */
+export default function InteractableAreasList(): JSX.Element
+
+/**
+ * A react hook to retrieve the friendly name of an InteractableAreaController, returning a string.
+ *
+ * This hook will re-render any components that use it when the friendly name changes.
+ *
+ */
+export function useInteractableAreaFriendlyName(area: GenericInteractableAreaController): string
+
+ +
+ +

Grading for implementation tasks:

+
    +
  • Implement the useInteractableAreaFriendlyName hook: 10 points
  • +
  • Implement the InteractableAreaList component: 20 points
  • +
+ +

All of the tests are provided in the handout. Run the tests for this task by running the command npx jest InteractableAreasList hooks in the frontend directory (for convenience, you may want to use npx jest --watch …). You will also likely find it convenient to run the app in your browser while you work on this task for interactive debugging.

+ +

The grading script will assign full marks for each implementation task if all of the tests for that task pass. There is no partial credit.

+

+ + + Submission Instructions + + +

+ +

Submit your assignment in GradeScope. The easiest way to get into GradeScope the first time is to first sign into Canvas and then click the link on our course for “GradeScope.” You should then also have the option to create an account on GradeScope (if you don’t already have one) so that you can log in to GradeScope directly. Please contact the instructors immediately if you have difficulty accessing the course on GradeScope.

+ +

To submit your assignment: run the command npm run zip in the top-level directory of the handout. This will produce a file called covey-town.zip. Submit that zip file on GradeScope.

+ +

GradeScope will provide you with feedback on your submission, but note that it will not include any marks that will be assigned after we manually grade your submission for code style (it will show 0 for this until it is graded). It may take several minutes for the grading script to complete.

+ +

GradeScope is configured to only provide feedback on at most 5 submissions per-24-hours per-student (submissions that fail to run or receive a grade of 0 are not counted in that limit). We strongly encourage you to lint and test your submission on your local development machine, and not rely on GradeScope for providing grading feedback - relying on GradeScope is a very slow feedback loop. To check for linter errors, run the command npm run lint from the terminal. The handout contains the same eslint configuration that is used by our grading script.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/assignments/project-grading.html b/assignments/project-grading.html new file mode 100644 index 00000000..030bf7b4 --- /dev/null +++ b/assignments/project-grading.html @@ -0,0 +1,663 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Final Project Grading | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Grading + + +

+ +

Each project will be graded by the team’s assigned TA mentor and the instructor. For most of the submission components below, we provide two benchmark rubrics: one for a submission that is satisfactory (full marks), and one that would be meeting our minimum expectations (a pass). In practice, when grading projects we will usually assign numeric grades and provide partial credit, using these rubrics as guidelines for those two extremes. A rubric for the remaining components will be provided by individual instructors.

+

+ + + Adjustments in project scope + + +

+ +

We are hopeful that all teams will deliver all essential AND desired features proposed in their revised project plans. However, it is possible that there might be some minor deviations from the origional plan - in the five-week implementation period, there are sure to be some teams that encounter unexpected technical hurdles. As described in greater detail in the rubrics below, teams that have regular communication with their TA regarding their project status may have the opportunity to request variances to their project scope.

+

+ + + Individual Contributions to the project + + +

+ +

In cases where team members do not equally contribute to the project, we may assign different grades to different individuals, up to an extreme of deducting 50% of the marks from a student. In the event that a group member does not contribute at all to the project, student might receive 0. We will evaluate each individual’s contribution on the basis of a variety of factors, including progress reports at weekly meetings, through inspecting version control history, through each team’s (or student’s) peer evaluations during and/or at the end of the project, and through each student’s self-reflection. We will make regular efforts to collect and distribute this feedback throughout the project. Our ultimate goal is for all students to participate and receive full marks.

+

+ + + Summary of grading + + +

+ + +

From Project Overview

+ +
    +
  • Planning (20%) +
      +
    • This includes the Preliminary Project Plan and the Revised Project Plan.
    • +
    +
  • +
  • Process (20%) +
      +
    • This includes: use of a structured development process, including code reviews, timely completion of sprint progress reports and individual/team surveys, and weekly meetings with TA Mentor.
    • +
    • This also includes appropriate division of labor within the project. For full credit, each member of the team must have at least 4 commits in the final delivered code.
    • +
    +
  • +
  • Product (40%) +
      +
    • 20% Successful delivery of your Minimum Viable Product as defined in your project plan
    • +
    • 10% Additional desirable features
    • +
    • 10% Test suite of your features.
    • +
    +
  • +
  • Reports (20%) +
      +
    • 10% Final Report
    • +
    • 10% Poster and Demo
    • +
    +
  • +
+ +

This adds up to 100%; this sum is worth 40% of the course grade.

+

+ + + Detailed Rubrics + + +

+ +

Here are the detailed rubrics for the final deliverables:

+

+ + + Minimum Viable Product (20%) + + +

+ +

+ + + Additional Desirable Features (10%) + + +

+ +

We will grade each of these using the following rubric:

+

+ + + Satisfactory: + + +

+ +
    +
  • Implemented feature satisfies the conditions of satisfaction as proposed by the team and as agreed to by the course staff. If technical difficulties resulted in features being dropped, the project may still earn full marks on “delivered features,” but these difficulties must have been documented with the course staff as you encountered them during development.
  • +
  • Implemented feature is deployed to a publicly-accessible URL, using Heroku and/or Render.com for hosting (or as per deployement instructions provided).
  • +
  • Implemented feature contains no ESLint warnings or errors; does not include any eslint-disable or ts-ignore flags
  • +
+

+ + + Meets minimum requirements: + + +

+ +
    +
  • Implemented feature largely satisfies acceptance criteria as proposed by the team, but may not meet the course staff’s interpretation of those criteria.
  • +
  • Implemented feature is deployed to a publicly-accessible URL, using Heroku and/or Render.com for hosting (or as per deployement instructions provided).
  • +
  • The implementation may have some obvious flaws, but largely works without crashing.
  • +
  • Implemented feature does not include any eslint-disable or ts-ignore flag
  • +
+

+ + + Testing (10%) + + +

+ +

The project must include evidence of testing. Ideally, all new features will be accompanied by fully automated tests, but in some circumstances (particularly when engaging with Phaser, the game library, or Tiled, the map editor), this may not be feasible. If automated tests are not possible, include a discussion of your manual testing strategy, including a script that a future developer could use to manually test the feature. There is no explicit requirement to make use of any specific testing technology (e.g. mocks, spies, etc.).

+

+ + + Satisfactory: + + +

+ +
    +
  • Any new or modified backend features include tests that validate that the feature works as intended. These tests cover the changed code, and also contain well-written assertions that thoroughly check the expected behaviors.
  • +
  • Tests contains no ESLint warnings or errors; does not include any eslint-disable or ts-ignore flags
  • +
+

+ + + Meets minimum requirements: + + +

+ +
    +
  • Any new or modified backend features include at least one test, which may or may not be an effective test.
  • +
  • Tests may contain ESlint warnings (but no errors); does not include any eslint-disable or ts-ignore flags
  • +
+

+ + + Process (20%) + + +

+ +

The Process grade includes: use of a structured development process, including code reviews, timely completion of sprint progress reports/retrospectives, individual/team surveys, peer evaluations and weekly meetings with TA Mentor (for Week 6, 8, 10-14). It also includes appropriate division of labor within the project. For full credit, each member of the team must have at least 4 commits in the final delivered code.

+
    +
  • Ongoing development progress including code reviews, github commits, issue tracker activity, etc. count 10% of the grade
  • +
  • Peer evaluations, TA meeting, submission of surveys/reports counts 10% of the project grade. +These items will be evaluated using the rubrics below.
  • +
+

+ + + Satisfactory: + + +

+ +
    +
  • There is a clear development history visible from the git repository: features were delivered incrementally, and not in a single (or several) commits at the end of the project
  • +
  • There is evidence of code review - for example, pull requests that have comments on them. Teams are expected to merge their development branches to main only after code review.
  • +
  • There is a correspondence between commit messages and the technical tasks that they are associated with; there are few (if any) commits with meaningless commits messages like . or Add files via upload.
  • +
  • Each team member has made at least 4 commits over the lifespan of the project
  • +
  • The team meets regularly with the TA mentor. All or most members attend all meetings. Members who are absent from a TA Meeting send an excuse in advance.
  • +
  • Progress reports and other weekly reports and surveys are submitted in a timely fashion, not filled in later.
  • +
  • TA Meetings include demos or other evidence of progress.
  • +
+

+ + + Meets minimum requirements: + + +

+ +
    +
  • There is a clear development history visible from the git repository: features were delivered incrementally, and not in a single (or several) commits at the end of the project
  • +
  • Each team member has made at least 2 commits over the lifespan of the project
  • +
  • Attendance at TA meetings may be irregular, but enough to give the TA a sense of the the team’s progress.
  • +
+

+ + + Individual grading for Process component + + +

+ +

Team members with unexcused absences from TA Meetings, or with fewer than 4 commits during the lifespan of the project, may receive a lowered grade for the Process component.

+

+ + + Final Report (10%) + + +

+ +

The final report should consist of three sections:

+
    +
  • Feature Overview and User Manual
  • +
  • Technical Overview
  • +
  • Process Overview
  • +
+ +

The allocation of the 10% credit will be as follows: Overview and manual will be worth 4%, technical and process overviews will be worth 3% each.

+

+ + + * Feature Overview and User Manual + + +

+ +

+ + + Satisfactory: + + +

+ +
    +
  • The Feature section contains sufficient documentation for a user to build and interact with your updated version of Covey.Town. +
      +
    • The documentation covers all the steps that a user would need to build a working version of your project.
    • +
    • The documentation includes a link to the deployed version of your project
    • +
    +
  • +
  • The documentation covers all steps that the user would need to take to exercise all of your user stories.
  • +
  • Screenshots are included that capture the key interactions between a user and your new feature.
  • +
  • Course staff were able to follow these instructions to successfully interact with your project implementation.
  • +
  • URL of the deployed app.
  • +
  • The section is at most 4 pages (roughly 2,000 words maximum), NOT including figures. Fewer pages are absolutely acceptable; consider this a rough limit
  • +
+

+ + + Meets minimum requirements: + + +

+ +
    +
  • The Feature contains documentation for a user to interact with your updated version of Covey.Town, but perhaps does not fully describe how.
  • +
  • The documentation includes at least one or two screenshots, but screenshots do not capture interactions with all of the user stories.
  • +
  • The course staff were able to figure out how to use it through trial and error.
  • +
  • URL of the deployed app.
  • +
  • The section is at most 4 pages, NOT including figures. Fewer pages are absolutely acceptable; consider this a rough limit
  • +
+

+ + + * Technical Overview + + +

+ +

+ + + Satisfactory: + + +

+ +
    +
  • Technical Overview contains a description of any substantive changes to the existing Covey.Town codebase, and of the architecture of your new code.
  • +
  • When appropriate, it uses diagrams and/or figures to illustrate a design (there is no requirement to use a specific tool or format for this - students often use lucidchart, draw.io, mermaid or even hand-drawn diagrams).
  • +
  • It provides a well-reasoned rationale for why this is the “right” design.
  • +
  • The document is at most 2 pages (fewer pages are absolutely acceptable, consider this a rough limit)
  • +
+

+ + + Meets minimum requirements: + + +

+ +
    +
  • Technical Overview includes a description of all major changes to the code compared to our existing Covey.Town codebase.
  • +
  • The document is at most 2 pages (fewer pages are absolutely acceptable, consider this a rough limit)
  • +
+

+ + + * Process Overview + + +

+ +

+ + + Satisfactory: + + +

+ +
    +
  • Process overview contains a detailed description of the manner in which agile project management processes were used during the project (i.e., sprints, sprint reviews, retrospectives and blameless reviews).
  • +
  • It provides a summary of what was planned to happen in each sprint vs what actually happened, with a discussion of what was revised as a result.
  • +
  • It provides a short list of best practices for the project that you would suggest to future teams.
  • +
  • The document is at most 2 pages (fewer pages are absolutely acceptable, consider this a rough limit)
  • +
+

+ + + Meets minimum requirements: + + +

+ +
    +
  • It provides a summary of what was planned to happen in each sprint vs what actually happened, with a discussion of what was revised as a result.
  • +
  • The document is at most 2 pages (fewer pages are absolutely acceptable, consider this a rough limit)
  • +
+

+ + + Posters and Demo (10%) + + +

+ +

Each team will be required to submit a poster. In addition, some sections may have a demo (live in-person, via zoom or by recorded video). Each instructor will provide details regarding expectations for the demo and/or presentation. The schedule and manner of these demos might also vary from section to section. The specifications for the different sections will be posted in the coming weeks.

+ +

Select projects may be hosted in our project showcase. Here are selected projects from the Fall 2022 project showcase and Fall 2023 project showcase. Posters will only be included in the showcase with the team’s permission.

+

+ + + Individual Reflection + + +

+ + +

Accompanying the final team deliverable will be an individual reflection, which every student must submit on their own. +Satisfactory completion of all parts of this reflection is required to receive an “A” grade in the course, and may be used to calibrate project scores across multiple team members. +The individual reflection also provides an opportunity for students to provide confidential feedback on the performance of their teammates.

+

+ + + Project Concept + + +

+ + +

Reflect on the evolution of your project concept: How does the project that you delivered compare to what you originally planned to deliver? What caused these deviations?

+

+ + + Satisfactory: + + +

+ +
    +
  • Is at least 2 paragraphs long;
  • +
  • Includes at least 1 paragraph describing all variances from original project concept;
  • +
  • Includes at least 1 paragraph of personal reflection on the cause of any variances from the project concept.
  • +
+

+ + + Project Process + + +

+ + +

Reflect on the evolution of your development process: How did the process by which you designed and implemented evolve from your original project plan? Were there any processes that in hindsight, you wish that you followed, or wish that you followed better?

+

+ + + Satisfactory: + + +

+ +
    +
  • Is at least 2 paragraphs long;
  • +
  • Includes at least 1 paragraph describing all variances from the software development processes envisioned in your original project plan;
  • +
  • Includes at least 1 paragraph describing software processes (described in class or not) that you wish you had followed, or wish you had followed better, supported by evidence from your personal experience working on the project.
  • +
+

+ + + Project Team + + +

+ + +

Reflect on your team dynamic: Provide a frank (and ideally, blameless) postmortem of your and your teammates collaborative performance and participation. If you had to do this same project over with the same teammates, what would you have done differently (or not) to improve your team’s overall performance? Do you think that each of your teammates are deserving of the same grade as you?

+

+ + + Satisfactory: + + +

+ +
    +
  • Is at least 2 paragraphs long;
  • +
  • Includes at least 1 paragraph reflecting on your own performance as a team member on this project, including what you would have done differently, given what you know now;
  • +
  • Includes at least 1 paragraph reflecting on your overall team dynamic, including strengths and weaknesses. Reflect on how you might have organized your team differently given what you know now.
  • +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/assignments/project-overview.html b/assignments/project-overview.html new file mode 100644 index 00000000..ba041a42 --- /dev/null +++ b/assignments/project-overview.html @@ -0,0 +1,447 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Project Overview | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Project Overview + + +

+ +

The individual and team project for this class are designed to mirror the experiences of a software engineer joining a new development team: +you will be “onboarded” to our codebase, make several individual contributions, and then form a team to propose, develop and implement a new feature. +The codebase that we are be developing on is a remote collaboration tool called Covey.Town. +Covey.Town provides a virtual meeting space where different groups of people can have simultaneous video calls, allowing participants to drift between different conversations, just like in real life. +Covey.Town is inspired by existing products like Gather.Town, Sococo, and Gatherly.IO — but it is an open source effort, and the features will be proposed and implemented by you! +All implementation will take place in the TypeScript programming language, using React for the user interface.

+

+ + + Overview of Project Deliverables + + +

+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateDeliverableDescription
01/31/2024Team Formation SurveySpecify preferences for teammates
02/05/2024Project Kick-off MeetingSchedule a meeting with your Mentor TA during this week
02/14/2024Preliminary Project PlanPropose a new feature for Covey.Town that can be planned and implemented within 7 weeks
02/28/2024Revised Project PlanRefine the scope of your feature based on staff feedback, define detailed requirements and project acceptance criteria.
04/10/2024Project Delivery - Implementation and DocumentationDeliver your new feature, including design documentation and tests
+

+ + + Summary of Project Grading + + +

+ +

Your overall project grade (which will account for 40% of your final grade in this course) will be the weighted average of each of the deliverables.

+ +
    +
  • Planning (20%) +
      +
    • This includes the Preliminary Project Plan and the Revised Project Plan.
    • +
    +
  • +
  • Process (20%) +
      +
    • This includes: use of a structured development process, including code reviews, timely completion of progress reports and individual/team surveys, and weekly meetings with TA Mentor.
    • +
    • This also includes appropriate division of labor within the project (i.e., roughly equal). For full credit, each member of the team must have at least 4 commits in the final delivered code.
    • +
    +
  • +
  • Product (40%) +
      +
    • 20% Successful delivery of your Minimum Viable Product as defined in your project plan
    • +
    • 10% Desirable delivered features
    • +
    • 10% Test suite of your features.
    • +
    +
  • +
  • Reports (20%) +
      +
    • 10% Final Report
    • +
    • 10% Poster and Demo
    • +
    +
  • +
+ +

In cases where team members do not equally contribute to the project, we may assign different grades to different individuals, up to an extreme of deducting 50% of the team project grade for a student (which might arise to 100% deduction for not contributing to the project at all). +We will evaluate each individual’s contribution on the basis of a variety of factors, including progress reports at weekly meetings, through inspecting version control history, through each students’ self-reflection, and through each students’ peer evaluations (during and/or at the end of the project). +We will make regular efforts to collect and distribute this feedback throughout the project — our ultimate goal is for all students to participate and receive full marks.

+

+ + + Team Formation + + +

+ +

All projects will be completed in a team of 3-4 students. +The very first deliverable for the project will be a team formation survey: you will be able to indicate +your preferences for teammates. The instructors will assign students to the teams based on a number of factors including your responses to the survey. +All students in each team must be in the same section of the class.

+

+ + + Team Meetings with TA Mentor + + +

+ +

Each team will be assigned a TA to act as a mentor, who will work closely with you for the entire project and also will serve as your point of contact for project questions and grading.

+ +

During Week 5, you will arrange a “Kickoff Meeting” with your TA mentor, where you will meet your TA mentor and have the opportunity to share any early ideas that you might want feedback on before submitting the your preliminary project plan.

+ +

Once project begins in full force, you will have weekly meetings with your TA mentor (scheduled at your team’s and the TA’s convenience) in order to help ensure that you are making progress on the project, and to help address problems that you encounter (be they technical or non-technical problems).

+

+ + + Preliminary Project Plan + + +

+ +

All projects will involve frontend and backend development of a new feature for Covey.Town. +Once teams have been formed, you and your team will decide what kind of new feature you would like to build. +Your feature should be something that can be implemented within the timeframe allotted (5-7 weeks), and will be implemented in a fork of the main Covey.Town codebase. +Given that you will be up-to-speed on the Covey.Town codebase (and have been introduced to TypeScript, React, NodeJS, and testing frameworks), +and that you will have a team of three or four, we expect that the feature that you propose will be more complex than the feature implemented in the individual assignments.

+ +

The project plan will focus on two sections:

+
    +
  • User stories and conditions of satisfaction that describe the feature that you plan to implement. EACH CONDITION OF SATISFACTION MUST HAVE A PRIORITY (Essential, Desirable, or Optional). The set of Essential items will constitute the “Minimum Viable Product” discussed above.
  • +
  • Work breakdown: Map your user stories to engineering tasks. Assign each task to a team member (or pair of team members), provide an estimate for how long each task will take, a rationale for that estimate, and schedule those stories into sprints.
  • +
+

+ + + Creating a GitHub Repository + + +

+ +

Your team’s development must take place within a GitHub repository in our GitHub Classroom. This repository will be private, and visible only to your team and the course staff. After the semester ends, you are welcome to make it public - you will have complete administrative control of the repository.

+ +

We will provide instructions to set up these repositories for all groups and will also provide the starter code for the project (after the revised project plans are submitted).

+

+ + + Revised Project Plan + + +

+ +

Based on the feedback that you receive from the course staff, you will revise your preliminary project plan, creating a more detailed plan to implement your new feature.

+ +

The project plan will include:

+
    +
  • Revised user stories and conditions of satisfaction (based on feedback on the preliminary project plan)
  • +
  • Revised work breakdown (based on feedback on the preliminary project plan)
  • +
+ +

Your team will self-organize, as agile teams do, and will use the work breakdown and schedule as the basis for weekly check-ins with your team’s TA.

+

+ + + Software Development Process + + +

+ +

Each team is expected to use of a structured development process, including use of pull requests and code reviews. You will also need to ensure appropriate division of labor within the project (i.e., roughly equal). Teams will also be expected to complete regular progress reports (or sprint retrospectives), and individual/team surveys. Peer evaluation may also be used (for Week 6, 8, 10-14). +Please note that one of the most important factors in successfully completing a team project is having effective communication.

+

+ + + Project Implementation and Documentation + + +

+ + +

Your final team deliverable will be a “release” of your new feature on GitHub (with tests), and will be accompanied by a demo. +Optionally, you may also open a pull request to merge your feature into our main repository (submitting a pull request, or the pull request being merged into our codebase is independent of the grade you receive, but provides a platform for more visiblity of your project).

+ +

Your final team deliverable will include:

+
    +
  • The implementation of your new feature
  • +
  • Automated tests for your new feature
  • +
  • A Final Report
  • +
  • A Poster & Demo (each instructor will provide specifics of the demo, which might vary for each section)
  • +
+ +

Accompanying the final team deliverable will be an individual reflection, which every student must submit on their own, which will include your reflections on:

+
    +
  • The evolution of your project concept: How does the project that you delivered compare to what you originally planned to deliver? What caused these deviations?
  • +
  • The software engineering processes that you feel could have been improved in your project: were there any procesess that in hindsight, you wish that you followed, or wish that you followed better?
  • +
  • Your team dynamic: Provide a frank (and ideally, blameless) postmortem of your and your teammates collaborative performance and participation. If you had to do this same project over with the same teammates, what would you have done differently (or not) to improve your team’s overall performance?
  • +
+ +

The details for the final project deliverable will be released by Week 10.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/assignments/project-plan.html b/assignments/project-plan.html new file mode 100644 index 00000000..cc7765a5 --- /dev/null +++ b/assignments/project-plan.html @@ -0,0 +1,486 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Preliminary Project Plan | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Project Plan Due Wednesday February 14, 11:00am ET + + +

+ +

All projects will involve frontend and backend development of a new feature for Covey.Town. +Once teams have been formed, you and your team will decide what kind of new feature you would like to build. +Your feature should be something that can be implemented within the timeframe allotted (5 weeks, plus 2 weeks of planning), and will be implemented in a fork of the main Covey.Town codebase. +You can play with a demo deployment of the app at app.covey.town, and in the coming weeks, we will provide tutorials and instructions for you to run the entire application in a local development environment, and also to deploy it to the cloud. +Given that you will be up-to-speed on the Covey.Town codebase (and have been introduced to TypeScript, React, NodeJS, and testing frameworks), +and that you will have a team of four, we expect that the feature that you propose will be at least as complex as the feature implemented in the individual assignments.

+ +

Feel free to look at existing systems like Gather.Town, Sococo, Reslash, Screen, and Gatherly.IO for inspiration on new features to build for Covey.Town. Also see the recent NYTimes Magazine article The Race to Fix Fix Virtual Meetings (click here to access through NEU libraries). +Examples of features that students might propose include:

+
    +
  • Create some new form of “Interactable” object, such as a whiteboard or game
  • +
  • Create an interface for uploading and choosing between different maps and avatars (will require also learning to use the Phaser3 API)
  • +
  • Add screenreader support - generate a textual representation of the map and what players can do to interact with it
  • +
  • Support real persistence: store data in a database (e.g. Postgres + GraphQL), allow users to register and save a profile (e.g. using Auth0)
  • +
  • Add direct messaging, image messaging, and other chat features
  • +
+ +

Please note that multiple teams might choose to propose the same feature, or a variation of that same feature - this is OK.

+ +

When considering your project, please keep in mind that you will be allowed to publicly post your project online: while your immediate audience for the project is the course staff, if you are ultimately looking for software engineering jobs or co-ops, this project can be a useful piece of your portfolio. If you build a sufficiently maintainable feature (i.e., if your project is particularly well architected and tested), we will also consider pull requests to merge your feature into the main Covey.Town codebase on GitHub, allowing you to also tell recruiters that you have contributed a feature to an open source project on GitHub!

+ +

The project plan will include:

+
    +
  • Introductory problem statement
  • +
  • User stories and acceptance criteria: high level description of how users will interact with your new feature.
  • +
  • Work breakdown: Define engineering tasks that will be necessary to implement your new feature. Map each task to a sprint.
  • +
+ +

Your assigned TA mentor will review your project plan and provide you with feedback on the scope and details provided in this plan. +Your team will self-organize, as agile teams do, and will enhance and adapt its plan during the project lifecycle. +As such, the primary goal of this document is to begin the planning process, and not to produce a detailed plan that must be followed precisely. +The course staff will provide feedback on your project to help ensure that the scope of your project is appropriate.

+ +

We list page maximums for each section as general guidance of what we are willing to grade. Please do not feel compelled to use all of the pages provided, and remember that a diagram or table can be as expressive (or more) as a comparable amount of text.

+

+ + + Problem Statement, User Stories and Acceptance Criteria (max 4 pages) + + +

+ +

Your project plan should begin with a 1-3 paragraph introductory problem statement: what problem in Covey.Town does your (proposed) feature solve? Provide a paragraph (or two) that describes why you are interested in building this feature.

+ +

Given the problem statement, develop three user stories that show how a user would interact with the feature. User stories are requirements specified in the following format +“As a < type of user >, I want < some goal > so that < some reason >.” +My conditions of satisfaction are < list of common cases and special cases that must work >.

+ +

Please make sure that your conditions of satisfaction are complete (i.e., cover all common cases) and can be turned into testable behaviors.

+ +

EACH CONDITION OF SATISFACTION MUST HAVE A PRIORITY (Essential, Desirable, or Optional). The set of Essential items will constitute the “Minimum Viable Product”. Each user story should include one or more essential and desirable conditions of satisfaction.

+ +

You should include three different user stories to describe how users will interact with your feature. Your three user stories should cover the key behavior that your feature will provide.

+ +

Do not provide more than three user stories. Your problem statement and description of user stories and conditions of satisfaction should be between 2-4 pages.

+

+ + + Work Breakdown (max 10 pages) + + +

+ +

Given the project concept that you have chosen and the functionality that you expect to implement to satisfy your user stories, define a breakdown of the work that will be necessary to complete the project.

+ +

A work breakdown includes all of the tasks necessary to accomplish the project, and will be an artifact that we will refer back to throughout the project to evaluate whether you are making satisfactory progress. +Consider all of the kinds of tasks that your team will need to perform, including knowledge acquisition, design, implementation, testing and documentation tasks. +It is hard to say (generically) how many work items are necessary.

+ +

Each task on the work breakdown should be assigned to exactly one team member (as primary responsible party), who should provide a “T-Shirt” estimate for how long it will take (along with a justification for that estimate). +Consider the dependencies between tests: perhaps an API needs to be designed and specified before implementation can begin; perhaps your development environment needs to be configured before anything else can proceed. +Assign tasks to sprints considering these dependencies.

+ +

Given the preliminary nature of your project, we do not expect that you will know all of the details about precisely how to implement your feature such that you could break it down into tasks that you feel could be implemented in a day or two. Large tasks (those which you can not provide a responsible estimate for) must be accompanied by smaller “research” tasks that can be performed early on in the project. You may wish to provide deadlines by which the task must either be refined into smaller tasks (based on new knowledge gathered), or reworked/abandoned.

+ +

In keeping with the agile philosophy, you should plan on spending the first two weeks of the project (from January 31 until February 14) in a “Sprint -1” in which you will undertake organizational and research tasks to help you improve your project plan (due on February 14).

+ +

For example: Consider if you were proposing the “Viewing Area” project (the individual project), without the experience of having completed it. It might be difficult to consider how to break down a task like “Implement the frontend components for synchronized video playback” into something that you could commit to doing within a day or two. Given that this is a task that can be delayed until the end of the project (no other tasks depend on it), it would be wise to consider having some tasks early on in the project, such as: “Find react components that embed YouTube videos,” and “Implement simple video player that does not synchronize playback.” Completing these smaller tasks early would let you both demonstrate that some forward progress is being made, and also allow you to create a much more responsible estimate for how that last, otherwise insurmountably large task would take.

+ +

Do not wait for your TA feedback to begin this work. You probably know more about the details of your project then they do. It will be helpful for all concerned if your Project Plan lists the major unknowns or things that you expect to need help with– this will help the TA provide more useful feedback for you

+ +

Be realistic, and leave time for contingencies (including the time around the midterm exam on March 11-13). +Remember that you will need to have a demo prepared of your feature by 4/10 - just 7 weeks from the due date of this assignment, not counting spring break. +If you are uncertain that some tasks will be feasible, then be sure to include evaluation tasks earlier-on in the project that will allow for “go/no-go” decisions to move forward on a task or drop it.

+ +

We understand that it is quite difficult to estimate the technical complexity of a new project (as you are doing in the case of this course). +We will provide you with feedback on this preliminary project plan, which you will use to produce a revised project plan (due Feb 28). +Throughout the project period, teams will meet regularly with their dedicated TA Mentor, who will help track progress on a week-to-week basis and help to determine when adjustments to the project scope are needed.

+ +

Each work item should contain the following information:

+
    +
  • Task to be performed
  • +
  • User story (or stories) that this task relates to
  • +
  • Team member responsible for completing the task
  • +
  • T-shirt size estimate of how long will be needed to complete the task, using the following buckets: +
      +
    • Small: Can likely be completed by one team member in one sitting of less than 3-4 hours
    • +
    • Medium: Likely to require involvement of multiple team members, over the course of 1-2 days
    • +
    • Large: Currently unable to provide a responsible estimate.
    • +
    +
  • +
  • A brief (1-2 sentence max) justification of how you reached the size estimate of the task
  • +
  • Milestone for delivering the task, chosen from one of the following: +
      +
    • Sprint 0: Feb 14-Feb 28
    • +
    • Sprint 1: Feb 29-Mar 13 (Sprint 1 is just a single week, not including spring break)
    • +
    • Sprint 2: Mar 14-Mar 28
    • +
    • Sprint 3: Mar 29-Apr 10
    • +
    +
  • +
+ +

Your work breakdown should take the format of a simple textual list.

+

+ + + Submission + + +

+ +

Your project plan should be submitted as a single PDF in Canvas to the assignment “Preliminary Project Plan.” +Each team submits a single document to Canvas. +This assignment is due Feb 14 at 11am.

+

+ + + Grading + + +

+ +

The project plan will account for 10% of your project grade, and will be graded out of 100 points. The grading of the project plan is further broken down as follows:

+

+ + + Introductory problem statement (5 points): + + +

+ +
    +
  • Receive full marks if there is a narrative consisting of 1-3 paragraphs that describes a specific problem that your project aims to solve.
  • +
  • Receive partial credit if the narrative is present, but does not describe a problem that the project aims to solve
  • +
+

+ + + User stories (15 points): + + +

+ +

Each of the three user stories will account for 5% of your grade on this assignment and will be graded as follows:

+
    +
  • Receive full marks if: +
      +
    • The user story fits the problem statement
    • +
    • The user story satisfies the INVEST criteria for good user stories (construed quite broadly)
    • +
    • The user story includes conditions of satisfaction that cover the “normal” expected behavior of the feature, and any relevant error cases
    • +
    • The conditions of satisfaction include priority
    • +
    • Each user story includes one or more condition of satisfaction marked with essential, desirable and extension priority.
    • +
    +
  • +
+

+ + + Work breakdown (80 points): + + +

+ +

Your work breakdown will be evaluated holistically on the following rubric:

+

+ + + Coverage of tasks needed (20 points): + + +

+ +

Receive full marks if the work breakdown includes all (reasonably expected) tasks to implement your feature, considering these kinds of tasks:

+
    +
  • Background research
  • +
  • Design of interfaces and data types
  • +
  • Deployment of third-party services
  • +
  • Implementation
  • +
  • Testing
  • +
  • Documentation
  • +
+ +

It is not possible to state generically for all projects whether all of the above types of tasks are necessary. +However, we believe that this list is exhaustive (we do not expect other kinds of tasks).

+

+ + + Assignment of tasks (10 points): + + +

+ +

Receive full marks if:

+
    +
  • Each element on the work breakdown is assigned to one team member
  • +
  • The distribution of tasks of each size are roughly similar between the whole team (no single person is assigned significantly more or fewer tasks of one size)
  • +
+

+ + + Sizing of tasks (40 points): + + +

+ +

Receive full marks if each element on the work breakdown:

+
    +
  • Has a size estimate (small, medium, or large) that is provided by the team member assigned the task.
  • +
  • Has a responsible justification for that estimation
  • +
  • Every “large” task: +
      +
    • Is accompanied by a reasonable explanation of why the team is unable to provide a responsible estimate
    • +
    • Is accompanied by at least one small or medium task, scheduled well-before the “large” task is due to be completed. We would expect that most of these research tasks are scheduled to sprint 0, or perhaps sprint 1.
    • +
    +
  • +
+

+ + + Scheduling of tasks (10 points): + + +

+ +

Receive full marks if each element on the work breakdown:

+
    +
  • Is assigned to a sprint
  • +
  • There are no obvious constraint violations (tasks that logically must happen before others should be scheduled before them)
  • +
  • There are no “Large” tasks scheduled in sprint 0
  • +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/assignments/revised-project-plan.html b/assignments/revised-project-plan.html new file mode 100644 index 00000000..da7128de --- /dev/null +++ b/assignments/revised-project-plan.html @@ -0,0 +1,472 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Revised Project Plan | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Revised Project Plan Due Wednesday February 28, 11:00am ET + + +

+ +

You should use the feedback on your preliminary project plan to revise the scope of your project, and discuss any changes with your assigned TA. The goal of this assignment is to finalize the intended scope of your project based on these discussions and to outline the tasks that you will need to accomplish in order to complete your project.

+ +

Your assigned TA mentor will review your project plan and provide you with feedback on the scope and details provided in your preliminary plan. +In this deliverable, you will revise your project plan based on that feedback, and any preliminary research tasks that you have completed. +If there were any deficiencies or concerns noted by your TA mentor on your preliminary plan, you must address those concerns in this revised plan. +If you are unsure of how to address any of these concerns or if you have addressed them, please discuss them with your TA mentor before submitting this revised plan.

+ +

We list page maximums for each section as general guidance of what we are willing to grade. Please do not feel compelled to use all of the pages provided, and remember that a diagram or table can be as expressive (or more) as a comparable amount of text.

+

+ + + Revised Problem Statement, User Stories and Acceptance Criteria (max 4 pages) + + +

+ +

Your revised project plan should begin with a 1-3 paragraph introductory problem statement: what problem in Covey.Town does your (proposed) feature solve? Provide a paragraph (or two) that describes why you are interested in building this feature.

+ +

Given the problem statement, develop three user stories that show how a user would interact with the feature. User stories are requirements specified in the format +“As a < type of user >, I want < some goal > so that < some reason >.” +My conditions of satisfaction are < list of common cases and special cases that must work >. Please make sure that your conditions of satification are complete (i.e., cover all common cases) and can be turned into testable behaviors.

+ +

EACH USER STORY MUST HAVE ONE OR MORE ESSENTIAL AND DESIRABLE CONDITION(S) OF SATISFACTION. The set of essential items will constitute the “Minimum Viable Product”. EACH CONDITION OF SATISFACTION MUST HAVE A PRIORITY (Essential, Desirable, or Optional).

+ +

You should include three different user stories to describe how users will interact with your feature. +Your three user stories should cover the key behavior that your feature will provide. +Do not provide more than three user stories. Your problem statement and description of user stories and conditions of satisfaction should be between 2-4 pages.

+ +

This revised project plan should be a standalone document (it is OK to copy/paste from the preliminary plan in this case).

+

+ + + Revised Work Breakdown (max 10 pages) + + +

+ +

Given the project concept that you have chosen and the functionality that you expect to implement to satisfy your user stories, define a breakdown of the work that will be necessary to complete the project. This breakdown is required for all essential, desirable and optional tasks.

+ +

A work breakdown includes all of the tasks necessary to accomplish the project, and will be an artifact that we will refer back to throughout the project to evaluate whether you are making satisfactory progress. +Consider all of the kinds of tasks that your team will need to perform, including knowledge acquisition, design, implementation, testing and documentation tasks. +It is hard to say (generically) how many work items are necessary.

+ +

Each task on the work breakdown should be assigned to exactly one team member (as primary responsible party), who should provide a “T-Shirt” estimate for how long it will take (along with a justification for that estimate). Feel free to assign a second person as secondary responsible party (if you plan to use pair programming) but make clear who has primary responsibility.

+ +

Consider the dependencies between tests: perhaps an API needs to be designed and specified before implementation can begin; perhaps your development environment needs to be configured before anything else can proceed. +Assign tasks to sprints considering these dependencies.

+ +

Given the preliminary nature of your project, we do not expect that you will know all of the details about precisely how to implement your feature such that you could break it down into tasks that you feel could be implemented in a day or two. Large tasks (those which you can not provide a responsible estimate for) must be accompanied by smaller “research” tasks that can be performed early on in the project. You may wish to provide deadlines by which the task must either be refined into smaller tasks (based on new knowledge gathered), or reworked/abandoned.

+ +

In keeping with the agile philosophy, you should plan on spending these two weeks of the project (from February 14 until February 28) in a “Sprint 0” in which you will undertake tasks you previously planned including those that help you improve your project plan.

+ +

For example: Consider if you were proposing the “Viewing Area” project (the individual project), without the experience of having completed it. It might be difficult to consider how to break down a task like “Implement the frontend components for sychnronized video playback” into something that you could commit to doing within a day or two. Given that this is a task that can be delayed until the end of the project (no other tasks depend on it), it would be wise to consider having some tasks early on in the project, such as: “Find react components that embed YouTube videos,” and “Implement simple video player that does not synchronize playback.” Completing these smaller tasks early would let you both demonstrate that some forward progress is being made, and also allow you to create a much more responsible estimate for how that last, otherwise insurmountably large task would take.

+ +

Do not wait for your TA feedback to begin this work. You probably know more about the details of your project then they do. It will be helpful for all concerned if your Project Plan lists the major unknowns or things that you expect to need help with– this will help the TA provide more useful feedback for you

+ +

Be realistic, and leave time for contingencies (including the time around the midterm exam on Nov 1-3). +Remember that you will need to have a demo prepared of your feature by April 10th - just 5 weeks from the due date of this assignment (not counting spring break).

+ +

If you are uncertain that some tasks will be feasible, then be sure to include evaluation tasks earlier-on in the project that will allow for “go/no-go” decisions to move forward on a task or drop it.

+ +

We understand that it is quite difficult to estimate the technical complexity of a new project (as you are doing in the case of this course).

+ +

Throughout the project period, teams will meet regularly with their dedicated TA Mentor, who will help track progress on a week-to-week basis and help to determine when adjustments to the project scope are needed.

+ +

Each work item should contain the following information:

+
    +
  • Task to be performed
  • +
  • User story (or stories) that this task relates to
  • +
  • Team member responsible for completing the task
  • +
  • T-shirt size estimate of how long will be needed to complete the task, using the following buckets: +
      +
    • Small: Can likely be completed by one team member in one sitting of less than 3-4 hours
    • +
    • Medium: Likely to require involvement of multiple team members, over the course of 1-2 days
    • +
    • Large: Currently unable to provide a responsible estimate.
    • +
    +
  • +
  • A brief (1-2 sentence max) justification of how you reached the size estimate of the task
  • +
  • Milestone for delivering the task, chosen from one of the following: +
      +
    • Sprint 0: Feb 14-Feb 28
    • +
    • Sprint 1: Feb 29-Mar 13 (Sprint 1 is just a single week, not including spring break)
    • +
    • Sprint 2: Mar 14-Mar 28
    • +
    • Sprint 3: Mar 29-Apr 10
    • +
    +
  • +
+ +

Your work breakdown may take the format of a simple textual list.

+ +

Mandatory update for all teams: Even if your work breakdown is entirely unchanged compared to the preliminary plan, please be certain to include updates on all “sprint 0” tasks (those which were scheduled to be completed before the due date of this revised plan). Many teams included research/learning tasks in the first week to help inform the rest of the work breakdown: you should be certain to revise your work breakdown based on what you have learned so far.

+ +

Please note that in order to receive full credit for the final delivered product, each team is expected to deliver Minimum Viable Product (marked as essential) as well as additional features proposed in the project plan (marked as desirables). Please plan your work accordingly.

+

+ + + Submission + + +

+ +

Your revised project plan should be submitted as a single PDF on Canvas under the assignment “Revised Project Plan.” +Each team submits a single document. Make sure that your team number is added to the title of the document. +This assignment is due Feb 28 at 11am.

+

+ + + Grading + + +

+ +

The revised project plan will account for 10% of your project grade, and will be graded out of 100 points. The grading of the revised project plan is further broken down as follows:

+

+ + + Introductory problem statement (5 points): + + +

+ +
    +
  • Receive full marks if there is a narrative consisting of 1-3 paragraphs that describes a specific problem that your project aims to solve.
  • +
  • Receive partial credit if the narrative is present, but does not describe a problem that the project aims to solve
  • +
+

+ + + User stories (45 points): + + +

+ +

Each of the three user stories will account for 5% of your grade on this assignment and will begraded as follows:

+
    +
  • Receive full marks if: +
      +
    • The user story fits the problem statement
    • +
    • The user story satisfies the INVEST criteria for good user stories (construed quite broadly)
    • +
    • The user story includes conditions of satisfaction that cover the “normal” expected behavior of the feature, and any relevant error cases
    • +
    • Conditions of satisfaction can be turned into Testable behaviors
    • +
    • Conditions of satisfaction are prioritized
    • +
    • Each user story includes one or more condition of satisfaction marked with essential, desirable and extension priority.
    • +
    • Any deficiencies or concerns noted by your TA mentor on your preliminary plan are addressed
    • +
    +
  • +
+

+ + + Work breakdown (50 points): + + +

+ +

Your work breakdown will be evaluated holistically on the following rubric:

+

+ + + Coverage of tasks needed (20 points): + + +

+ +

Receive full marks if the work breakdown includes all (reasonably expected) tasks to implement your feature, considering these kinds of tasks:

+
    +
  • Background research
  • +
  • Design of interfaces and data types
  • +
  • Deployment of third-party services
  • +
  • Implementation
  • +
  • Testing
  • +
  • Documentation
  • +
+ +

It is not possible to state generically for all projects whether all of the above types of tasks are necessary. +However, we believe that this list is exhaustive (we do not expect other kinds of tasks).

+

+ + + Assignment of tasks (5 points): + + +

+ +

Receive full marks if:

+
    +
  • Each element on the work breakdown is assigned to one team member
  • +
  • The distribution of tasks of each size are roughly similar between the whole team (no single person is assigned significantly more or fewer tasks of one size)
  • +
  • Any deficiencies or concerns noted by your TA mentor on your preliminary plan are addressed
  • +
+

+ + + Sizing of tasks (20 points): + + +

+ +

Receive full marks if each element on the work breakdown:

+
    +
  • Has a size estimate (small, medium, or large) that is provided by the team member assigned the task.
  • +
  • Has a responsible justification for that estimation
  • +
  • Every “large” task: +
      +
    • Is accompanied by a reasonable explanation of why the team is unnable to provide a responsible estimate
    • +
    • Is accompanied by at least one small or medium task, scheduled well-before the “large” task is due to be completed. We would expect that most of these research tasks are scheduled to sprint -1, or perhaps sprint 0.
    • +
    +
  • +
  • Any deficiencies or concerns noted by your TA mentor on your preliminary plan are addressed
  • +
+

+ + + Scheduling of tasks (5 points): + + +

+ +

Receive full marks if each element on the work breakdown:

+
    +
  • Is assigned to a sprint
  • +
  • There are no obvious constraint violations (tasks that logically must happen before others should be scheduled before them)
  • +
  • There are no “Large” tasks scheduled in sprint 0
  • +
  • Tasks scheduled to be completed before 3/1 have an update as to their status, and the work breakdown is updated to reflect the result of these tasks.
  • +
  • Any deficiencies or concerns noted by your TA mentor on your preliminary plan are addressed
  • +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/calendar/index.html b/calendar/index.html new file mode 100644 index 00000000..471197c4 --- /dev/null +++ b/calendar/index.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Calendar | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+ +

Given the synchronization amongst the sections, and the academic calendar starting on a Wednesday, the course material generally is designed to run on weeks from Wednesday-Tuesday. We provide a schedule for each section:

+ + + + +
+

Table of contents

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/calendar/s1.html b/calendar/s1.html new file mode 100644 index 00000000..cdc840c6 --- /dev/null +++ b/calendar/s1.html @@ -0,0 +1,1301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Section 1 - Calendar | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +

+ + + Section 1 - Calendar + + +

+ + +
Prof Bell
+Mondays & Thursdays 11:45am-1:25pm, West Village H 110
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Week 1 + +
+ +
Mon, Jan 08
+ + + + + + + +
+ +
+ + + + + +
+ +
Thu, Jan 11
+ + + + + + + +
+ +
+ +
Week 2 + +
+ +
Mon, Jan 15
+ + + + + +
+ + MLK Day (No class) + +
+
+
+ +
+ +
+ + + + + +
+ +
Thu, Jan 18
+ + + + + +
+ + Test Adequacy + +
+
+
+ +
+ +
+ +
Week 3 + +
+ +
Mon, Jan 22
+ + + + + + + +
+ +
+ + + +
+ + +
Wed, Jan 24
+
+ + + Individual Project Deliverable 1 + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Jan 25
+ + + + + + + +
+ +
+ +
Week 4 + +
+ +
Mon, Jan 29
+ + + + + +
+ + Concurrency Patterns + +
+
+
+ +
+ +
+ + + +
+ + +
Wed, Jan 31
+
+ + + Project Formation + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Feb 01
+ + + + + + + +
+ +
+ +
Week 5 + +
+ +
Mon, Feb 05
+ + + + + + + +
+ +
+ + + + + +
+ +
Thu, Feb 08
+ + + + + +
+ + React Part 2 (Hooks) + +
+
+
+ +
+ +
+ +
Week 6 + +
+ +
Mon, Feb 12
+ + + + + + + + + + + + +
+ +
+ + + +
+ + +
Wed, Feb 14
+
+ + + Preliminary Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Feb 15
+ + + + + + + +
+ +
+ +
Week 7 + +
+ +
Mon, Feb 19
+ + + + + +
+ + President's Day (No class) + +
+
+
+ +
+ +
+ + + +
+ + +
Wed, Feb 21
+
+ + + Individual Project Deliverable 2 + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Feb 22
+ + + + + + + +
+ +
+ +
Week 8 + +
+ +
Mon, Feb 26
+ + + + + +
+ + Work on project (Discuss project plan/revisions) + +
+
+
+ +
+ +
+ + + +
+ + +
Wed, Feb 28
+
+ + + Revised Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Feb 29
+ + + + + + + + + + +
+ + Midterm Review + +
+
+
+ +
+ +
+ +
Week 9 + +
+ +
Mon, Mar 04
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
+ + + + + +
+ +
Thu, Mar 07
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
+ +
Week 10 + +
+ +
Mon, Mar 11
+ + + + + +
+ + Midterm + +
+
+
+ +
+ +
+ + + + + +
+ +
Thu, Mar 14
+ + + + + +
+ + Security + +
+
+
+ +
+ +
+ +
Week 11 + +
+ +
Mon, Mar 18
+ + + + + +
+ + Work on project + +
+
+
+ +
+ +
+ + + + + +
+ +
Thu, Mar 21
+ + + + + +
+ + Technical Debt + +
+
+
+ +
+ +
+ +
Week 12 + +
+ +
Mon, Mar 25
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ + + + + +
+ +
Thu, Mar 28
+ + + + + +
+ + Open Source + +
+
+
+ +
+ +
+ +
Week 13 + +
+ +
Mon, Apr 01
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ + + + + +
+ +
Thu, Apr 04
+ + + + + +
+ + Ethics + +
+
+
+ +
+ +
+ +
Week 14 + +
+ +
Mon, Apr 08
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ + + +
+ + +
Wed, Apr 10
+
+ + + Project Final Deliverable + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Apr 11
+ + + + + +
+ + Presentations + +
+
+
+ +
+ +
+ +
Week 15 + +
+ +
Mon, Apr 15
+ + + + + +
+ + Patriots Day (No class) + +
+
+
+ +
+ +
+ + + + + + + +
+ + +
+
    + +
+
    + +
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/calendar/s2.html b/calendar/s2.html new file mode 100644 index 00000000..1715fc2f --- /dev/null +++ b/calendar/s2.html @@ -0,0 +1,1330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Section 2 - Calendar | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +

+ + + Section 2 - Calendar + + +

+ + +
Prof Bhutta
+Mondays & Wednesdays 2:50pm-4:30pm, Dodge Hall 173
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Week 1 + +
+ +
Mon, Jan 08
+ + + + + + + +
+ +
+ + + + + +
+ +
Wed, Jan 10
+ + + + + + + +
+ +
+ +
Week 2 + +
+ +
Mon, Jan 15
+ + + + + +
+ + MLK Day (No class) + +
+
+
+ +
+ +
+ + + + + +
+ +
Wed, Jan 17
+ + + + + +
+ + Test Adequacy + +
+
+
+ +
+ +
+ +
Week 3 + +
+ +
Mon, Jan 22
+ + + + + + + +
+ +
+ + + +
+ + +
Wed, Jan 24
+
+ + + Individual Project Deliverable 1 + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Jan 24
+ + + + + + + +
+ +
+ +
Week 4 + +
+ +
Mon, Jan 29
+ + + + + +
+ + Concurrency Patterns + +
+
+
+ +
+ +
+ + + +
+ + +
Wed, Jan 31
+
+ + + Project Formation + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Jan 31
+ + + + + + + +
+ +
+ +
Week 5 + +
+ +
Mon, Feb 05
+ + + + + + + +
+ +
+ + + + + +
+ +
Wed, Feb 07
+ + + + + +
+ + React Part 2 (Hooks) + +
+
+
+ +
+ +
+ +
Week 6 + +
+ +
Mon, Feb 12
+ + + + + + + + + + + + +
+ +
+ + + +
+ + +
Wed, Feb 14
+
+ + + Preliminary Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Feb 14
+ + + + + + + +
+ +
+ +
Week 7 + +
+ +
Mon, Feb 19
+ + + + + +
+ + President's Day (No class) + +
+
+
+ +
+ +
+ + + +
+ + +
Wed, Feb 21
+
+ + + Individual Project Deliverable 2 + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Feb 21
+ + + + + + + +
+ +
+ +
Week 8 + +
+ +
Mon, Feb 26
+ + + + + +
+ + Work on project (Discuss project plan/revisions) + +
+
+
+ +
+ +
+ + + +
+ + +
Wed, Feb 28
+
+ + + Revised Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Feb 28
+ + + + + + + + + + +
+ + Midterm Review + +
+
+
+ +
+ +
+ +
Week 9 + +
+ +
Mon, Mar 04
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
+ + + + + +
+ +
Wed, Mar 06
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
+ +
Week 10 + +
+ +
Mon, Mar 11
+ + + + + +
+ + Midterm + +
+
+
+ +
+ +
+ + + + + +
+ +
Wed, Mar 13
+ + + + + +
+ + Security + +
+
+
+ +
+ +
+ +
Week 11 + +
+ +
Mon, Mar 18
+ + + + + +
+ + Work on project + +
+
+
+ +
+ +
+ + + + + +
+ +
Wed, Mar 20
+ + + + + +
+ + Technical Debt + +
+
+
+ +
+ +
+ +
Week 12 + +
+ +
Mon, Mar 25
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ + + + + +
+ +
Wed, Mar 27
+ + + + + +
+ + Open Source + +
+
+
+ + + + +
+ + Ethics + +
+
+
+ +
+ +
+ +
Week 13 + +
+ +
Mon, Apr 01
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ + + + + +
+ +
Wed, Apr 03
+ + + + + +
+ + Work on Project / Mini-Demo + +
+
+
+ +
+ +
+ +
Week 14 + +
+ +
Mon, Apr 08
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ + + +
+ + +
Wed, Apr 10
+
+ + + Project Final Deliverable + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Apr 10
+ + + + + +
+ + Presentations + +
+
+
+ +
+ +
+ +
Week 15 + +
+ +
Mon, Apr 15
+ + + + + +
+ + Patriots Day (No class) + +
+
+
+ +
+ +
+ + + + + +
+ +
Wed, Apr 17
+ + + + + +
+ + Presentations + +
+
+
+ +
+ +
+ +
+ + +
+
    + +
+
    + +
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/calendar/s3.html b/calendar/s3.html new file mode 100644 index 00000000..11075895 --- /dev/null +++ b/calendar/s3.html @@ -0,0 +1,1301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Section 3 - Calendar | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +

+ + + Section 3 - Calendar + + +

+ + +
Prof Bhutta
+Tuesdays 11:45am-1:25pm & Thursdays 2:50pm-4:30pm, Snell Library 031
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Week 1 + + + +
+ +
Tue, Jan 09
+ + + + + + + +
+ +
+ + + +
+ +
Thu, Jan 11
+ + + + + + + +
+ +
+ +
Week 2 + + + +
+ +
Tue, Jan 16
+ + + + + +
+ + Test Adequacy + +
+
+
+ +
+ +
+ + + +
+ +
Thu, Jan 18
+ + + + + + + +
+ +
+ +
Week 3 + + + +
+ +
Tue, Jan 23
+ + + + + + + +
+ +
+ +
+ + +
Wed, Jan 24
+
+ + + Individual Project Deliverable 1 + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Jan 25
+ + + + + +
+ + Concurrency Patterns + +
+
+
+ +
+ +
+ +
Week 4 + + + +
+ +
Tue, Jan 30
+ + + + + + + +
+ +
+ +
+ + +
Wed, Jan 31
+
+ + + Project Formation + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Feb 01
+ + + + + + + +
+ +
+ +
Week 5 + + + +
+ +
Tue, Feb 06
+ + + + + +
+ + React Part 2 (Hooks) + +
+
+
+ +
+ +
+ + + +
+ +
Thu, Feb 08
+ + + + + + + +
+ +
+ +
Week 6 + + + +
+ +
Tue, Feb 13
+ + + + + + + +
+ +
+ +
+ + +
Wed, Feb 14
+
+ + + Preliminary Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Feb 15
+ + + + + + + +
+ +
+ +
Week 7 + + + +
+ +
Tue, Feb 20
+ + + + + +
+ + Work on project (Discuss project plan/revisions) + +
+
+
+ +
+ +
+ +
+ + +
Wed, Feb 21
+
+ + + Individual Project Deliverable 2 + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Feb 22
+ + + + + + + +
+ +
+ +
Week 8 + + + +
+ +
Tue, Feb 27
+ + + + + +
+ + Work on project (Discuss project plan/revisions) + +
+
+
+ +
+ +
+ +
+ + +
Wed, Feb 28
+
+ + + Revised Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Feb 29
+ + + + + + + + + + +
+ + Midterm Review + +
+
+
+ +
+ +
+ +
Week 9 + + + +
+ +
Tue, Mar 05
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
+ + + +
+ +
Thu, Mar 07
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
+ +
Week 10 + + + +
+ +
Tue, Mar 12
+ + + + + +
+ + Midterm + +
+
+
+ +
+ +
+ + + +
+ +
Thu, Mar 14
+ + + + + +
+ + Security + +
+
+
+ +
+ +
+ +
Week 11 + + + +
+ +
Tue, Mar 19
+ + + + + +
+ + Work on project + +
+
+
+ +
+ +
+ + + +
+ +
Thu, Mar 21
+ + + + + +
+ + Technical Debt + +
+
+
+ +
+ +
+ +
Week 12 + + + +
+ +
Tue, Mar 26
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ + + +
+ +
Thu, Mar 28
+ + + + + +
+ + Open Source + +
+
+
+ + + + +
+ + Ethics + +
+
+
+ +
+ +
+ +
Week 13 + + + +
+ +
Tue, Apr 02
+ + + + + +
+ + Work on Project / Mini-Demo + +
+
+
+ +
+ +
+ + + +
+ +
Thu, Apr 04
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ +
Week 14 + + + +
+ +
Tue, Apr 09
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ +
+ + +
Wed, Apr 10
+
+ + + Project Final Deliverable + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Thu, Apr 11
+ + + + + +
+ + Presentations + +
+
+
+ +
+ +
+ +
Week 15 + + + +
+ +
Tue, Apr 16
+ + + + + +
+ + Presentations + +
+
+
+ +
+ +
+ + + + + +
+ + +
+
    + +
+
    + +
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/calendar/s4.html b/calendar/s4.html new file mode 100644 index 00000000..720e983c --- /dev/null +++ b/calendar/s4.html @@ -0,0 +1,1126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Section 4 - Calendar | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +

+ + + Section 4 - Calendar + + +

+ + +
Prof Wand
+Wednesdays 6:00pm-9:20pm, Virtually - See Canvas for Zoom Link
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Week 1 + + + + + + + +
+ +
Wed, Jan 10
+ + + + + + + + + + + + +
+ +
+ +
Week 2 + + + + + + + +
+ +
Wed, Jan 17
+ + + + + +
+ + Test Adequacy + +
+
+
+ + + + + + +
+ +
+ +
Week 3 + + + + + +
+ + +
Wed, Jan 24
+
+ + + Individual Project Deliverable 1 + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Jan 24
+ + + + + + + + + + +
+ + Concurrency Patterns + +
+
+
+ +
+ +
+ +
Week 4 + + + + + +
+ + +
Wed, Jan 31
+
+ + + Project Formation + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Jan 31
+ + + + + + + + + + + + +
+ +
+ +
Week 5 + + + + + + + +
+ +
Wed, Feb 07
+ + + + + +
+ + React Part 2 (Hooks) + +
+
+
+ + + + + + +
+ +
+ +
Week 6 + + + + + +
+ + +
Wed, Feb 14
+
+ + + Preliminary Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Feb 14
+ + + + + + + + + + + + +
+ +
+ +
Week 7 + + + + + +
+ + +
Wed, Feb 21
+
+ + + Individual Project Deliverable 2 + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Feb 21
+ + + + + + + + + + +
+ + Work on project (Discuss project plan/revisions) + +
+
+
+ +
+ +
+ +
Week 8 + + + + + +
+ + +
Wed, Feb 28
+
+ + + Revised Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Feb 28
+ + + + + + + + + + +
+ + Midterm Review + +
+
+
+ +
+ +
+ +
Week 9 + + + + + + + +
+ +
Wed, Mar 06
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
+ +
Week 10 + + + + + + + +
+ +
Wed, Mar 13
+ + + + + +
+ + Midterm + +
+
+
+ +
+ +
+ +
Week 11 + + + + + + + +
+ +
Wed, Mar 20
+ + + + + +
+ + Security + +
+
+
+ + + + +
+ + Ethics + +
+
+
+ +
+ +
+ +
Week 12 + + + + + + + +
+ +
Wed, Mar 27
+ + + + + +
+ + Team Meetings + +
+
+
+ +
+ +
+ +
Week 13 + + + + + + + +
+ +
Wed, Apr 03
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ +
Week 14 + + + + + +
+ + +
Wed, Apr 10
+
+ + + Project Final Deliverable + +
Due at 11:00 am
+
+ + +
+ +
+ +
+ +
Wed, Apr 10
+ + + + + +
+ + Presentations + +
+
+
+ +
+ +
+ +
Week 15 + + + + + + + +
+ +
Wed, Apr 17
+ + + + + +
+ + No Class + +
+
+
+ +
+ +
+ +
+ + +
+
    + +
+
    + +
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/calendar/s5.html b/calendar/s5.html new file mode 100644 index 00000000..6aa531f6 --- /dev/null +++ b/calendar/s5.html @@ -0,0 +1,1301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Section 5 - Calendar | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +

+ + + Section 5 - Calendar + + +

+ + +
Prof Bhutta
+Tuesdays & Fridays 3:25pm-5:05pm, Robinson Hall 109
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Week 1 + + + +
+ +
Tue, Jan 09
+ + + + + + + +
+ +
+ + + + + +
+ +
Fri, Jan 12
+ + + + + + + +
+ +
Week 2 + + + +
+ +
Tue, Jan 16
+ + + + + +
+ + Test Adequacy + +
+
+
+ +
+ +
+ + + + + +
+ +
Fri, Jan 19
+ + + + + + + +
+ +
Week 3 + + + +
+ +
Tue, Jan 23
+ + + + + + + +
+ +
+ +
+ + +
Wed, Jan 24
+
+ + + Individual Project Deliverable 1 + +
Due at 11:00 am
+
+ + +
+ +
+ + + +
+ +
Fri, Jan 26
+ + + + + +
+ + Concurrency Patterns + +
+
+
+ +
+ +
Week 4 + + + +
+ +
Tue, Jan 30
+ + + + + + + +
+ +
+ +
+ + +
Wed, Jan 31
+
+ + + Project Formation + +
Due at 11:00 am
+
+ + +
+ +
+ + + +
+ +
Fri, Feb 02
+ + + + + + + +
+ +
Week 5 + + + +
+ +
Tue, Feb 06
+ + + + + +
+ + React Part 2 (Hooks) + +
+
+
+ +
+ +
+ + + + + +
+ +
Fri, Feb 09
+ + + + + + + +
+ +
Week 6 + + + +
+ +
Tue, Feb 13
+ + + + + + + +
+ +
+ +
+ + +
Wed, Feb 14
+
+ + + Preliminary Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ + + +
+ +
Fri, Feb 16
+ + + + + + + +
+ +
Week 7 + + + +
+ +
Tue, Feb 20
+ + + + + +
+ + Work on project (Discuss project plan/revisions) + +
+
+
+ +
+ +
+ +
+ + +
Wed, Feb 21
+
+ + + Individual Project Deliverable 2 + +
Due at 11:00 am
+
+ + +
+ +
+ + + +
+ +
Fri, Feb 23
+ + + + + + + +
+ +
Week 8 + + + +
+ +
Tue, Feb 27
+ + + + + +
+ + Work on project (Discuss project plan/revisions) + +
+
+
+ +
+ +
+ +
+ + +
Wed, Feb 28
+
+ + + Revised Project Plan + +
Due at 11:00 am
+
+ + +
+ +
+ + + +
+ +
Fri, Mar 01
+ + + + + + + + + + +
+ + Midterm Review + +
+
+
+ +
+ +
Week 9 + + + +
+ +
Tue, Mar 05
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
+ + + + + +
+ +
Fri, Mar 08
+ + + + + +
+ + Spring Break (No class) + +
+
+
+ +
+ +
Week 10 + + + +
+ +
Tue, Mar 12
+ + + + + +
+ + Midterm + +
+
+
+ +
+ +
+ + + + + +
+ +
Fri, Mar 15
+ + + + + +
+ + Security + +
+
+
+ +
+ +
Week 11 + + + +
+ +
Tue, Mar 19
+ + + + + +
+ + Work on project + +
+
+
+ +
+ +
+ + + + + +
+ +
Fri, Mar 22
+ + + + + +
+ + Technical Debt + +
+
+
+ +
+ +
Week 12 + + + +
+ +
Tue, Mar 26
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ + + + + +
+ +
Fri, Mar 29
+ + + + + +
+ + Open Source + +
+
+
+ + + + +
+ + Ethics + +
+
+
+ +
+ +
Week 13 + + + +
+ +
Tue, Apr 02
+ + + + + +
+ + Work on Project / Mini-Demo + +
+
+
+ +
+ +
+ + + + + +
+ +
Fri, Apr 05
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
Week 14 + + + +
+ +
Tue, Apr 09
+ + + + + +
+ + Work on Project + +
+
+
+ +
+ +
+ +
+ + +
Wed, Apr 10
+
+ + + Project Final Deliverable + +
Due at 11:00 am
+
+ + +
+ +
+ + + +
+ +
Fri, Apr 12
+ + + + + +
+ + Presentations + +
+
+
+ +
+ +
Week 15 + + + +
+ +
Tue, Apr 16
+ + + + + +
+ + Presentations + +
+
+
+ +
+ +
+ + + + + +
+ + +
+
    + +
+
    + +
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/calendars/git-placeholder.txt b/calendars/git-placeholder.txt new file mode 100644 index 00000000..2d6401c9 --- /dev/null +++ b/calendars/git-placeholder.txt @@ -0,0 +1 @@ +Placeholder file to force git to create a folder here. \ No newline at end of file diff --git a/generateDates.php b/generateDates.php new file mode 100644 index 00000000..09aadaf4 --- /dev/null +++ b/generateDates.php @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +CS4530, Spring 2024 | Fundamentals of Software Engineering + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + CS4530, Spring 2024: Fundamentals of Software Engineering + + +

+ + +
+

+ + + Team Repository Setup Now Available + + +

+ + + + Feb 26 + · + + + 0 min read + +
+

We have posted the instructions for setting up your team repository. You can find them in the start of the CI/CD Pipeline Activity. Please note that setting up the deployment to Heroku requires creating an account and waiting for it to be manually validated by Heroku. We recommend that you start this process as soon as possible to avoid any delays.

+ +
+
+ +

Announcements

+

+ + + Overview + + +

+ +

Building, delivering and maintaining successful software products requires more than being good at programming. Software engineering encompasses the tools and processes that we use to design, construct and maintain programs over time. Software engineering has been said to consider the “multi person development of multi version programs.” Development processes that work well for a single developer do not scale to large or even medium-sized teams. Similarly, development processes that work well for quickly delivering a one-off program to a client cause chaos when applied to a codebase that needs to be maintained and updated over months and years. This class will begin to explore these tradeoffs throughout the entire software development lifecycle, with a particular focus on how these decisions affect the quality of the resulting software.

+ +

This class will serve as an introduction to the field of software engineering, covering key topics such as:

+ +
    +
  • Requirements gathering and specification
    How to make sure that you build the product that your customer really wants
  • +
  • Designing code for reuse, for readability, and for scale
    How to avoid reinventing the wheel? What makes code readable? Where does performance fit into designs? When do we decide when to revisit old design decisions, and how do we replace them? Can we avoid the mistakes that past developers have made?
  • +
  • How to organize your development process to collaborate effectively
    How do we communicate our designs with others? How do we structure and coordinate development activities? How do we measure the performance of these processes, and tweak them over time?
  • +
  • How to ensure that your code works, is secure, and broadly speaking, “does the right thing”
    How do we measure different quality attributes like usability, scalability and performance? How do we minimize the cost of defects? How do we automatically test complex systems? Can we automatically prove the absence of some kinds of defects?
  • +
+

+ + + Course Outcomes + + +

+ + +
    +
  • Students will be able to define and describe the phases of the software engineering lifecycle (requirements, design, implementation, testing, deployment, maintenance)
  • +
  • Students will be able to explain the role of key processes and technologies in modern software development.
  • +
  • Students will be able to productively apply instances of major tools used in elementary SE tasks.
  • +
  • Students will design and implement a portfolio-worthy software engineering project in a small team environment that can be publicly showcased to recruiters.
  • +
+

+ + + Course Delivery + + +

+ +

The course will be delivered in a “traditional” lecture style. Prof Wand’s section will be entirely virtual, and the other sections will be entirely on-the-ground, with no virtual participation option. You must attend the section for which you have registered, and you may not partner with students in other sections for the term project.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SectionInstructorMeeting TimeMeeting Place
1Prof BellMR 11:45 am - 1:25 pmWest Village H 110
2Prof BhuttaMW 2:50 pm - 4:30 pmDodge Hall 173
3Prof BhuttaT 11:45 am - 1:25 pm & R 2:50 pm - 4:30pmSnell Library 031
4Prof WandW 6:00 pm - 9:20 pmOnline
5Prof BhuttaTF 3:25 pm - 5:05 pmRobinson Hall 109
+

+ + + Course Project + + +

+ +

The assignments and project for this class are designed to mirror the experiences of a software engineer joining a new development team: +you will be “onboarded” to our codebase, make several individual contributions, and then form a team to propose, develop and implement a new feature. +The codebase that we’ll be developing on is a remote collaboration tool called Covey.Town. +Covey.Town provides a virtual meeting space where different groups of people can have simultaneous video calls, allowing participants to drift between different conversations, just like in real life. +Covey.Town is inspired by existing products like Gather.Town, Sococo, and Gatherly.IO — but it is an open source effort, and the features will be proposed and implemented by you! +All implementation will take place in the TypeScript programming language, using React for the user interface.

+ +

At the end of the semester, the instructors and TAs will evaluate all of the student projects, and select the best (in terms of usability, code quality, test suite quality, and overall design) to merge into +the open source Covey.Town codebase on GitHub repository. +No additional course credit will be awarded to these teams, but these students will have the opportunity to receive public recognition for their project (in the form of a pull request merged into our repository and acknowledgements in the project’s contributors list).

+ +

The project will provide hands-on experience to complement the skills taught in this class, requiring students to be able to:

+
    +
  • Work effectively in a small team
  • +
  • Enumerate and prioritize development tasks
  • +
  • Propose, design, implement and test a new feature in an existing non-toy software application
  • +
  • Write code that their team members can read and review
  • +
  • Review teammates’ code
  • +
  • Analyze a proposed software architecture
  • +
  • Use relevant software tools, such as: +
      +
    • TypeScript
    • +
    • Visual Studio Code (or similar IDE)
    • +
    • Git
    • +
    • Mocha and Jest
    • +
    • Twilio’s Programmable Video API
    • +
    • Postman
    • +
    +
  • +
+ +

Select projects from Fall 2023 are hosted in our project showcase.

+

+ + + Acknowledgements + + +

+ +

This class and its contents were inspired by Software Engineering courses at various institutions, including:

+ + +

This website is built using Kevin Lin’s Just the Class Jekyll template. The term project, Covey.Town, is built using code from the Clowdr open source project.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/lectures/git-placeholder.txt b/lectures/git-placeholder.txt new file mode 100644 index 00000000..2d6401c9 --- /dev/null +++ b/lectures/git-placeholder.txt @@ -0,0 +1 @@ +Placeholder file to force git to create a folder here. \ No newline at end of file diff --git a/modules/1-requirements-testing.html b/modules/1-requirements-testing.html new file mode 100644 index 00000000..b7ab87c6 --- /dev/null +++ b/modules/1-requirements-testing.html @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1 - Orientation & Requirements | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 1 - Orientation & Requirements + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

In this lecture, you will learn

+ +
    +
  • a little about the scope of Software Engineering and how it differs from programming
  • +
  • how this course will be organized, and what will be expected from you each week.
  • +
  • two different ways to solicit and document our users’ requirements
  • +
+

+ + + Important Dates: + + +

+ + +

+ + + Lecture Slides: + + +

+ + +

+ + + Activity + + +

+ + +

+ + + Tutorials + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/10-ds-1.html b/modules/10-ds-1.html new file mode 100644 index 00000000..69a06839 --- /dev/null +++ b/modules/10-ds-1.html @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 - Distributed Systems Architectures (Part 1) | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 10 - Distributed Systems Architectures (Part 1) + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

In this module, you will learn to:

+
    +
  • Describe 5 key goals of distributed systems
  • +
  • Understand the fundamental constraints of distributed systems
  • +
  • Understand the roles of replication and partitioning in distributing data
  • +
+

+ + + Lecture Slides: + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/11-distributed-systems-2.html b/modules/11-distributed-systems-2.html new file mode 100644 index 00000000..f2b574c8 --- /dev/null +++ b/modules/11-distributed-systems-2.html @@ -0,0 +1,324 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +11 - Distributed Systems (Part 2) | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 11 - Distributed Systems (Part 2) + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

In this module, you will learn to:

+
    +
  • Recognize common software architectures
  • +
  • Understand tradeoffs of scalability, performance, and fault tolerance between these architectures
  • +
  • Explain the basic principles of the REST and WebSocket communication patterns
  • +
+

+ + + Lecture Slides: + + +

+ + +

+ + + Activity + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/12-larger-tests.html b/modules/12-larger-tests.html new file mode 100644 index 00000000..e10bf959 --- /dev/null +++ b/modules/12-larger-tests.html @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +12 - Tests With Larger Scope | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 12 - Tests With Larger Scope + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

In this module, you will learn to:

+
    +
  • Design test cases for code using fakes, mocks and spies
  • +
  • Explain why you might need a test double in your testing
  • +
  • Explain why you might need tests that are larger than unit tests
  • +
  • Explain how large, deployed systems lead to additional testing challenges
  • +
+

+ + + Lecture Slides: + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/13-cloud-infrastructure.html b/modules/13-cloud-infrastructure.html new file mode 100644 index 00000000..11700ab3 --- /dev/null +++ b/modules/13-cloud-infrastructure.html @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +13 - Cloud Infrastructure | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 13 - Cloud Infrastructure + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

In this module, you will learn to:

+
    +
  • Explain what “cloud” computing is and why it is important
  • +
  • Explain why multi-tenancy is important in cloud computing
  • +
  • Describe the difference between virtual machines and containers
  • +
  • Discuss trade-offs that you might consider for self or vendor-managed platforms
  • +
+

+ + + Lecture Slides: + + +

+ + +

+ + + Important Dates: + + +

+ +
    +
  • Revised Project Plan Due Wednesday, Feb 28 by 11am
  • +
+ +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/14-continuous-development.html b/modules/14-continuous-development.html new file mode 100644 index 00000000..28100223 --- /dev/null +++ b/modules/14-continuous-development.html @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +14 - Continuous Development | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 14 - Continuous Development + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

In this module, you will learn to:

+
    +
  • Describe how continuous development helps to catch errors sooner in the software lifecycle
  • +
  • Describe strategies for performing quality-assurance on software as and after it is delivered
  • +
  • Compare and contrast continuous delivery with test driven development as a quality assurance strategy
  • +
+

+ + + Lecture Slides: + + +

+ + +

+ + + Activity + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/2-requirements-to-code.html b/modules/2-requirements-to-code.html new file mode 100644 index 00000000..67d844ef --- /dev/null +++ b/modules/2-requirements-to-code.html @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2 - From Requirements to Code | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 2 - From Requirements to Code + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

In this lecture, you will learn to:

+ +
    +
  • Explain the basics of the Test-Driven Design
  • +
  • Develop simple applications using Typescript and Jest
  • +
  • Learn more about Typescript and Jest from tutorials, blog posts, and documentation
  • +
+

+ + + Important Dates: + + +

+ + +

+ + + Lecture Slides: + + +

+ + +

+ + + Activity + + +

+ + +

+ + + Tutorials + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/3-test-adequacy.html b/modules/3-test-adequacy.html new file mode 100644 index 00000000..715dbdd6 --- /dev/null +++ b/modules/3-test-adequacy.html @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +3 - Test Adequacy | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 3 - Test Adequacy + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

By the end of this lesson, you should be able to:

+
    +
  • Explain different reasons why you might want to test
  • +
  • Design a TDD test suite by identifying equivalence classes of inputs
  • +
  • Explain the following measures of code coverage, and how they differ: +
      +
    • Statement or line coverage
    • +
    • Branch coverage
    • +
    • Path coverage
    • +
    +
  • +
  • Use mutation testing to judge the completeness of a test suite
  • +
+

+ + + Lecture Slides: + + +

+ +
    + +
  • When have I written enough tests? + + Slides PDF, + + PPT +
  • + +
+

+ + + Activity + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/4-code-level-design.html b/modules/4-code-level-design.html new file mode 100644 index 00000000..a7c2837f --- /dev/null +++ b/modules/4-code-level-design.html @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4 - Code-Level Design Principles | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 4 - Code-Level Design Principles + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

This lecture is about writing code that other people will be able to understand, reuse, and modify. We’ve distilled this down to five general program-design princples which should be best practices for effective programmers:

+
    +
  • Use Good Names
  • +
  • Make Your Data Mean Something
  • +
  • One Method/One Job
  • +
  • Don’t Repeat Yourself
  • +
  • Don’t Hardcode Things That Are Likely To Change
  • +
+ +

In this lecture, you will learn to:

+ +
    +
  • Describe the purpose of our best practices for code-level design
  • +
  • List 5 principles for designing readable code, with examples
  • +
  • Identify some violations of the practices and suggest ways to mitigate them
  • +
+

+ + + Important Dates: + + +

+ + +

+ + + Lecture Slides: + + +

+ + +

+ + + Activity + + +

+ +
    +
  • Traffic Light Activity: (Handout)
  • +
+

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/5-interaction-level-design-patterns.html b/modules/5-interaction-level-design-patterns.html new file mode 100644 index 00000000..458a27d5 --- /dev/null +++ b/modules/5-interaction-level-design-patterns.html @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +5 - Interaction-Level Design Patterns | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 5 - Interaction-Level Design Patterns + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

This lecture is about common patterns for organizing the communication between classes. These are what are often called “OO Design Patterns”. We will see that using such patterns makes OO code more readable and modifiable.

+ +

By the end of this lesson, you should be able to

+
    +
  • Explain how patterns capture common solutions and tradeoffs for recurring problems.
  • +
  • Give 3 examples of interaction patterns and describe their distinguishing characteristics
  • +
  • Draw a picture or give an example to illustrate each one
  • +
+

+ + + Important Dates: + + +

+ + +

+ + + Lecture Slides: + + +

+ +
    + +
  • Interaction-Level Design Patterns + + Slides PDF, + + PPT +
  • + +
+

+ + + Activities: + + +

+ + +

+ + + Resources + + +

+ + +
    +
  • Code Examples from Lecture (zip file)
  • +
  • Design Patterns: Elements of Reusable Object-Oriented Software (1994) This book is quite dated, but it is a foundational text for object-oriented design. It is available from many sources. You could look it up.
  • +
  • Freeman & Robson Head First Design Patterns, 2nd Edition This should be available free to you with your student O’Reilly subscription.
  • +
+ +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/6-concurrency-patterns-in-typescript.html b/modules/6-concurrency-patterns-in-typescript.html new file mode 100644 index 00000000..090ddaed --- /dev/null +++ b/modules/6-concurrency-patterns-in-typescript.html @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +6 - Concurrency Patterns in Typescript | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 6 - Concurrency Patterns in Typescript + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

A 1 GHz CPU executes an instruction every 1 ns. Almost anything else takes approximately forever. Rather than waste time waiting for a long-running operation to complete, we want our programs to make progress on other tasks. This is called “masking latency with concurrency”.

+ +

In this lecture, we will study some common patterns for organizing concurrency in Typescript/Javascript.

+ +

By the end of this lesson, you should be prepared to:

+
    +
  • Explain the difference between JS run-to-completion semantics and interrupt-based semantics.
  • +
  • Given a simple program using async/await, work out the order in which the statements in the program will run.
  • +
  • Write simple programs that create and manage promises using async/await
  • +
  • Write simple programs to mask latency with concurrency by using non-blocking IO and Promise.all in TypeScript.
  • +
+

+ + + Important Dates: + + +

+ + +

+ + + Lecture Slides: + + +

+ +
    + +
  • Concurrency Patterns in Typescript + + Slides PDF, + + PPT +
  • + +
+

+ + + Activities: + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/7-software-process.html b/modules/7-software-process.html new file mode 100644 index 00000000..1ad3e6c1 --- /dev/null +++ b/modules/7-software-process.html @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +7 - Software Process | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 7 - Software Process + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

The overall theme of this week’s lessons is software engineering processes: how do we organize our (non-programming) activities so that we can build some software project as efficiently as possible? The lessons will center on these three themes:

+ +
    +
  • How do we organize our development activities?
  • +
  • How do we estimate how long a software project will take?
  • +
  • How do we build an effective team?
  • +
+

+ + + Important Dates: + + +

+ + +

+ + + Lecture Slides: + + +

+ + +

+ + + Resources + + +

+ + +

+ + + Additional Readings: + + +

+ +

The following books provide additional depth on the topics in this week’s lessons:

+ + +

For more reflections on software processes in the 1980’s, David Parnas’ article: “Software Aspects of Strategic Defense Systems”. For a discussion of various xDD approaches, see Rebecca Wirfs-Brock on What Drives Design?. Chuck Rossi’s 2014 Release Engineering Keynote includes some great discussion of Facebook’s mobile release process, now outdated, but quite interesting; there is a discussion of team structure around 18 minutes in. Sarah Mei’s 2018 Power of Agile talk provides a great discussion of embracing agile values that work for your team, while leaving behind toxic practices. Miscellaneous: Investigating the origins of the tire swing meme

+ +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/8-react.html b/modules/8-react.html new file mode 100644 index 00000000..50397b5f --- /dev/null +++ b/modules/8-react.html @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +8 - React | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 8 - React + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

This module will provide an introduction to UI development using React. By the end of this module you should be able to:

+
    +
  • Understand how the React framework binds data (and changes to it) to a UI
  • +
  • Create simple React components that use state and properties
  • +
+

+ + + Lecture Slides: + + +

+ + +

+ + + Activities + + +

+ + +

+ + + Tutorials + + +

+ + +

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/9-patterns-of-react.html b/modules/9-patterns-of-react.html new file mode 100644 index 00000000..9f37b7fe --- /dev/null +++ b/modules/9-patterns-of-react.html @@ -0,0 +1,307 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +9 - React Hooks | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + 9 - React Hooks + + +

+ +
+

+ + + Learning Objectives: + + +

+ +

Building on Module 8, we will have a deeper discussion of common patterns of programming with React. By the end of this module you should be able to:

+ +
    +
  • Explain the basic uses of useEffect
  • +
  • Explain when a useEffect is executed, and when its return value is executed
  • +
  • Construct simple custom hooks and explain why they are useful
  • +
  • Be able to map the three core steps of a test (assemble, act, assess) to UI component testing.
  • +
+

+ + + Lecture Slides: + + +

+ + +

+ + + Activity + + +

+ +
    +
  • No activity this week, sorry
  • +
+

+ + + Resources + + +

+ + + +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/modules/index.html b/modules/index.html new file mode 100644 index 00000000..00038020 --- /dev/null +++ b/modules/index.html @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Modules | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+ + + + +
+

Table of contents

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/policies/index.html b/policies/index.html new file mode 100644 index 00000000..b64a73c9 --- /dev/null +++ b/policies/index.html @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Policies | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Grading + + +

+ +

There will be individual projects (previously known as assignments) and a team project (in a group of 3-4 members). The overall grading breakdown is:

+
    +
  • 30% Individual project (10% for deliverable 1, 20% for deliverable 2)
  • +
  • 40% Team project (including peer evaluations / surveys)
  • +
  • 10% Participation in synchronous, in-class activities
  • +
  • 20% Exam
  • +
+ +

Assignment of team project grades: In cases where team members do not equally contribute to the project, we may assign different grades to different individuals, up to an extreme of deducting 50% of the team project grade for a student (which might arise to 100% deduction for not contributing to the project at all). We will evaluate each individual’s contribution on the basis of a variety of factors, including progress reports at weekly meetings, through inspecting version control history, through each students’ self-reflection, and through each students’ peer evaluation {during and/or} at the end of the project. We will make regular efforts to collect and distribute this feedback throughout the project — our ultimate goal is for all students to participate and receive full marks.

+ +

Assignment of final grades: Although each instructor will use the same lesson plans and assignments, we expect that there may be variation in grades when compared between sections. Hence, each instructor will assign final grades to students in their sections, and reserve the right to apply different curves than are used by the other instructors. With each graded assignment, students will be provided with the distribution of all grades in their section.

+ +

Grading grievances: If you have concerns regarding the grading of your work, please let us know right away by opening a regrade request. All regrade requests must be submitted within 7 days from your receipt of the graded work. If your regrade request is closed and you feel that the response was not satisfactory, you may appeal to the instructor via email within 48 hours.

+

+ + + Homework policy + + +

+ + +

Students must work individually on all homework assignments. We encourage you to have high-level discussions with other students in the class about the assignments, however, we require that when you turn in an assignment, it is only your work. That is, copying any part of another student’s assignment is strictly prohibited, and repercussions for doing so will be severe (up to and including failing the class outright). You are free to reuse small snippets of example code found on the Internet (e.g. via StackOverflow) provided that it is attributed. If you are concerned that by reusing and attributing that copied code it may appear that you didn’t complete the assignment yourself, then please raise a discussion with the instructor.

+ +

Your work is late if it is not turned in by the deadline.

+
    +
  • 10% will be deducted for late assignments turned in within 24 hours after the due date.
  • +
  • Assignments submitted more than 24 hours late will receive a zero.
  • +
+ +

If you’re worried about being busy around the time of a HW submission, please plan ahead and get started early. Homework that does not compile or run will receive at most 50% credit.

+ +

For fairness to all, there are no exceptions to the above rules.

+

+ + + In Class Activities: + + +

+ + +

Most lectures will feature interactive activities and/or polls that support the material being presented. You must be present in class to participate in the activity or take the poll (participating in an online activity while not attending the synchronous session will be considered academic dishonesty and will be treated harshly). Each instructor may have a different style for assigning participation grades, but historical grading information suggests that each style results in a similar overall grade distribution. Participation grades will be posted on Canvas, and regularly updated.

+ +

If you join class in person, you are strongly encouraged to bring your laptop or phone to class so that you can participate in the activities.

+

+ + + Policy on remote attendance + + +

+ + +

If you are registered for an on-the-ground section but do not feel comfortable or are unable to attend in person, please contact your instructor in advance of class. +We would like to provide the best and most comfortable learning experience, and encourage you to stay at home if advised by a medical professional.

+

+ + + Debugging + + +

+ +

One of the objectives of this class is to provide students with experiences writing new code for large, existing codebases. +We anticipate that you may run into difficulties debugging the project code: it is often difficult to build debugging skills until you have a problem in front of you that requires them. +The course staff is happy to help you with debugging, with the specific goal of helping you learn to successfully apply scientific debugging.

+ +

Andreas Zeller’s Debugging Book provides an excellent guide to scientific debugging. +The short version is roughly: if you can’t debug an issue in the first few minutes “just by looking at it”, it will be hard to keep all of the relevant information in your head at once, and a formal process to help you generate and refine guesses for why something is wrong can be immensely useful.

+ +

The key idea is to create a debugging note file, where you track information like:

+
    +
  1. What was the input/application state that caused the bug?
  2. +
  3. What was the behavior that I expected?
  4. +
  5. What was the behavior that I observed?
  6. +
  7. What are possible hypotheses for that behavior?
  8. +
  9. How have I tested those hypotheses, and what was the result?
  10. +
+ +

The overall goal with hypothesis formulation is to come up with possible causes for why the bug exists. +Then, as long as those hypotheses are testable, we can prove or disprove them. +Most hypotheses will be along the lines of “did I make an incorrect assumption about how a library or API works.” +The devil is in enumerating all of the possible incorrect assumptions that you might have made, and testing them. +The best way to attack these kinds of problems is to start with testing some high-level, general assumptions, and then refine them.

+ +

If you come to us for debugging help, we will ask you to answer these 5 questions, as our goal is to help you get better at debugging and not to simply point out bugs that we might have seen before. +We are happy to discuss the problematic behavior that you are observing, possible hypotheses for why that behavior is occurring, and strategies to test those hypotheses. +In the past, students have found that using a variety of strategies to test their hypotheses (e.g. using a debugger, creating a minimized test case, measured application of console.log statements, internet research) are useful, and we would be happy to demonstrate these. +We may not be able to stay with you while you work on refining your hypotheses and fixing the bug, but would be happy to continue following up if you get stuck again.

+

+ + + Classroom Environment + + +

+ + +

To create and preserve a classroom atmosphere that optimizes teaching and learning, all participants share a responsibility in creating a civil and non-disruptive forum for the discussion of ideas. Students are expected to conduct themselves at all times in a manner that does not disrupt teaching or learning. Your comments to others should be constructive and free from harassing statements. You are encouraged to disagree with other students and the instructor, but such disagreements need to respectful and be based upon facts and documentation (rather than prejudices and personalities). The instructors reserve the right to interrupt conversations that deviate from these expectations. Repeated unprofessional or disrespectful conduct may result in a lower grade or more severe consequences. Part of the learning process in this course is respectful engagement of ideas with others.

+ +

Please don’t be late. You are an essential part of the class. Your participation is an essential part of the class. If you are late, please be courteous to others when entering.

+ +

BE PRESENT WHEN YOU ARE ATTENDING CLASS. Please do not distract yourself from the class by doing other activities such as phone calls, email, facebook, chat/IM/texting, games, web surfing – unless it has a direct bearing on the course. Then, by all means, surf away!

+ +

Attendance in the synchronous meetings is expected. Sometimes you cannot avoid missing a class. If you need to be away from class, it is your responsibility to catch up on the materials discussed in the class.

+

+ + + Academic Integrity + + +

+ +

Students must work individually on all homework assignments. +We encourage you to have high-level discussions with other students in the class about the assignments, however, we require that when you turn in an assignment, it is only your work. That is, copying any part of another student’s assignment is strictly prohibited. +If you steal someone else’s work, you fail the class. You are responsible for protecting your work. If someone uses your work, with or without your permission, you fail the class.

+ +

You are free to reuse small snippets of example code found on the Internet (e.g., via StackOverflow) provided that it is attributed. Use of co-pilot is also enrouraged.

+ +

If you are concerned that by reusing and attributing that copied code it may appear that you didn’t complete the assignment yourself, then please raise a discussion with the instructor. If you are in doubt whether using others’ work is allowed, you should assume that it is NOT allowed unless the instructors confirm otherwise.

+

+ + + Accommodations for Disabilities + + +

+ +

Students who have disabilities who wish to receive academic services and/or accommodations should visit the Disability Resource Center at 20 Dodge Hall or call (617) 373-2675. +Please be sure to provide your instructor with DRC’s accommodations letter early in the semester in order to avoid logistical challenges. +This course includes a significant group project, and providing extensions for group work can be complex. +As-per the DRC’s policy, it is the student’s responsibility to coordinate with the instructor in order to provide suitable accommodations.

+

+ + + Title IX Notice + + +

+ +

Title IX of the Education Amendments of 1972 protects individuals from sex or gender-based discrimination, including discrimination based on gender-identity, in educational programs and activities that receive federal financial assistance.

+ +

Northeastern’s Title IX Policy prohibits Prohibited Offenses, which are defined as sexual harassment, sexual assault, relationship or domestic violence, and stalking. The Title IX Policy applies to the entire community, including students, faculty and staff of all gender identities.

+ +

Faculty members are considered “responsible employees” at Northeastern University, meaning they are required to report all allegations of sex or gender-based discrimination to the Title IX Coordinator.

+ +

If you or someone you know has been a survivor of a Prohibited Offense, confidential support and guidance can be found through University Health and Counseling Services staff and the Center for Spiritual Dialogue and Service clergy members. By law, those employees are not required to report allegations of sex or gender-based discrimination to the University.

+ +

Alleged violations can be reported non-confidentially to the Title IX Coordinator within The Office for Gender Equity and Compliance at: titleix@northeastern.edu and/or through NUPD (Emergency 617.373.3333; Non-Emergency 617.373.2121). Reporting Prohibited Offenses to NUPD does NOT commit the victim/affected party to future legal action.

+ +

In case of an emergency, please call 911.

+ +

Please visit www.northeastern.edu/ouec for a complete list of reporting options and resources both on- and off-campus.

+ + + + +
+

Table of contents

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/policies/style/index.html b/policies/style/index.html new file mode 100644 index 00000000..45344c25 --- /dev/null +++ b/policies/style/index.html @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Code Style | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Code Style + + +

+ +

Programs are easier to read and to understand when they are written in a familiar style and follow standard coding conventions. Most organizations that develop software therefore require programmers to write programs that follow the organization’s preferred style and coding conventions. These conventions can be very elaborate.

+ +

Many of the guidelines are, in fact, arbitrary. When you are working on a coding project with other developers, +different developers often have preference for slightly different syntax, for instance: should our string literals +be enclosed by single quotes (') or double quotes (")? Should files be indented with spaces or tabs? +In TypeScript, there are often multiple ways to accomplish the same goal, for instance, you could create a new empty array +with the expression const myArray = new Array(), or const myArray = []. Sometimes there is no difference between +these variants except for how it reads, and other times there may be unintended consequences of making the seemingly correct but +subtly wrong choice.

+ +

Thankfully, it is possible to write automated checkers (and, at times, fixers) for these style violations. +All code that you write for this class will be checked by ESLint, and must be free +of style warnings and errors. Our ESlint rules are derived from Airbnb’s JavaScript Style Guide, +ESLint’s Recommended Rules, TypeScript/ESLint Recommended Rules, +React ESLint Rules and React Hooks ESLint Rules. +We do not suggest studying these lists directly, instead, begin writing code naturally, and allow your IDE’s built-in style checker to +report issues as you find them. The most important rules to get started are:

+
    +
  • Indent using spaces, not tabs; use 2 spaces for each level of indentation
  • +
  • Enclose all string literals with single quotes, not double
  • +
  • Variables must be named using lowerCamelCase, types are named using UpperCamelCase. Constants (read-only variables that are assigned a static value once and not reused) must be named using UPPER_CASE_WITH_UNDERSCORES.
  • +
  • The maximum line length is 100 characters
  • +
+

+ + + Naming + + +

+ +

While the linter can automatically flag names that violate camelCase rules, it can not automatically determine that a name is “good” - this is a subjective judgement call.

+ +

Please consider the following when choosing names:

+
    +
  • Names should be informative (e.g lineTooLong() rather than checkLineLength())
  • +
  • Names for types are typically nouns or noun phrases. Interface names, however, might be adjectives (e.g. Serializable). Class names may be noun phrases that include the interface name (e.g. CuckooClock and DigitalClock for classes that implement the interface Clock).
  • +
  • Use noun-like names for functions or methods that return values (e.g. circleDiameter rather than calculateDiameter). (Exception: simple getters can still have names that begin with get).
  • +
  • Reserve verb-like names for functions that perform actions (e.g. addItem).
  • +
  • Use adjective phrases for predicates when possible (e.g. line.tooLong())
  • +
  • Variable and property names should be descriptive of what the variable is for, and not be named +after the type of the variable (which is captured in the variable’s type declaration)
  • +
  • Names for properties that are private must start with a _
  • +
+

+ + + Documentation and Comments + + +

+ +

All public properties and methods (other than getters, setters, and constructors) must be documented +using JSDoc-style comments +that describes what the property/method does. Example:

+
/** The unique identifier for this player * */
+private readonly _id: string;
+
+ +
/**
+ * A handler to process a remote player's subscription to updates for a room
+ *
+ * @param socket the Socket object that we will use to communicate with the player
+ */
+
+ +

Consider adding comments within your code to describe non-obvious behavior, or to capture why the code is written the +way that it is. Do not add comments that simply restate what can already be immediately discerned by reading the code. +For example, here is a useful comment:

+
// No valid session exists for this token, hence this client's connection should be terminated
+socket.disconnect(true);
+return;
+
+

Here is a useless comment on the same code snippet:

+
// Disconnect the socket
+socket.disconnect(true);
+return;
+
+ +

Comments are for documentation, not for keeping track of old code you are no longer using (do not submit commented-out code).

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/resources/index.html b/resources/index.html new file mode 100644 index 00000000..f92c4d01 --- /dev/null +++ b/resources/index.html @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Other Resources | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Other Resources + + +

+ + +

There are many articles, blogs, books and podcasts that are very interesting for more reading on the topics discussed in class. We’ll update this list as the semester goes, and if you have suggestions of materials to share, please let us know and we’ll add it to the list.

+

+ + + Podcasts + + +

+ +

The Software Engineering Radio podcast (also available wherever you get your podcasts) aims to produce educational material for professional softare developers, and includes conversations between experts and researchers on various software engineering topics. Here are a few of our favorites that are most relevant to topics that we cover in this class:

+ +

+ + + Code style + + +

+ + + +

+ + + Debugging + + +

+ + + +

+ + + Design Patterns + + +

+ + + +

+ + + Infrastructure + Operations + + +

+ + +

+ + + JavaScript + + +

+ + +

+ + + Program Understanding + + +

+ + + + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/staff/index.html b/staff/index.html new file mode 100644 index 00000000..55f514c9 --- /dev/null +++ b/staff/index.html @@ -0,0 +1,804 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Staff | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Staff + + +

+ + +

For technical questions related to Covey.Town, please check piazza first (or post a query there). For discussing the course material or the individual projects, please feel free to attend whichever TA’s office hours are convenient. For project-related questions, please coordinate a time to chat directly with your assigned project mentor.

+

+ + + Office Hours + + +

+ +

All TA office hours will be held virtually. We will explore the option of holding in-person office hours too, subject to interest from students/TAs and availability of a room. Instructor office hours may be held in-person or remotely.

+ +

TA Office Hours are hosted on Microsoft Teams, using the Khoury Office Hours app. Please see the user guide for information on how to log in and ask questions.

+

+ + + Schedule + + +

+ + +

+ + + Instructors + + +

+ + +
+ + + +
+

+ + + + + Jonathan Bell + + + + + +

+ + +

j.bell@northeastern.edu

+ + +

Section: CS4530 Section 1 +

+ + +
Office Hours:
    +
  • Monday and Thursday, 11:00 am - 11:45 am, 350 WVH
  • +
+
+ + + +
+
+ +
+ + + +
+

+ + + + + Adeel Bhutta + + + + + +

+ + +

a.bhutta@northeastern.edu

+ + +

Section: CS4530 Sections 2, 3 & 5 +

+ + +
Office Hours:
    +
  • In-Person: Monday and Thursday 135-230pm, Meserve 337
  • +
  • or by appointment
  • +
+
+ + + +
+
+ +
+ + + +
+

+ + + + + Mitch Wand + + + + + +

+ + +

wand@ccs.neu.edu

+ + +

Section: CS4530 Section 4 +

+ + +
Office Hours:
    +
  • on Zoom, directly following class
  • +
+
+ + + +
+
+

+ + + Teaching Assistants + + +

+ + +
+ + + +
+

+ + + + + Anurag Arasan + + + + + +

+ + +

arasan.a@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Bhupesh Digambar Patil + + + + + +

+ + +

patil.bhu@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Harikrishnan Unnikrishna Pillai + + + + + +

+ + +

unnikrishnapillai.h@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Hongyi Zhang + + + + + +

+ + +

zhang.hongyi@northeastern.edu

+ + + + + +
+
+ +
+ +
+

+ + + + + Irina Park + + + + + +

+ + +

park.yun@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + James Perretta + + + + + +

+ + +

perretta.j@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Kevin (TaeYoon) Jin + + + + + +

+ + +

jin.ta@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Ming-Ho Yee + + + + + +

+ + +

yee.mi@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Muskaan Nandu + + + + + +

+ + +

nandu.mu@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Prajwal Mathad + + + + + +

+ + +

mathad.p@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Rumjhum Singru + + + + + +

+ + +

singru.r@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Saideep Samineni + + + + + +

+ + +

samineni.sa@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Shashank Joshi + + + + + +

+ + +

joshi.shash@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Sindhu Krovvidi + + + + + +

+ + +

krovvidi.si@northeastern.edu

+ + + + + +
+
+ +
+ + + +
+

+ + + + + Sushruth Prasannakumar Konapur + + + + + +

+ + +

konapur.s@northeastern.edu

+ + + + + +
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/textbooks/index.html b/textbooks/index.html new file mode 100644 index 00000000..10f01644 --- /dev/null +++ b/textbooks/index.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Textbooks | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Textbooks + + +

+ + +

This class is a survey of many sub-areas of software engineering, and an unfortunate result of this is that there is no single textbook that is appropriate for this class. However, there are several texts that have chapters that are quite relevant, and most of these books are available for free for Northeastern students through O’Reilly’s Safari Books online. To create your account, first visit this page: https://www.safaribooksonline.com/library/view/temporary-access/ and then select “Not Listed? Click Here” for institution, then enter your @northeastern.edu email address. After this first account creation, you’ll be able to log in directly to Safari Books Online with your @northeastern.edu email and no need for 2FA (hooray!). The schedule will list which chapters of which texts are relevant for each lecture:

+ + + + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/tutorials/assets/week1-getting-started/node/additional-tools.JPG b/tutorials/assets/week1-getting-started/node/additional-tools.JPG new file mode 100644 index 00000000..2b43f8a9 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/additional-tools.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/custom.JPG b/tutorials/assets/week1-getting-started/node/custom.JPG new file mode 100644 index 00000000..9b5d6d70 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/custom.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/destination.JPG b/tutorials/assets/week1-getting-started/node/destination.JPG new file mode 100644 index 00000000..b069b0f9 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/destination.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/download-nvmw.png b/tutorials/assets/week1-getting-started/node/download-nvmw.png new file mode 100644 index 00000000..c2744cb1 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/download-nvmw.png differ diff --git a/tutorials/assets/week1-getting-started/node/download.JPG b/tutorials/assets/week1-getting-started/node/download.JPG new file mode 100644 index 00000000..b9fb8d15 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/download.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/finished.JPG b/tutorials/assets/week1-getting-started/node/finished.JPG new file mode 100644 index 00000000..96a5aa86 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/finished.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/install.JPG b/tutorials/assets/week1-getting-started/node/install.JPG new file mode 100644 index 00000000..d597c040 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/install.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/license-agreement.JPG b/tutorials/assets/week1-getting-started/node/license-agreement.JPG new file mode 100644 index 00000000..c0fbef29 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/license-agreement.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/native.JPG b/tutorials/assets/week1-getting-started/node/native.JPG new file mode 100644 index 00000000..9503d222 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/native.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/new-wizard.JPG b/tutorials/assets/week1-getting-started/node/new-wizard.JPG new file mode 100644 index 00000000..55aa4a32 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/new-wizard.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/nvm_using_fish.png b/tutorials/assets/week1-getting-started/node/nvm_using_fish.png new file mode 100644 index 00000000..2a68f90b Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvm_using_fish.png differ diff --git a/tutorials/assets/week1-getting-started/node/nvmw-destination.JPG b/tutorials/assets/week1-getting-started/node/nvmw-destination.JPG new file mode 100644 index 00000000..e2cf927b Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvmw-destination.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/nvmw-finish.JPG b/tutorials/assets/week1-getting-started/node/nvmw-finish.JPG new file mode 100644 index 00000000..28d7b880 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvmw-finish.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/nvmw-finished.png b/tutorials/assets/week1-getting-started/node/nvmw-finished.png new file mode 100644 index 00000000..a0ed8fd5 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvmw-finished.png differ diff --git a/tutorials/assets/week1-getting-started/node/nvmw-license.JPG b/tutorials/assets/week1-getting-started/node/nvmw-license.JPG new file mode 100644 index 00000000..7fa4638b Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvmw-license.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/nvmw-list-available.png b/tutorials/assets/week1-getting-started/node/nvmw-list-available.png new file mode 100644 index 00000000..6c16b43e Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvmw-list-available.png differ diff --git a/tutorials/assets/week1-getting-started/node/nvmw-symlink.JPG b/tutorials/assets/week1-getting-started/node/nvmw-symlink.JPG new file mode 100644 index 00000000..441278f8 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvmw-symlink.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/nvmw-use-18.png b/tutorials/assets/week1-getting-started/node/nvmw-use-18.png new file mode 100644 index 00000000..06af8e31 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvmw-use-18.png differ diff --git a/tutorials/assets/week1-getting-started/node/nvmw-verification.png b/tutorials/assets/week1-getting-started/node/nvmw-verification.png new file mode 100644 index 00000000..5e5229b2 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/nvmw-verification.png differ diff --git a/tutorials/assets/week1-getting-started/node/powershell.JPG b/tutorials/assets/week1-getting-started/node/powershell.JPG new file mode 100644 index 00000000..779990ab Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/powershell.JPG differ diff --git a/tutorials/assets/week1-getting-started/node/verification.png b/tutorials/assets/week1-getting-started/node/verification.png new file mode 100644 index 00000000..eb4a96d5 Binary files /dev/null and b/tutorials/assets/week1-getting-started/node/verification.png differ diff --git a/tutorials/assets/week1-getting-started/ts/installing-tsc.JPG b/tutorials/assets/week1-getting-started/ts/installing-tsc.JPG new file mode 100644 index 00000000..ad0ea520 Binary files /dev/null and b/tutorials/assets/week1-getting-started/ts/installing-tsc.JPG differ diff --git a/tutorials/assets/week1-getting-started/ts/result.png b/tutorials/assets/week1-getting-started/ts/result.png new file mode 100644 index 00000000..b8be8d03 Binary files /dev/null and b/tutorials/assets/week1-getting-started/ts/result.png differ diff --git a/tutorials/assets/week1-getting-started/vsc/additional-tasks.JPG b/tutorials/assets/week1-getting-started/vsc/additional-tasks.JPG new file mode 100644 index 00000000..f9943f43 Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/additional-tasks.JPG differ diff --git a/tutorials/assets/week1-getting-started/vsc/destination.JPG b/tutorials/assets/week1-getting-started/vsc/destination.JPG new file mode 100644 index 00000000..0ff551cd Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/destination.JPG differ diff --git a/tutorials/assets/week1-getting-started/vsc/download.JPG b/tutorials/assets/week1-getting-started/vsc/download.JPG new file mode 100644 index 00000000..3345ad6d Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/download.JPG differ diff --git a/tutorials/assets/week1-getting-started/vsc/eslint-search.JPG b/tutorials/assets/week1-getting-started/vsc/eslint-search.JPG new file mode 100644 index 00000000..71785627 Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/eslint-search.JPG differ diff --git a/tutorials/assets/week1-getting-started/vsc/finish.JPG b/tutorials/assets/week1-getting-started/vsc/finish.JPG new file mode 100644 index 00000000..22912b34 Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/finish.JPG differ diff --git a/tutorials/assets/week1-getting-started/vsc/license-agreement.JPG b/tutorials/assets/week1-getting-started/vsc/license-agreement.JPG new file mode 100644 index 00000000..a1442639 Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/license-agreement.JPG differ diff --git a/tutorials/assets/week1-getting-started/vsc/ready-to-install.JPG b/tutorials/assets/week1-getting-started/vsc/ready-to-install.JPG new file mode 100644 index 00000000..9b3c7d0a Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/ready-to-install.JPG differ diff --git a/tutorials/assets/week1-getting-started/vsc/start-menu.JPG b/tutorials/assets/week1-getting-started/vsc/start-menu.JPG new file mode 100644 index 00000000..5a7465f8 Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/start-menu.JPG differ diff --git a/tutorials/assets/week1-getting-started/vsc/useful-extensions.JPG b/tutorials/assets/week1-getting-started/vsc/useful-extensions.JPG new file mode 100644 index 00000000..f3e0aeb8 Binary files /dev/null and b/tutorials/assets/week1-getting-started/vsc/useful-extensions.JPG differ diff --git a/tutorials/assets/week1-unit-testing/jest-tutorial-solution.zip b/tutorials/assets/week1-unit-testing/jest-tutorial-solution.zip new file mode 100644 index 00000000..5c112655 Binary files /dev/null and b/tutorials/assets/week1-unit-testing/jest-tutorial-solution.zip differ diff --git a/tutorials/assets/week1-unit-testing/jest-tutorial-starter-code.zip b/tutorials/assets/week1-unit-testing/jest-tutorial-starter-code.zip new file mode 100644 index 00000000..911aa4c6 Binary files /dev/null and b/tutorials/assets/week1-unit-testing/jest-tutorial-starter-code.zip differ diff --git a/tutorials/assets/week1-unit-testing/vscode-jest/allow-browser-vscode.jpg b/tutorials/assets/week1-unit-testing/vscode-jest/allow-browser-vscode.jpg new file mode 100644 index 00000000..c30010df Binary files /dev/null and b/tutorials/assets/week1-unit-testing/vscode-jest/allow-browser-vscode.jpg differ diff --git a/tutorials/assets/week1-unit-testing/vscode-jest/debugging-tests.jpg b/tutorials/assets/week1-unit-testing/vscode-jest/debugging-tests.jpg new file mode 100644 index 00000000..ff971ee8 Binary files /dev/null and b/tutorials/assets/week1-unit-testing/vscode-jest/debugging-tests.jpg differ diff --git a/tutorials/assets/week1-unit-testing/vscode-jest/disable-auto-run.gif b/tutorials/assets/week1-unit-testing/vscode-jest/disable-auto-run.gif new file mode 100644 index 00000000..59256687 Binary files /dev/null and b/tutorials/assets/week1-unit-testing/vscode-jest/disable-auto-run.gif differ diff --git a/tutorials/assets/week1-unit-testing/vscode-jest/manual-jest-runner-start.jpg b/tutorials/assets/week1-unit-testing/vscode-jest/manual-jest-runner-start.jpg new file mode 100644 index 00000000..60741915 Binary files /dev/null and b/tutorials/assets/week1-unit-testing/vscode-jest/manual-jest-runner-start.jpg differ diff --git a/tutorials/assets/week1-unit-testing/vscode-jest/start-debug-test.jpg b/tutorials/assets/week1-unit-testing/vscode-jest/start-debug-test.jpg new file mode 100644 index 00000000..4d0c550b Binary files /dev/null and b/tutorials/assets/week1-unit-testing/vscode-jest/start-debug-test.jpg differ diff --git a/tutorials/assets/week1-unit-testing/vscode-jest/test-overview.jpg b/tutorials/assets/week1-unit-testing/vscode-jest/test-overview.jpg new file mode 100644 index 00000000..53cdada6 Binary files /dev/null and b/tutorials/assets/week1-unit-testing/vscode-jest/test-overview.jpg differ diff --git a/tutorials/assets/week1-unit-testing/vscode-jest/verify-installation.jpg b/tutorials/assets/week1-unit-testing/vscode-jest/verify-installation.jpg new file mode 100644 index 00000000..cf0d0c92 Binary files /dev/null and b/tutorials/assets/week1-unit-testing/vscode-jest/verify-installation.jpg differ diff --git a/tutorials/assets/week1-unit-testing/week1-tutorial-starter-code.zip b/tutorials/assets/week1-unit-testing/week1-tutorial-starter-code.zip new file mode 100644 index 00000000..47c7697f Binary files /dev/null and b/tutorials/assets/week1-unit-testing/week1-tutorial-starter-code.zip differ diff --git a/tutorials/assets/week1-unit-testing/week1-unit-tests.zip b/tutorials/assets/week1-unit-testing/week1-unit-tests.zip new file mode 100644 index 00000000..37920d55 Binary files /dev/null and b/tutorials/assets/week1-unit-testing/week1-unit-tests.zip differ diff --git a/tutorials/assets/week4-react/chakra.png b/tutorials/assets/week4-react/chakra.png new file mode 100644 index 00000000..174b861d Binary files /dev/null and b/tutorials/assets/week4-react/chakra.png differ diff --git a/tutorials/assets/week4-react/start-details.png b/tutorials/assets/week4-react/start-details.png new file mode 100644 index 00000000..d9359e63 Binary files /dev/null and b/tutorials/assets/week4-react/start-details.png differ diff --git a/tutorials/assets/week4-react/start-screen.png b/tutorials/assets/week4-react/start-screen.png new file mode 100644 index 00000000..4af45fb3 Binary files /dev/null and b/tutorials/assets/week4-react/start-screen.png differ diff --git a/tutorials/assets/week4-react/start.png b/tutorials/assets/week4-react/start.png new file mode 100644 index 00000000..5520fb4d Binary files /dev/null and b/tutorials/assets/week4-react/start.png differ diff --git a/tutorials/git-placeholder.txt b/tutorials/git-placeholder.txt new file mode 100644 index 00000000..2d6401c9 --- /dev/null +++ b/tutorials/git-placeholder.txt @@ -0,0 +1 @@ +Placeholder file to force git to create a folder here. \ No newline at end of file diff --git a/tutorials/index.html b/tutorials/index.html new file mode 100644 index 00000000..764814b5 --- /dev/null +++ b/tutorials/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Tutorials | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+ + + + +
+

Table of contents

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/tutorials/week1-getting-started.html b/tutorials/week1-getting-started.html new file mode 100644 index 00000000..bb8cdc63 --- /dev/null +++ b/tutorials/week1-getting-started.html @@ -0,0 +1,618 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Installing a Development Environment | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+ +

This tutorial describes the basic steps needed to set up a development environment with NodeJS, TypeScript, and VisualStudio Code. +At the end of this tutorial, you should have a complete local development environment that you can use to build and test +code for this class.

+ +

Contents:

+ + +

If you run into any difficulties following any of these steps, please post (in a non-private question) on Piazza.

+

+ + + Installing NodeJS + + +

+ + +

Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine.

+ +

For this class, you will need Node.js version 18 (18.19.0 was the latest version at time of writing, although any 18.x or 16.x should work in theory). +There are many ways that you can install Node.js: for instance, you can use a package manager like +snap or homebrew to install it; you can download an installer directly from the Node.js website, and you can, +of course, build it from source. However, due to the complexity of running different Node.js versions on the same machine, we very strongly suggest using nvm, as explained below.

+ +

We recommend installing Node.js using nvm, the node version manager. When language runtimes +are in active development (like Node.js is), sometimes you end up needing to have multiple versions of Node.js installed, +and different projects that you work on might require different versions of Node.js. These annoyances are quite rare, +but when it happens that you need to have mutliple versions of Node.js installed, it’s super handy to have your system set up already +so that installing multiple versions and switching between it is easy. You can use our instructions to set up nvm even if you +have previously installed Node.js.

+

+ + + Installing NodeJS with NVM for Windows + + +

+ + +

Before starting the installation, make sure to kill your Visual Studio Code if you have it installed. To do that on Windows, open a command prompt(type cmd in the windows start bar, then select “Run as administrator”) and run the command taskkill.exe /IM code.exe.

+ +
    +
  1. +

    Download nvm-setup.zip from the most recent release of nvm-windows (at time of writing this document, version was 1.1.12).

    + +
      +
    • image
    • +
    +
  2. +
  3. Extract the contents of nvm-setup.zip and run the executable nvm-setup.exe. +
      +
    • This should open the nvm installation wizard.
    • +
    +
  4. +
  5. Accept the license agreement and click next. Continue to accept the default choices for any remaining prompts, and click “install”. If you receive messages along the lines of “NodeJS version XYZ is already installed, would you like nvm to control this installation,” select “Yes”.
  6. +
  7. Upon completion, you will see the below window +
      +
    • image
    • +
    +
  8. +
  9. Open a command prompt with administrative privileges (type cmd in the windows start bar, then select “Run as administrator”).
  10. +
  11. Verify the installation, run the command nvm version +
      +
    • This should display the version of nvm installed.
    • +
    • image
    • +
    +
  12. +
  13. Run the command nvm list available to display all available NodeJS versions. +
      +
    • image
    • +
    +
  14. +
  15. Install Node.js version 18 using the command nvm install 18.
  16. +
  17. To use this version of NodeJS, run the command nvm use 18. +
      +
    • image
    • +
    +
  18. +
+ + + +

Troubleshooting with VSCode: Did you follow these instructions successfully, but find a “Command not found” error when you try to run npm in VSCode? Try this: Close VSCode completely. Re-open it. In your command shell in VSCode, try again. We have noticed that if you have VSCode open while installing nvm, it is possible that VSCode will not see the new software installation until it’s closed and re-opened. You can also confirm that VSCode correctly sees the NodeJS installation by running echo %PATH% in your windows command shell in VSCode: it should include an entry similar to C:\Program Files\nodejs.

+

+ + + Installation Steps (Linux / Mac) + + +

+ + +
    +
  1. Run either curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash or If wget is installed then run wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash.
  2. +
  3. Close and reopen the active terminal. +
    +

    Note: You can also restart your terminal by running source ~/.bashrc or source ~/.zshrc depending on your shell.

    +
    +
  4. +
  5. Verify nvm is working by entering command -v nvm. If your terminal prints +out nvm, it should be working. If you see nvm: command not found or no +feedback, open a new terminal and trying again or restart from step 1.
  6. +
  7. Install the required version of Node.js by typing nvm install 18. +
    +

    Note: While the LTS of node is 20, we are using 18 for this class (and has been tested on node version 18).

    +
    +
  8. +
+

+ + + Installing NVM when using the fish terminal (Linux / Mac) + + +

+ +
+

Note: When using the Fish terminal, nvm.fish should be used to install nvm. +This is not a fish wrapper for nvm rather it is written from scratch using fish. +Current version of NVM is not supported on terminals that are running fish or have installed fish in the past.

+
+ +
    +
  1. Install nvm.fish using Fisher: fisher install jorgebucaran/nvm.fish
  2. +
  3. Restart fish by entering fish in your active terminal
  4. +
  5. Verify nvm is working by entering nvm -v
  6. +
  7. Install the required version of Node.js by typing nvm install 18.
  8. +
+ +

Working with MacOS M1 Silicon: If you have macOS on M1 Silicon 2022, you can find instructions here.

+

+ + + Verification + + +

+ + +
    +
  1. Open a shell (for Linux / Mac) or windows powershell/command prompt (for Windows).
  2. +
  3. Run the command node -v +
      +
    • This should print the current version of nodeJS installed (v18.x.x).
    • +
    +
  4. +
  5. Run the command npm -v +
      +
    • This should print the current version of npm installed (v10.x.x). +image +image
    • +
    +
  6. +
  7. If you find that some other version is being used, run the command nvm use 18. For Linux / Mac, you can change the default to 18 by running the command nvm alias default 18.
  8. +
+ +
+

Note: The alias command is not compatible with nvm.fish. Instead, you can +add a default version with the command exec nvm use 18 in your ~/.config/fish/config.fish file.

+
+

+ + + Installing Visual Studio Code (VSCode) + + +

+ + +

Visual Studio Code is a lightweight but powerful source code editor which runs on your desktop and is available for Windows, macOS and Linux. It comes with built-in support for JavaScript, TypeScript and Node.js and has a rich ecosystem of extensions for other languages (such as C++, C#, Java, Python, PHP, Go) and runtimes (such as .NET and Unity). VSCode also supports importing hotkey configurations from most other text editors and IDEs. Read more here. +While you are required to use an IDE for this class, it is not mandatory to use VSCode: if you already are comfortable developing TypeScript or JavaScript +in another suitable IDE (like IntelliJ), then you are welcome to continue to use that. However, VSCode is the +“supported” option: if you struggle to get things like the linter set up correctly in VSCode, we will be happy to help you. +However, we can’t provide such support for all IDEs.

+

+ + + Pre-requisites + + +

+ + +
    +
  • Administrative access on the host machine for software installation.
  • +
+

+ + + Installation Steps (Windows / Mac) + + +

+ + +
    +
  1. Download the VSCode installer from the VSCode website. +
      +
    • image
    • +
    +
  2. +
  3. Run the installer to start the installation wizard for VSCode +
      +
    • image
    • +
    +
  4. +
  5. Accept the license agreement and click next. +
      +
    • image
    • +
    +
  6. +
  7. Choose the installation directory of your choice and click next. +
      +
    • image
    • +
    +
  8. +
  9. Click next on the select start menu folder screen. +
      +
    • image
    • +
    +
  10. +
  11. Tick all check boxes on the select additional tasks menu and click next. +
      +
    • image
    • +
    +
  12. +
  13. Review the settings on the the “Ready to install” screen and click Install to begin installation.
  14. +
  15. Once the installation is complete, you will see the below screen and you are ready to use VSCode. +
      +
    • image
    • +
    +
  16. +
+

+ + + Installation (Linux) + + +

+ + +
    +
  • The above instructions for Windows/Mac should also work, but VSCode is also provided as a snap package
  • +
+ +
    +
  1. Install snap if you haven’t already by running sudo apt update and then sudo apt install snapd.
  2. +
  3. Install VSCode by running sudo snap install --classic code.
  4. +
  5. Open VSCode as an application on your desktop.
  6. +
+

+ + + Additional Extensions (Optional) + + +

+ + +

VSCode comes with a rich set of extensions to aid in software development. +Below is a list of extensions that you may find useful.

+ +
    +
  1. GitLens - Git Supercharged
  2. +
  3. Prettier - code formatter
  4. +
  5. vscode-icons
  6. +
  7. ESLint
  8. +
  9. Andromeda (Color theme: Andromeda Colorizer)
  10. +
  11. Jest Runner (highly recommended)
  12. +
  13. GitHub Copilot and GitHub Copilot Chat
  14. +
+ +

image

+

+ + + Installing Extensions (eg. ESLint) + + +

+ + +
    +
  1. Click on the extensions tab on the left in VSCode.
  2. +
  3. Search ESLint.
  4. +
  5. Click on Install.
  6. +
  7. Done! +
      +
    • image
    • +
    +
  8. +
+

+ + + Additional Settings (Optional) + + +

+ + +

You may find it useful to add some settings:

+ +
    +
  1. Turn on “bracket pair colorization” (in File > Preferences > Settings)
  2. +
  3. Add a keybinding for formatting the current selection (in File > Preferences > Keyboard Shortcuts > Format Selection )
  4. +
+

+ + + Getting Started With Typescript + + +

+ + +

Typescript is a superscript of JavaScript which adds type information and other features.

+

+ + + Pre-requisites + + +

+ + +
    +
  • NodeJS
  • +
  • VSCode (recommended but not required)
  • +
+ + +

+ + + Hello World + + +

+ + +
    +
  1. Create a new directory and open it with VSCode.
  2. +
  3. Create a new file called hello-world.ts. +
      +
    • You can do this from within VSC by typing ctrl + N, but this is not necessary.
    • +
    +
  4. +
  5. Add the following code to the file: +
     console.log('Hello, World!');
    +
    +
  6. +
  7. Open the terminal with ctrl + ~ or ctrl + ‘`’ (ctrl-backtick) +
      +
    • Ensure that you are in the same directory as hello-world.ts.
    • +
    +
  8. +
  9. Install typescript by running the command npm install --save typescript . +
      +
    • This will install Typescript locally in the current directory.
    • +
    +
  10. +
  11. Run the command npx ts-node hello-world.ts. +
      +
    • If you are prompted, enter y.
    • +
    • This will give the result below.
    • +
    • image
    • +
    +
  12. +
+ + + +

Troubleshooting with VSCode: Did you follow these instructions successfully, but find a “Command not found” error when you try to run npm in VSCode? Try this: Close VSCode completely. Re-open it. In your command shell in VSCode, try again. We have noticed that if you have VSCode open while installing nvm, it is possible that VSCode will not see the new software installation until it’s closed and re-opened. You can also confirm that VSCode correctly sees the NodeJS installation by running echo %PATH% in your windows command shell in VSCode: it should include an entry similar to C:\Program Files\nodejs.

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/tutorials/week1-typescript-basics.html b/tutorials/week1-typescript-basics.html new file mode 100644 index 00000000..5248c546 --- /dev/null +++ b/tutorials/week1-typescript-basics.html @@ -0,0 +1,1613 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Typescript Basics | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + Typescript Basics + + +

+ + +

This tutorial describes the basic concepts and syntax of typescript with code examples. Typescript is treated here as an extension of JavaScript, but it requires no prior knowledge to follow along.

+ +

You can run the examples on Online Typescript Editor .

+ +

Contents:

+ + + +

Typescript is a superset of JavaScript. Thus, all JavaScript code is valid Typescript code. Typescript introduces concepts of optional typing, modules, and few additional features to Javascript. Let us begin by extending JavaScript to use the features of Typescript and primitive types.

+

+ + + Types in typescript + + +

+ +

+ + + Boolean + + +

+ + +

The boolean type has two values: true and false.

+ +

Example:

+
let a = true                // boolean
+     var b = false               // boolean
+     const c = true              // true
+     let d: boolean = true       // boolean
+     let e: true = true          // true
+
+
    +
  • Usage: +You can compare them (with ==, ===, ||, &&, and ?), negate them (with !).
  • +
+

+ + + Number + + +

+ + +

Number is the set of all numbers: integers, floats, positives, negatives, Infinity, NaN.

+ +

Example:

+
var b = Infinity * 0.10     // number
+const c = 5678              // 5678
+let d = a < b               // boolean
+let e: number = 100         // number
+let f: 26.218 = 26.218
+
+ +
    +
  • Usage: +Numbers can do things, like addition (+), subtraction (-), modulo (%), and comparison (<). When working with long numbers, use numeric separators to make those numbers easier to read.
  • +
+

+ + + BigInt + + +

+ + +

The bigint type is the set of all BigInts, and supports things like addition (+), subtraction (-), multiplication (*), division (/), and comparison (<).

+ +

Example:

+
let a = 1234n               // bigint
+const b = 5678n             // 5678n
+var c = a + b               // bigint
+let d = a < 1235            // boolean
+let e = 88.5n               // Error TS1353: A bigint literal must be an integer.
+let f: bigint = 100n        // bigint
+let g: 100n = 100n
+
+ +
    +
  • Usage: +It lets you work with large integers without running into rounding errors. While the number type can only represent whole numbers it can hold numbers larger than 253 – 1.
  • +
+

+ + + String + + +

+ + +

String is the set of all strings and the things you can do with them like concatenate (+), slice (.slice),etc.

+ +

Example:

+
let a: string = 'hello'           // any
+let b: string = 'world'           // any
+let c: string = a + ' ' + b       // any
+
+ +
    +
  • Usage: +Any functionality that requires the functionality of a string of characters. Here are a couple of string operations:
  • +
+ +

charAt() : This method returns the character from the specified index. Characters in a string are indexed from left to right. The index of the first character is 0, and the index of the last character in a string, called stringName, is stringName.length – 1.

+ +

Syntax:

+
string.charAt(index)
+
+

slice() : This method extracts a section of a string and returns a new string. +Syntax:

+
string.slice( beginslice [, endSlice])
+
+

split() : This method splits a String object into an array of strings by separating the string into substrings. +Syntax:

+
string.split([separator][, limit])
+
+

concat(): This method adds two or more strings and returns a new single string. +Syntax:

+
string.concat(string2, string3[, ..., stringN])
+
+

indexOf(): This method returns the index within the calling String object of the first occurrence of the specified value, starting the search at index or -1 if the value is not found. +Syntax:

+
string.indexOf(searchValue[, fromIndex])
+
+

+ + + Arrays + + +

+ + +

Like in JavaScript, TypeScript arrays are special kinds of objects that support things like concatenation, pushing, searching, and slicing. Arrays are iterable Objects with the keys as numbers.

+ +

Example:

+
let a = [1, 2, 3]           // number[]
+var b = ['a', 'b']          // string[]
+let c: string[] = ['a']     // string[]
+let d = [1, 'a']            // (string | number)[]
+const e = [2, 'b']          // (string | number)[]
+ 
+let f = ['red']
+f.push('blue')
+let g = []                  // any[]
+g.push(1)                   // number[]
+g.push('red')               // (string | number)[]
+ 
+let h: number[] = []        // number[]
+h.push(1)                   // number[]
+
+

+ + + Tuples + + +

+ + +

Tuples are subtypes of array. They’re a special way to type arrays that have fixed lengths, where the values at each index have specific, known types. Unlike most other types, tuples have to be explicitly typed when you declare them.

+ +

Example:

+
let a: [number] = [1]
+ 
+// A tuple of [first name, last name, birth year]
+let b: [string, string, number] = ['malcolm', 'gladwell', 1963]
+
+ +
    +
  • Usage: +In TypeScript Tuples support optional elements too. Just like in object types, ? means “optional”. Tuples also support rest elements, which you can use to type tuples with minimum lengths. Not only do tuple types safely encode heterogeneous lists, but they also capture the length of the list they type.
  • +
+

+ + + Enums + + +

+ + +

Enums are a way to enumerate the possible values for a type. They are unordered data structures that map keys to values.

+ +

Example:

+
enum Language {
+ English,
+ Spanish,
+ Russian
+}
+
+
    +
  • Usage: +There are two kinds of enums: enums that map from strings to strings, and enums that map from strings to numbers. Use it when you wish to provide flexibility making it easier to express and document intentions and use cases or when you want to save compile-time and runtime with inline code.
  • +
+

+ + + Any + + +

+ + +

The any data type is the super type of all types in TypeScript. It denotes a dynamic type. Using the any type is equivalent to opting out of type checking for a variable.

+ +

Example:

+
let a: any = 666            // any
+let b: any = ['danger']     // any
+let c = a + b               // any
+
+
    +
  • Usage: +In TypeScript everything needs to have a type at compile time, and any is the default type when you (the programmer) and TypeScript (the typechecker) can’t figure out what type something is. It’s a last resort type, and you should avoid it when possible.
  • +
+

+ + + Unknown + + +

+ + +

If any is the Godfather, then unknown is Keanu Reeves as undercover FBI agent Johnny Utah in Point Break: laid back, fits right in with the bad guys, but deep down has a respect for the law and is on the side of the good guys. You can compare unknown values (with ==, ===, &&, and ?), and refine them (like you can any other type) with JavaScript’s typeof and instanceof operators.

+ +

Example:

+
let a: unknown = 30         // unknown
+let b = a === 123           // boolean
+
+
    +
  • Usage: +For the few cases where you have a value whose type you really don’t know ahead of time, don’t use any, and instead reach for unknown. Like any, it represents any value, but TypeScript won’t let you use an unknown type until you refine it by checking what it is.
  • +
+

+ + + Variable declaration in typescript + + +

+ + +

Variables can be declared in Typescript using one of 3 keywords:

+ +
    +
  • var: var declarations are accessible anywhere within their containing function, module, namespace, or global scope - all which we’ll go over later on - regardless of the containing block. Some people call this var-scoping or function-scoping. Parameters are also function scoped.
  • +
  • let: When a variable is declared using let, it uses what some call lexical-scoping or block-scoping. Unlike variables declared with var whose scopes leak out to their containing function, block-scoped variables are not visible outside of their nearest containing block.
  • +
  • const: They are like let declarations but, as their name implies, their value cannot be changed once they are bound. In other words, they have the same scoping rules as let, but you can’t re-assign to them.
  • +
+ +

The syntax of declarations is as below:

+
var <name>: <type> = <value>;
+let <name>: <type> = <value>;
+const <name>: <type> = <value>;
+
+ +

Example:

+
let num: number = 1;
+const PI: number = 3.14;
+let x: string = "This is a string";
+const t: boolean = true;
+const f: boolean = false;
+let uninitialized: any;
+
+

+ + + Objects + + +

+ + +
    +
  • Objects are similar to dictionaries with key-value pairs.
  • +
  • Arrays are iterable Objects with the keys as numbers.
  • +
+ +

Examples:

+ +
const myObj: any = {
+    key1: 'value1',
+    key2: 'value2'
+};
+
+console.log(myObj.key1); // prints 'value1'
+
+

+ + + Control Flow Statements + + +

+ + +

Typescript contains the following control flow statements:

+ +
    +
  • If-Else
  • +
  • Switch
  • +
  • Ternary operator: Shorthand for if-else which returns a value.
  • +
+ +

If condition syntax:

+ +
if(condition) {
+    // executed when condition is true
+}
+
+if(condition) {
+    // executed when condition is true
+} else {
+    // executed when condition is false
+}
+
+if(condition) {
+    // executed when condition is true
+} else if(condition2) { // checked only if condition is false
+    // executed when condition2 is true
+} else {
+    // executed when all conditions in the if-elseif ladder are false.
+}
+
+ +

Switch case syntax:

+
switch (variable) {
+
+    case <case1>:
+        // executed when value of variable matches <case1>
+        break; // Break is required to prevent all subsequent cases from executing
+    case <case2>:
+        // executed when value of variable matches <case1>
+        break; // Break is required to prevent all subsequent cases from executing
+    default:
+        // executed if variable does not match any prior cases
+}
+
+ +

Ternary Syntax:

+ +
let x = (condition) ? /* Executed when condition is true */ : /*Executed when condition is false*/;
+
+ +

Examples:

+
const str: string = "ABCD";
+
+if(str === "ABCD") {
+    console.log("it was true");
+} else {
+    console.log("it was false");
+}
+
+switch (str) {
+    case "ABCD":
+        console.log('It was ABCD');
+        break;
+    case "WXYZ":
+        console.log('It was WXYZ');
+        break;
+    default:
+        console.log('It was something completely different')
+}
+
+let y: string = (str.includes("A")) ? "The string contains A" : "The string does not contain A";
+// y now contains "The string contains A"
+
+

+ + + Equality vs Strict Equality + + +

+ + +

Typescript/JavaScript contain 2 equality operators:

+ +
    +
  • ==: Compares only the value of entities being compared.
  • +
  • ===: Compares the type and value of entities being compared.
  • +
+ +

Example:

+
if(0 == '0') { } // Evaluates to true despite comparing string to number.
+
+if(0 === '0') { } // Evaluated to false because types are different.
+
+ +

We recommend using strict equality (===) in all cases, and this recommendation is enforced by our linter.

+

+ + + Loops + + +

+ + +

Typescript contains the following loops:

+ +
    +
  • For
  • +
  • While
  • +
  • Do-while
  • +
+ +

Examples:

+
for(let i: number = 0; i < 10; i++) {
+
+}
+
+while(condition) {
+
+}
+
+do {
+
+} while(condition)
+
+

+ + + Array Functions + + +

+ + +

ForEach, Map, reduce, and filter are all array methods in JavaScript. Each one will iterate over an array and perform a transformation or computation. Each will return a new array based on the result of the function.

+

+ + + ForEach + + +

+ + +

forEach() method calls a function for each element in the array. It returns the resultant array.

+ +

Syntax

+
array.forEach(callback[, thisObject]);
+
+ +

Example

+
let num = [7, 8, 9];
+num.forEach(function (value) {
+  console.log(value);
+}); 
+
+

+ + + Map + + +

+ + +

It is an array function that transforms the array according to the applied function and returns the updated array. It works on each element of an array.

+ +

Syntax

+
array.map(callback[,object])
+
+

callback - It is a function that provides an element of the new Array from an element of the current one.

+ +

object - object to use as this when executing callback.

+ +

Return Type - List

+ +

Examples

+
//Calculate cube of each element with the help of map. 
+function cube(n){  
+   return n*n*n;  
+}  
+var arr=new Array(1,2,3,4)  
+var newArr=arr.map(cube);  
+console.log(newArr)  // Output : [1,8,27,64]
+
+

In the above example, a function called “cube” is created and then is passed as a callback function into map().

+

+ + + Reduce + + +

+ +

It also works on a callback for each element of an array. It reduces the result of this callback function from one array element to the other.

+ +

Syntax

+
array.reduce(callback[,initalValue])
+
+

callback - this parameter is the function to execute on each value in the array.

+ +

intitalValue - this parameter is the object to use as the first argument of the first call of the callback.

+ +

Examples

+
//To calculate product of every element of an array,
+var arr = new Array (1,2,3,4,5)    
+var val = arr.reduce(function(a,b){    
+   return a*b;    
+});      
+//output => 120  
+
+

Calculate the total salary from a list of object, then you will use the reduce() like,

+ +
var employees = [    
+   { id: 20, name: 'Ajay', salary:30000 },    
+   { id: 24, name: 'Vijay', salary:35000 },    
+   { id: 56, name: 'Rahul', salary:32000 },    
+   { id: 88, name: 'Raman', salary:38000 }    
+];    
+var totalSalary= employees .reduce(function (total, record) {    
+   return total + record.salary;    
+}, 0);    
+    
+//It will return the total salary of all the employees.  
+
+

+ + + Filter + + +

+ +

As the name suggests it can filter out the data/array elements on the basis of condition and return the result as a list. Basically, this function pushes the current element into a new array when the callback functions return true.

+ +

Syntax

+
array.filter(callback[, object])
+
+

callback - it is a function that provides an element of the new Array from an element of the current one. +object - object to use as this when executing callback.

+ +

Return Type: List

+ +

Examples

+
//Calculate a list of even elements from an array :    
+arr = new Array(1, 2, 3, 6, 5, 4)  
+var newArr = arr.filter(function(record) {  
+    return record % 2 == 0;  
+}); // output => [2,6,4] 
+
+

+ + + Functions + + +

+ +
    +
  • +

    Functions usually “take in” data, process it, and “return” a result. Remember one function/method one job!

    +
  • +
  • +

    There is a lot you can do with functions in JavaScript like assign them to variables, pass them to other functions, return them from functions, assign them to objects and prototypes, write properties to them, read those properties back, and so on, and TypeScript models all of those things with its rich type system, which we just learnt.

    +
  • +
  • +

    Typescript enables us to set the types for our function parameters and our return type.

    +
  • +
  • +

    Functions in Typescript are defined as below:

    +
    function functionName(argument1: <type>, defaultArgument: <type> = value, optionalArgument?: <type>): <return type> {
    +// Function body
    +}
    +
    +
  • +
+

+ + + Typing the function + + +

+ + +

A simple function in javascript would look like this:

+
// Named function
+function add(a, b) {
+  return a + b;
+}
+
+

Now, let’s add types to our simple examples above:

+ +
function add(a: number, b: number): number {
+  return a + b;
+}
+
+ +

You will usually explicitly annotate function parameters (a and b in this example)—TypeScript will always infer types throughout the body of your function, but in most cases it won’t infer types for your parameters. The return type is inferred, but it’s a good practice to explicitly annotate it.

+

+ + + Invoking the function + + +

+ + +

When you invoke a function in TypeScript, you don’t need to provide any additional type information—just pass in some arguments, and TypeScript will go to work checking that your arguments are compatible with the types of your function’s parameters:

+ +
add(1, 2);         // evaluates to 3
+
+

Of course, if you forgot an argument, or passed an argument of the wrong type, TypeScript will be quick to point it out:

+ +
add(1);            // Error TS2554: Expected 2 arguments, but got 1.
+add(1, 'a');       // Error TS2345: Argument of type '"a"' is not assignable
+                  // to parameter of type 'number'.
+
+

+ + + Optional and Default Parameters + + +

+ + +

Like in object and tuple types, you can use ? to mark parameters as optional. When declaring your function’s parameters, required parameters have to come first, followed by optional parameters:

+
function log(message: string, userId?: string) {
+  let time = new Date().toLocaleTimeString()
+  console.log(time, message, userId || 'Not signed in')
+}
+ 
+log('Page loaded') // Logs "12:38:31 PM Page loaded Not signed in"
+log('User signed in', 'da763be') // Logs "12:38:31 PM User signed in da763be"
+
+

You can provide default values for optional parameters. Semantically it’s similar to making a parameter optional, in that callers no longer have to pass it in (a difference is that default parameters don’t have to be at the end of your list of parameters, while optional parameters do).

+ +

For example, we can rewrite log as:

+
function log(message: string, userId = 'Not signed in') {
+  let time = new Date().toISOString()
+  console.log(time, message, userId)
+}
+
+log('User clicked on a button', 'da763be')
+log('User signed out')
+
+

+ + + Rest Parameters + + +

+ + +

If a function takes a list of arguments, you can of course simply pass the list in as an array:

+ +
function sum(numbers: number[]): number {
+  return numbers.reduce((total, n) => total + n, 0);
+}
+ 
+sum([1, 2, 3]); // evaluates to 6
+
+

+ + + Functions as Constructors + + +

+ + +

In Typescript/Javascript, Functions can be used as constructors for creating objects. This is similar to classes, but not quite the same.

+ +

Examples:

+
function Person(firstName: string, lastName: string): void {
+    this.firstName = firstName;
+    this.lastName = lastName;
+    this.getFullName = function(): string {
+        return this.firstName + ' ' + this.lastName;
+    }
+}
+const person1 = new Person('first', 'last');
+console.log(person1.getFullName()); // Returns 'first last'.
+
+
    +
  • +

    Constructors can’t have type parameters - these belong on the outer class declaration

    +
  • +
  • +

    Constructors can’t have return type annotations - the class instance type is always what’s returned +```ts +class Point { + x: number; + y: number;

    +
  • +
+ +

// Normal signature with defaults + constructor(x = 0, y = 0) { + this.x = x; + this.y = y; + } +}

+

+## Arrow Functions
+
+Arrow Functions (also called fat arrow functions) are functions which have lexical 'this' and 'arguments'. This are especially useful in class methods to preserve the context when using higher order functions.
+
+Examples:
+```ts
+let sum = (x: number, y: number): number => {
+    return x + y;
+}
+
+sum(10, 20); //returns 30
+
+

In the above example, sum is an arrow function. (x:number, y:number) denotes the parameter types, :number specifies the return type. The fat arrow => separates the function parameters and the function body. The right side of => can contain one or more code statements.

+

+ + + Function Overloads + + +

+ + +

In TypeScript, we can specify a function that can be called in different ways by writing overload signatures. To do this, write some number of function signatures (usually two or more), followed by the body of the function:

+
//function makeDate() with one parameter
+function makeDate(timestamp: number): Date;
+//function makeDate() with three parameters 
+function makeDate(m: number, d: number, y: number): Date;
+//function makeDate() with one parameter and 2 default parameters
+function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
+  if (d !== undefined && y !== undefined) {
+    return new Date(y, mOrTimestamp, d);
+  } else {
+    return new Date(mOrTimestamp);
+  }
+}
+const d1 = makeDate(12345678);
+const d2 = makeDate(5, 5, 5);
+const d3 = makeDate(1, 3); //No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
+
+

+ + + Classes + + +

+ + +

Classes are blueprints for creating objects.

+ +
    +
  • They can contain properties, methods, and a constructor.
  • +
  • All members of a class can have an access modifier: public, protected, private.
  • +
  • Members can also be static (shared across all instances of the class) and final (immutable).
  • +
  • Class properties may have getters and setters.
  • +
  • Classes can extend other classes.
  • +
  • Classes can implement interfaces.
  • +
+

+ + + Creating a Class + + +

+ + +

A class definition can include the following:

+ +
    +
  • Fields − A field is any variable declared in a class. Fields represent data pertaining to objects
  • +
  • Constructors − Responsible for allocating memory for the objects of the class
  • +
  • Functions − Functions represent actions an object can take. They are also at times referred to as methods
  • +
+ +

Examples:

+
class Person {
+
+    private firstName: string = '';
+    protected middleName: string;
+    public lastName: string = '';
+
+    private static final NeverGonnaGiveYouUp: any;
+    protected static final NeverGonnaLetYouDown: any;
+    public static final isRickRolled: boolean = true;
+
+    constructor() {
+        // I execute when you call new Person().
+        // No access modifier === public by default.
+        // Make me private if implementing a singleton.
+    }
+
+    public anyoneCanCallMe(): void {
+        this.childClassesCanCallMe();
+    }
+
+    protected childClassesCanCallMe(): void {
+        this.onlyPersonCanCallMe();
+    }
+    
+    private onlyAccessibleInsidePerson(): void {
+        // I lied, anyone can call me if you know how.
+        // Welcome to JavaScript :p
+    }
+
+    public get firstName(): string {
+        return this.firstName;
+    }
+
+    public set firstName(firstName: string): void {
+        this.firstName = firstName;
+    }
+
+}
+
+const person = Person();
+person.firstName = 'first';
+console.log(person.firstName);
+person.anyoneCanCallMe();
+
+class SpecialPerson extends Person {
+    // I contain everything person has, and can extend/override it.
+
+    constructor() {
+        super() // I call the constructor for Person.
+    }
+
+}
+
+

+ + + Creating an abstract class + + +

+ + +
    +
  • +

    Define an abstract class in Typescript using the abstract keyword. Abstract classes are mainly for inheritance where other classes may derive from them. We cannot create an instance of an abstract class.

    +
  • +
  • +

    An abstract class typically includes one or more abstract methods or property declarations. The class which extends the abstract class must define all the abstract methods.

    +
  • +
+ +

Example:

+
abstract class Person {
+ abstract name: string;
+ display(): void{
+     console.log(this.name);
+ }
+}
+class Employee extends Person {
+ name: string;
+ empCode: number;
+  constructor(name: string, code: number) {
+     super(); // must call super()
+     this.empCode = code;
+     this.name = name;
+ }
+}
+let emp: Person = new Employee("James", 100);
+emp.display(); //James
+
+

+ + + Type Aliases + + +

+ + +

We’ve been using object types and union types by writing them directly in type annotations. This is convenient, but it’s common to want to use the same type more than once and refer to it by a single name. +A type alias is exactly that - a name for any type. The syntax for a type alias is:

+ +
type Point = {
+  x: number;
+  y: number;
+};
+ 
+function printCoord(pt: Point) {
+  console.log("The coordinate's x value is " + pt.x);
+  console.log("The coordinate's y value is " + pt.y);
+}
+ 
+printCoord({ x: 100, y: 100 });
+
+ +

You can actually use a type alias to give a name to any type at all, not just an object type. For example, a type alias can name a union type:

+
type ID = number | string;
+
+

Note that aliases are only aliases - you cannot use type aliases to create different/distinct “versions” of the same type. When you use the alias, it’s exactly as if you had written the aliased type. In other words, this code might look illegal, but is OK according to TypeScript because both types are aliases for the same type:

+ +
type UserInputSanitizedString = string;
+ 
+function sanitizeInput(str: string): UserInputSanitizedString {
+  return sanitize(str);
+}
+ 
+// Create a sanitized input
+let userInput = sanitizeInput(getInput());
+ 
+// Can still be re-assigned with a string though
+userInput = "new input";
+
+

+ + + Interfaces + + +

+ + +

Interfaces are used to define contracts for interaction with external entities. If an interface has a property/method, an Object/Class which implements it must have it. Interfaces can be used to define custom types for Objects in Typescript.

+ +

Examples:

+
//Interface IPerson respresents a person by attributes firstName and lastName and a method to getFullName()
+interface IPerson {
+    firstName: string;
+    lastName: string;
+    getFullName(): string;
+}
+
+//Class Person implements interface Iperson. Person class must contains all the attributes and methods of interface. 
+class Person implements IPerson {
+
+    public firstName: string = '';
+    public lastName: string = '';
+
+    public getFullName(): string {
+        return this.firstName + ' ' + this.lastName;
+    }
+
+    // It can contain any other properties/methods but must contain those in the interface.
+
+}
+
+const person: IPerson = new Person();
+
+

+ + + Custom types + + +

+ + +
    +
  • For complicated types (usually required for Objects), it is recommended to use an interface.
  • +
  • Additionally, typescript contain the ‘type’ keyword which can be used to create new types.
  • +
  • This is generally used when a union of different types is required.
  • +
+ +

Examples:

+
interface IStudent {
+    name: string;
+    age: number;
+    studentID: number;
+    gender: string;
+    isEnrolled: boolean;
+}
+
+const student: IStudent = {
+    name: 'name',
+    age: 20,
+    studentID: 111111111,
+    gender: 'hidden'
+    isEnrolled: true;
+};
+
+type StringOrNumber = string | number;
+
+let x = 1;
+x = 'some string';
+
+

+ + + Generics + + +

+ + +
    +
  • Generics allow us to pass different types to a function/class making it more generalized.
  • +
  • This will be especially useful when working with http requests.
  • +
+ +

Examples:

+ +
function myFunc<T>(data: T): T {
+    console.log(data);
+    return data
+}
+
+let x: string = myFunc<string>('some string');
+let y: number = myFunc<number>(5);
+
+class myClass<T> {
+
+}
+
+interface IMyInterface<T> {
+
+}
+
+

+ + + Modules + + +

+ + +

A Module in typescript is defined by using the ‘Import’ or ‘Export’ keywords within a file. Variables declared in a module are scoped locally and are not added to the global scope.

+ +
    +
  • Export makes the variables and functions from a file visible outside it.
  • +
  • Import allows use to use variables and functions exported by another file.
  • +
+ +

Examples:

+
// file1.ts
+
+export const someVar: string = 'Variables can be exported too.';
+
+export default function add(x: number, y: number): number {
+    return x + y;
+}
+
+export function subtract(x: number, y: number): number {
+    return x - y;
+}
+
+function multiply(): void {
+    throw new Error();
+}
+
+
+// file2.ts
+import add from './file1'; // curly braces are not required if export default is used.
+// There can be only 1 export default in a while. Use it if file has only 1 export.
+import { subtract } from './file1';
+
+add(1, 2);
+subtract(2, 1);
+
+// multiply cannot be imported.
+
+

+ + + Object oriented programming concepts using typescript + + +

+ + +

Object Oriented Programming or OOP is a programming paradigm that has four principles which are:

+ +
    +
  • Inheritance
  • +
  • Polymorphism
  • +
  • Abstraction
  • +
  • Encapsulation
  • +
+

+ + + Inheritance + + +

+ + +

TypeScript supports the concept of Inheritance. Inheritance is the ability of a program to create new classes from an existing class. The class that is extended to create newer classes is called the parent class/super class. The newly created classes are called the child/sub classes.

+ +

A class inherits from another class using the ‘extends’ keyword. Child classes inherit all properties and methods except private members and constructors from the parent class. However, TypeScript doesn’t support multiple inheritance.

+ +

Syntax:

+
class child_class_name extends parent_class_name
+
+ +

Example:

+
//Parent class Shape
+class Shape {
+ Area:number
+  constructor(a:number) {
+    this.Area = a
+ }
+}
+
+//Child class Circle that inherits properties of Shape 
+class Circle extends Shape {
+ disp():void {
+    console.log("Area of the circle:  "+this.Area)
+ }
+}
+var obj = new Circle(223);
+obj.disp()
+
+

+ + + Polymorphism + + +

+ + +

When multiple classes inherit from a parent and override the same functionality, the result is polymorphism. Each of those child classes now implements a property or method, but they each may have their own way of performing that implementation.

+ +

Alternatively, one child class might override the parent’s members while another child doesn’t but just accepts the parent class’s implementation instead. This also demonstrates polymorphic behavior, since those behaviors are different between the siblings.

+ +
class CheckingAccount {
+  open(initialAmount: number) {
+    // code to open account and save in database
+  }
+}
+
+class BusinessCheckingAccount extends CheckingAccount {
+  open(initialAmount: number) {
+    if (initialAmount < 1000) {
+      throw new Error("Business accounts must have an initial deposit of 1.000 Euros")
+    }
+    super.open(initialAmount);
+  }
+}
+
+class PersonalCheckingAccount extends CheckingAccount {
+  open(initialAmount: number) {
+    if (initialAmount <= 0) {
+      throw new Error("Personal accounts must have an initial deposit of more than zero Euros")
+    }
+    super.open(initialAmount);
+  }
+}
+
+ +

In the above code sample shows, the two child classes have different business rules to implement when it comes to opening an account – mainly different opening balances. Because both children have a method to open the account but both children choose to do it differently means the behavior is polymorphic.

+ +

To achieve polymorphism, inherit from a base class, then override methods and write implementation code in them. In addition to overriding methods, you can overload methods to achieve polymorphism.

+ +

Overloaded methods are methods that have different signatures (i.e., different data types or number of arguments) with the same name. However, in TypeScript, methods aren’t overloaded by simply modifying the types or number of arguments like in some other languages.

+ +

To create an overload in TypeScript, you can either add optional arguments to a method, or overload function declarations in an interface and implement the interface.

+

+ + + Abstraction + + +

+ + +

Abstraction is a way to model objects in a system that creates a separation of duties between class or type and the code that inherits it.

+ +

A developer creates a type, i.e., a class or interface, and that type specifies what the calling code should implement, but not how. So it’s the job of the abstract type to define what needs to be done, but up to the consuming types to actually do those things. To enforce abstraction, inherit or implement from abstract classes and interfaces.

+ +

For example, some bank accounts have fees. You can create a Fee interface that defines a method for charging a fee. Fees don’t apply to all types of accounts, so it’s best to create an interface that can be applied to specific classes anywhere in the inheritance hierarchy. A checking account might charge fees, where its parent and sibling, the generic bank account and savings accounts might not.

+ +
interface Fee {
+  chargeFee(amount: number );
+}
+
+// parent BankAccount and sibling SavingsAccount do not implement Fee interface
+class BankAccount { ... }
+
+class SavingsAccount extends BankAccount { ... }
+
+// checking implements Fee
+class CheckingAccount extends BankAccount implements Fee {
+  chargeFee(amount: number) {}
+}
+
+ +

Children classes inherit interface members that have been implemented in their parent, so if a BusinessChecking account has inherited from the CheckingAccount class, then it inherits that implementation.

+ +
// BusinessChecking inherits CheckingAccount and therefore Fee
+class BusinessChecking extends CheckingAccount {  }
+
+
+// Code that uses BusinessChecking can call chargeFee
+function CalculateMonthlyStatements() {
+  let businessChecking = new BusinessChecking();
+  businessChecking.chargeFee(100);
+}
+
+

+ + + Encapsulation + + +

+ + +

Encapsulation is a key part of Object Oriented Programming that is a way to structure code so that a certain block of code has specific access points for external code. The term for this is “visibility” or “accessibility”. Visibility defines what code from one method, property, or class can call code in another method, property, or class.

+ +

In TypeScript, we enforce encapsulation with methods and properties that only allow access to data that we control. The Withdraw method below does that by doing the calculation and updating the class level _balance field. The Balance property then returns the private _balance field to the calling code.

+ +
Withdraw(amount: number): boolean
+{
+    if (this._balance > amount)
+    {
+        this._balance -= amount
+        return true;
+    }
+    return false;
+}
+private _balance: number;
+get Balance(): number {
+    return this._balance;
+}
+
+

+ + + General Guidelines + + +

+ + +
    +
  • Naming conventions +
      +
    • use kebab-case for file names.
    • +
    • Use camelCase for variable and function names.
    • +
    • Use PascalCase for Class and constructor function names.
    • +
    +
  • +
  • Prefer descriptive names over random letters.
  • +
  • Although typing is optional in typescript, it is not optional for this course.
  • +
  • Always use strict equality.
  • +
  • Use a linter. (as specified on course website)
  • +
  • Use a prettifier. (If the linter doesn’t do it already)
  • +
  • Use general coding guidelines as discussed in Week1.
  • +
+

+ + + tsconfig + + +

+ +

What is the tsconfig.json file?

+
    +
  • The presence of a tsconfig.json file in a directory indicates that the directory is the root of a TypeScript project. The tsconfig.json file specifies the root files and the compiler options required to compile the project.
  • +
  • JavaScript projects can use a jsconfig.json file instead, which acts almost the same but has some JavaScript-related compiler flags enabled by default.
  • +
  • Note: In many packages, a configuration file is included, featuring predefined settings that typically require no alterations. Also, you may not need this for simple projects like hello world. +A project is compiled in one of the following ways:
  • +
+ +

Using tsconfig.json or jsconfig.json:

+
    +
  • By invoking tsc with no input files, in which case the compiler searches for the tsconfig.json file starting in the current directory and continuing up the parent directory chain.
  • +
  • By invoking tsc with no input files and a –project (or just -p) command line option that specifies the path of a directory containing a tsconfig.json file, or a path to a valid .json file containing the configurations. +When input files are specified on the command line, tsconfig.json files are ignored.
  • +
+ +

Example tsconfig.json files: +Using the files property

+ +
  "compilerOptions": {
+    "module": "commonjs",
+    "noImplicitAny": true,
+    "removeComments": true,
+    "preserveConstEnums": true,
+    "sourceMap": true
+  },
+  "files": [
+    "core.ts",
+    "sys.ts",
+    "types.ts",
+    "scanner.ts",
+    "parser.ts",
+    "utilities.ts",
+    "binder.ts",
+    "checker.ts",
+    "emitter.ts",
+    "program.ts",
+    "commandLineParser.ts",
+    "tsc.ts",
+    "diagnosticInformationMap.generated.ts"
+  ]
+}
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/tutorials/week1-unit-testing.html b/tutorials/week1-unit-testing.html new file mode 100644 index 00000000..57599a2f --- /dev/null +++ b/tutorials/week1-unit-testing.html @@ -0,0 +1,1234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Unit Testing with Jest | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+ +

This tutorial covers the basics on unit testing with Jest. By the end of this tutorial, you will have an introduction to unit testing with jest, best practices, and some handy tricks and tips to use in your tests. Starter code is available here, and finished code with tests covered in this tutorial can be found here. We strongly recommend using the completed solution only for reference and implementing the tutorial step by step from the starter code.

+ +

Contents:

+ + +

+ + + Understanding Unit Testing + + +

+ +

At some point, every programmer has wondered why they should spend time writing test cases instead of focusing on implementing a new feature. The reason for this is that it is important! In this tutorial, we’ll go through several aspects of unit testing, with a focus on utilizing Jest. Before we get into that, let’s define unit testing and why it’s so important in the real world.

+ +

Unit testing is not a new concept; it has been around for a long time. “Unit tests are often automated tests prepared and executed by software engineers to check that a portion of an application (referred to as a “unit”) matches its design and behaves as expected,” according to Wikipedia. So, to put it another way, it’s a technique to undertake rigorous testing of every single function/module in isolation.

+ +

Unit testing technques:

+
    +
  • Black Box Testing : It is a process of validating a function’s input and output without any knowledge of it’s internal implementation details.
  • +
  • White Box Testing : Unlike Black Box testing, white box testing focuses on testing the specific internal code flows, uncovering any unidentified error or bug in that component.
  • +
  • Gray Box Testing : Gray Box testing is a combination of the two above wherein partial knowledge of the internal code is needed. This strategy lowers a tester’s reliance on a developer for every minor issue, allowing the tester to detect and resolved it alone.
  • +
+

+ + + Testing with Jest + + +

+ +

+ + + Basics + + +

+ + +

To understand the basics of unit testing, let us look at the file called ‘calculator.ts’ present in the directory src/services/math/. This file contains a class called Calculator with a method for add() defined as shown below:

+ +
  // Contents of src/services/math/calculator.ts
+
+  export default class Calculator {
+    public add(num1: number, num2: number): number {
+      const result: number = num1 + num2;
+      console.log("The result is: ", result);
+      return result;
+    }
+  }
+
+ +

Let us write some tests for this code using jest. The test code will go into a file in the same directory titled ‘calculator.spec.ts’. Create this file now. This pattern of ‘file-name.spec.ts’ is how you should name all of your test files when using jest.

+ +

All test files start with a suite. A suite is a collection of tests (or a logical grouping of tests). In jest, a suite is created by using the function describe(). The suite takes 2 arguments: the 1st being the description of the suite and the second being a callback function. Additionally, suites can be nested to form logical groups. +Suites can further be broken down into 3 components that we will explore in detail shortly:

+ +
    +
  1. Setup
  2. +
  3. Teardown
  4. +
  5. Test
  6. +
+ +

Syntax:

+ +
  describe("Description of suite", () => {
+    // The tests go here.
+  });
+
+ +

Suites can be used to make debugging easier when you are using a large number of tests. Here is one recommended suite hierarchy:

+ +
    +
  • Top level describe should contain the file path after src.
  • +
  • Second describe should contain the name of the Class/File being tested.
  • +
  • Subsequent describe blocks should contain the name of the function being tested.
  • +
+ +

Using this hierarchy, the test file for the above example would look as follows:

+ +
  describe("services > math", () => {
+    describe("Calculator", () => {
+      describe("add()", () => {
+        // Tests for add() go here.
+      });
+    });
+  });
+
+

+ + + Specs + + +

+ + +

A spec is an actual test that executes some code and asserts some result. A test is created using the keyword it() or test(). Similar to describe(), it() takes 2 arguments, the first being the description of the test and the second being a callback. Generally, we want to describe what the code should do in the description of it() and assert the described behavior within the test. Each test can be broken down into 3 parts (Assemble, Act, Assert) which makes up the AAA pattern. Optionally, there may be a clean-up/teardown step after the assert.

+ +

Syntax:

+ +
  it("should check a specific behaviour", () => {});
+
+ +

Let us write a simple test for our add() method to check 1 + 1 = 2. +We start by adding a spec to the suite we created previously.

+ +
  describe("services > math", () => {
+    describe("Calculator", () => {
+      describe("add()", () => {
+        it("should return 2 when inputs are 1 and 1", () => {
+          // Assemble
+          // Act
+          // Assert
+        });
+      });
+    });
+  });
+
+

+ + + Matchers + + +

+ +

+ + + .toEqual() vs .toBe() vs .toStrictEqual() + + +

+ + +

All three matchers are used to test equality, though they have slight but important differences. To understand these differences, let us take a look at the example of Store.ts with the below code.

+ +
  export default class Store {
+    private static _data: any = null;
+
+    public static getData(): any {
+      return Store._data;
+    }
+
+    public static setData(data: any): void {
+      Store._data = data;
+    }
+  }
+
+ +
    +
  1. Use .toEqual() to compare recursively all properties of object instances (also known as “deep” equality). It calls Object.is to compare primitive values, which is even better for testing than ‘===’, the strict equality operator. +This is the most commonly used matcher.
  2. +
+ +
  describe("utils > store", () => {
+    describe("Store", () => {
+      beforeEach(() => {
+        Store["_data"] = undefined;
+      });
+
+      describe("setData()", () => {
+        it("should assign the input data to Store._data", () => {
+          const mockData = { key: "value" };
+
+          Store.setData(mockData);
+
+          expect(Store["_data"]).toEqual(mockData);
+        });
+      });
+
+      describe("getData()", () => {
+        it("should return an object equal to Store._data", () => {
+          const mockData = { key: "value" };
+          Store["_data"] = mockData;
+
+          const returnedValue = Store.getData();
+
+          expect(returnedValue).toEqual(mockData);
+        });
+      });
+    });
+  });
+
+ +
    +
  1. Use .toBe() to compare primitive values or to check referential identity of object instances. It calls Object.is to compare values, which is even better for testing than ‘===’, the strict equality operator.
  2. +
+ +
  describe("getData()", () => {
+    it("should return an object with a reference different to Store._data", () => {
+      const mockData = { key: "value" };
+      Store["_data"] = mockData;
+
+      const returnedValue = Store.getData();
+
+      expect(returnedValue).toEqual(mockData);
+      expect(returnedValue).not.toBe(mockData);
+      expect(Store["_data"]).toBe(mockData);
+    });
+  });
+
+ +
    +
  1. Use .toStrictEqual() to test that objects have the same types as well as structure. +This checks for undefined in Objects and sparseness in Arrays. +
      +
    • { key: undefined } is not strictly equal to { }.
    • +
    • [ , 1] is not strictly equal to [undefined, 1].
    • +
    +
  2. +
+ +
  it("should return an object strictly equal to object stored in Store._data", () => {
+    const mockData = { key: "value" };
+    const mockDataWithUndefined = { key: "value", key2: undefined };
+    Store["_data"] = mockData;
+
+    const returnedValue = Store.getData();
+
+    expect(returnedValue).toStrictEqual(mockData);
+    expect(returnedValue).toEqual(mockDataWithUndefined);
+    expect(returnedValue).not.toStrictEqual(mockDataWithUndefined);
+  });
+
+

+ + + AAA + + +

+ +

+ + + Assemble + + +

+ + +

In order to run a test, we need to first assemble it. This may include creating instances of classes/variables, setting up test data for inputs, setting up spies/stubs/mocks (which will be covered in subsequest sections), or setting up the expected output. In simple cases, one may not need to assemble the test. This phase is very similar to the setup phase.

+ +

In our example, let us create an instance of the Calculator class as part of assembling the test.

+ +
  import Calculator from "./calculator";
+
+  describe("services > math", () => {
+    describe("Calculator", () => {
+      describe("add()", () => {
+        it("should return 2 when inputs are 1 and 1", () => {
+          const calculator: Calculator = new Calculator();
+
+          // Act
+
+          // Assert
+        });
+      });
+    });
+  });
+
+

+ + + Act + + +

+ + +

In this step, we actually execute the function under test with required inputs and get the returned result (if any).

+ +

In our example, we will invoke the add() method with inputs (1, 1) and get the result.

+ +
  import Calculator from "./calculator";
+
+  describe("services > math", () => {
+    describe("Calculator", () => {
+      describe("add()", () => {
+        it("should return 2 when inputs are 1 and 1", () => {
+          const calculator: Calculator = new Calculator();
+
+          const result: number = calculator.add(1, 1);
+
+          // Assert
+        });
+      });
+    });
+  });
+
+

+ + + Assert + + +

+ + +

Assertion is a statement that validates the behavior of our code by comparing the actual result against the expected results. There are many assertions provided by Jest, including some useful assertions we will use throughout our tests. Some of these assertions are listed below:

+ +
    +
  • expect(actual).toEqual(expected) // Expects both entities to have the same value.
  • +
  • expect(actual).toBe(expected) // Expects both entities to be the same.
  • +
  • expect(spy/stub/mock).toHaveBeenCalled() // Expects a function being spied/stubbed/mocked to be invoked.
  • +
  • expect(spy/stub/mock).toHaveBeenCalledWith([arguments]) // Expects a function being spied/stubbed/mocked to be invoked with specified arguments.
  • +
  • expect(actual).toBeDefined() // Expects the entity to be defined.
  • +
  • expect(actual).not. // Negates the assertion. Can be chained with any matchers above
  • +
  • await expect(error causing code returning a promise).rejects.toThrowError() // Waits for the error throwing code that returns promise (e.g. an API call) to throw the error and asserts the error was thrown.
  • +
+ +

A full list of matchers can be found here.

+ +

In our example, we can use the .toEqual() matcher.

+ +
  import Calculator from "./calculator";
+
+  describe("services > math", () => {
+    describe("Calculator", () => {
+      describe("add()", () => {
+        it("should return 2 when inputs are 1 and 1", () => {
+          const calculator: Calculator = new Calculator();
+
+          const result: number = calculator.add(1, 1);
+
+          expect(result).toEqual(2);
+        });
+      });
+    });
+  });
+
+

+ + + Setup and Teardown + + +

+ + +

Often in tests, we need some things to happen before a test actually runs and some things to happen after it. This may include resetting/initializing values, setting up test data, setting up spies/stubs/mocks, cleaning up variables after a test, or resetting spies/stubs/mocks. Sometimes these steps may need to be repeated for each test. This is where the setup and teardown can be especially useful.

+ +

Jest Provides 2 methods for setup and 2 methods for teardown:

+ +
    +
  • beforeAll(): Runs one time before all the tests in a suite.
  • +
  • beforeEach(): Runs before every test in a suite.
  • +
  • afterEach(): Runs after every test in a suite.
  • +
  • afterAll(): Runs once after all tests in a suite.
  • +
+ +

In our example, notice we created an instance of calculator in our Assemble phase. We will probably have multiple tests for the calculator that will require this instance. In order to avoid repeating this in every step, let us move this to the setup phase and add a teardown to clear this after all tests.

+ +

Note: Use beforeEach()/afterEach() if the function/class stores state, and we need a clean instance for each test. In our case, calculator does not store any state, and we can share the same instance across tests with out any side effects. Hence, we will use beforeAll()/afterAll().

+ +
  import Calculator from "./calculator";
+
+  describe("services > math", () => {
+    describe("Calculator", () => {
+      describe("add()", () => {
+        let calculator: Calculator;
+
+        beforeAll(() => {
+          calculator = new Calculator();
+        });
+
+        afterAll(() => {
+          calculator = undefined;
+        });
+
+        it("should return 2 when inputs are 1 and 1", () => {
+          const result: number = calculator.add(1, 1);
+
+          expect(result).toEqual(2);
+        });
+      });
+    });
+  });
+
+ +

Let us add another test to cover a different scenario, such as adding negative numbers.

+ +
  import Calculator from "./calculator";
+
+  describe("services > math", () => {
+    describe("Calculator", () => {
+      describe("add()", () => {
+        let calculator: Calculator;
+
+        beforeAll(() => {
+          calculator = new Calculator();
+        });
+
+        afterAll(() => {
+          calculator = undefined;
+        });
+
+        it("should return 2 when inputs are 1 and 1", () => {
+          const result: number = calculator.add(1, 1);
+
+          expect(result).toEqual(2);
+        });
+
+        it("should return -2 when inputs are -1 and -1", () => {
+          const result: number = calculator.add(-1, -1);
+
+          expect(result).toEqual(-2);
+        });
+      });
+    });
+  });
+
+

+ + + Mock Testing + + +

+ +

As a project grows so do the interdependencies in the project. A function under test can have dependencies from various external entities. This may include other functions, network requests, database connections, or built-in connections. Spies, Stubs, and Mocks are ways of dealing with such external dependencies. You can read more on what you can do with spies/stubs/mocks here.

+

+ + + Spy + + +

+ + +

A spy is a watcher on a function that tracks various properties of the function being spied on. This can return information such as whether a function was invoked, how many times it was invoked, and what argument it was invoked with. A spy on a function is created using the syntax const spy = jest.spyOn(object, 'methodName');

+ +

Note: The function being spied on actually executes.

+ +

In our example, we have an external dependency on console.log(). Let us add a spy and test for it.

+ +
  import Calculator from "./calculator";
+
+  describe("services > math", () => {
+    describe("Calculator", () => {
+      describe("add()", () => {
+        let calculator: Calculator;
+
+        beforeAll(() => {
+          calculator = new Calculator();
+        });
+
+        afterAll(() => {
+          calculator = undefined;
+        });
+
+        it("should invoke console.log() with the result 2 for inputs 1 and 1", () => {
+          const logSpy = jest.spyOn(window.console, "log");
+
+          const result: number = calculator.add(1, 1);
+
+          expect(logSpy).toHaveBeenCalledWith("The result is: ", result);
+
+          logSpy.mockRestore();
+        });
+      });
+    });
+  });
+
+

+ + + Mock + + +

+ + +

A mock is function which replaces an existing function. In our example, if we wanted to change the behavior of console.log() for our tests, we can do so using a mock. A mock implementation can be substituted for a spy or a jest.fn(). The syntax is as below:

+ +
  spy.mockImplementation(() => {
+    // new function body goes here.
+  });
+
+ +

Note: The function being mocked does not execute. +In our example, if we wanted to replace the behavior of console.log(), we can do so as shown:

+ +
  it("should invoke console.log() with the result 2 for inputs 1 and 1", () => {
+    const logSpy = jest.spyOn(window.console, "log");
+    logSpy.mockImplementation(() => {
+      // This will no longer print to console.
+    });
+
+    const result: number = calculator.add(1, 1);
+
+    expect(logSpy).toHaveBeenCalledWith("The result is: ", result);
+
+    logSpy.mockRestore();
+  });
+
+ +

Warning: Watch out for circular dependencies in mock implementations.

+

+ + + Stub + + +

+ + +

A stub is a special kind of mock which does not require an alternate implementation but instead returns some value that we specify. When a stub gets invoked, it does not invoke the actual function, but returns the desired value instead. The syntax is as below:

+ +
  spy.mockReturnValue(someValue);
+
+ +

To return a promise, we can use:

+ +
  spy.mockResolvedValue(someValue);
+
+

This can be especially handy when stubbing Axios requests.

+ +

Using a stub in our example simply prevents console.log() from being executed, since it does not return a value anyway.

+ +
  it("should invoke console.log() with the result 2 for inputs 1 and 1", () => {
+    const logSpy = jest.spyOn(window.console, "log");
+    logSpy.mockReturnValue();
+
+    const result: number = calculator.add(1, 1);
+
+    expect(logSpy).toHaveBeenCalledWith("The result is: ", result);
+
+    logSpy.mockRestore();
+  });
+
+

+ + + Testing Asynchronous Code + + +

+ +

+ + + Promise + + +

+ + +

In previous tutorials, we have used Axios to make http requests which return promises. This is how we can write tests for axios requests. Consider the example below:

+ +
  import axios from "axios";
+  import Store from "../../utils/store/store";
+
+  export default class HttpService {
+    public getData(): Promise<any> {
+      return axios.get("/myUrl");
+    }
+  }
+
+ +

We can test the above code as follows:

+ +
  // Assuming we have done the setup as in previous tests
+
+  describe("getData()", () => {
+    it('should invoke axios.get() with "myUrl"', async () => {
+      const getStub = jest
+        .spyOn(axios, "get")
+        .mockResolvedValue({ status: 200, data: {} });
+
+      await httpService.getData();
+
+      expect(getStub).toHaveBeenCalledWith("/myUrl");
+    });
+
+    it("should return the status as 200", async () => {
+      const getStub = jest
+        .spyOn(axios, "get")
+        .mockResolvedValue({ status: 200, data: {} });
+
+      const response = await httpService.getData();
+
+      expect(response.status).toEqual(200);
+
+      getStub.mockRestore();
+    });
+  });
+
+ +

Note: You can return different values for subsequent calls to a stub.

+ +

Occasionally, you may run into situations where an http request is made but no promise is returned. This is often found in cases involving “fire and forget” calls or a central store with an Observable pattern implementation (e.g. Redux with react). We cannot await a function that does not return a promise. However, we can use fake timers to simulate passage of time to test such asynchronous behavior. Consider the example below:

+ +
  import axios from "axios";
+  import Store from "../../utils/store/store";
+
+  export default class HttpService {
+    public getData(): Promise<any> {
+      return axios.get("/myUrl");
+    }
+
+    public getDataAndSetStore(): void {
+      axios.get("/myUrl").then((res) => {
+        Store.setData(res.data);
+      });
+    }
+  }
+
+ +

We can test the above functionality as follows:

+ +
  describe("getDataAndSetStore()", () => {
+    it('should invoke axios.get() with "myUrl"', async () => {
+      const getStub = jest
+        .spyOn(axios, "get")
+        .mockResolvedValue({ status: 200, data: {} });
+
+      await httpService.getDataAndSetStore();
+
+      expect(getStub).toHaveBeenCalledWith("/myUrl");
+    });
+
+    it("should set the data in store", async () => {
+      const addDataStub = jest.spyOn(Store, "setData").mockImplementation();
+      const getStub = jest
+        .spyOn(axios, "get")
+        .mockResolvedValue({ status: 200, data: "myData" });
+      jest.useFakeTimers();
+
+      httpService.getDataAndSetStore();
+      jest.runAllTimers();
+      await Promise.resolve();
+
+      expect(addDataStub).toHaveBeenCalledWith("myData");
+
+      addDataStub.mockRestore();
+      getStub.mockRestore();
+      jest.useRealTimers();
+    });
+  });
+
+

+ + + Callbacks + + +

+ + +

Callbacks are one of the most commonly used patterns for asynchronous programming in JavaScript/TypeScript. Consider the below callback function use case:

+ +
test('Check if I am a true husky', () => {
+  function callback(data) {
+    expect(data).toBe('I am from Northeastern!');
+  }
+  fetchData(callback);
+});
+
+

Here, fetchData() is a function that takes a callback and would call that callback function later in it’s implementation. Now consider 2 scenarios:

+ +

case 1:

+
fetchData(callback) {
+  setTimeout(()=>{
+    callback('I am from Northeastern!');
+  }, 1000);
+}
+
+ +

case 2:

+
fetchData(callback) {
+  setTimeout(()=>{
+    callback('I am not from Northeastern!');
+  }, 1000);
+}
+
+ +

The test case would still pass in either of the above scenarios since fetchData() is an async function, which means the program will not wait for the call to complete.

+ +

The correct way to test a callback would be using the argument done in the test like shown in below example:

+ +
test('Check if I am a true husky', (done) => {
+  function callback(data) {
+    expect(data).toBe('I am from Northeastern!');
+    done();
+  }
+  fetchData(callback);
+});
+
+ +

Now, the program would wait for done to be invoked. This implementation would correctly test both the above scenarios.

+

+ + + UI Testing + + +

+ +

Testing UIs can be very tricky, especially when we want to test features involving user interaction (e.g. a user clicking on a button). However, there are some useful tools that can help us. The React Testing Library provides many helpful features that can help us.

+
import {render, screen} from '@testing-library/react'
+import Counter from './Counter'
+it('renders the Counter component correctly', async () => {
+  render(<Counter />);
+
+  // Will throw error if not found
+  screen.getByText("Count: 0" )
+  screen.getByText("Click me!" )
+})
+
+

Above is a very simple test to ensure that our Counter component renders as expected with out any user input. Suppose now that we wanted to test user interaction with the page:

+ +
import {render, fireEvent, screen} from '@testing-library/react'
+import Counter from './Counter'
+it('correctly renders the updated count after the user clicks the button', async () => {
+  render(<Counter />);
+  screen.getByText("Count: 0" )
+  fireEvent.click(screen.getByRole('button'))
+  screen.getByText("Count: 1" )
+})
+
+ +

A full list of testing functions from React Testing Library can be found here.

+

+ + + Setting up testing using Jest in VSCode + + +

+ + +

Testing can sometimes get cumbersome as the user is expected to remember all the options provided by Jest to run a specific set of tests, or otherwise the user will have to run the entire test suite just to verify the result of a single test case. Not anymore!

+ +

With vscode-jest, you have complete control over the way you want to run tests, with many features that visually inform the users about which specific line is failing with what error.

+ +

Running tests on demand

+

+ + + Features + + +

+ + +
    +
  • Starts Jest automatically when you’re in a root folder project with Jest installed.
  • +
  • Show individual fail / passes inline.
  • +
  • Show fails inside the problem inspector.
  • +
  • Highlights the errors next to the expect functions.
  • +
  • Adds syntax highlighting to snapshot files.
  • +
  • A one button update for failed snapshots.
  • +
  • Show coverage information in files being tested.
  • +
  • Help debug jest tests in vscode.
  • +
  • Supports multiple test run modes (automated, manual, and hybrid onSave) to meet user’s preferred development experience.
  • +
  • Track and shows overall workspace/project test stats.
  • +
+

+ + + Installation + + +

+ +

+ + + Direct Installation + + +

+ + +
    +
  1. Open Visual Studio Code, go to the extension tab.
  2. +
  3. Search for “Jest” with the publisher name as “Orta”.
  4. +
  5. Click “Install”.
  6. +
+

+ + + From Visual Studio Marketplace + + +

+ + +
    +
  1. Visit Jest - Visual Studio Marketplace and click “Install”.
  2. +
  3. +

    Allow the browser to open VSCode as shown (might be different for macOS/Linux systems) by clicking on Continue.

    + +

    Allow browser to open VSCode

    +
  4. +
  5. Once VSCode is open, click on “Install”.
  6. +
+ +

Once installed, the extension should work out of the box without any extra configuration for most of the homework handouts and project starter code.

+ +

You can verify the installation by checking if the following is visible to you when you open a project which has Jest configured (VSCode restart maybe required.).

+ +

Verify Installation

+ +

If restarting Jest does not work, you can run the following in your VSCode by opening VSCode’s command palette (Ctrl + Shift + P in Windows, Cmd + Shift + P in macOS) and run the following - “Jest: Start All Runners”.

+ +

Manually start Jest Runner

+

+ + + Additional Extensions and Libraries + + +

+ + +

If you would like to run each test individually (and all tests in test suites collectively), VSC extenstion called “Jest Runner” will be very useful. You can also explore jest extended library that offers additional matchers for testing.

+

+ + + Configuration + + +

+ + +

As of the latest stable version, the extension is set to run all tests whenever any change is made to the files containing the tests. You can modify the behaviour to manual execution where you can run specific test cases as and when required. This can be modified by changing the "jest.autoRun" setting as follows -

+ +
    +
  • "jest.autoRun": {"watch": true} => will start the jest with the watch flag and leave all tests at “unknown” state until changes are detected.
  • +
  • "jest.autoRun": {"watch": true, "onStartup": ["all-tests"]} => will start running all tests upon project launch to update overall project test stats, followed by the jest watch for changes.
  • +
  • "jest.autoRun": "off" => there will be no automatic test run, users will trigger test run by either command or context-menu.
  • +
  • "jest.autoRun": {"watch": false, "onSave": "test-file"} => the extension will trigger test run for the given test file upon save.
  • +
  • "jest.autoRun": {"watch": false, "onSave": "test-src-file"} => the extension will trigger test run for the given test or source file upon save.
  • +
+ +

An example of changing the extension to run in manual mode is demonstrated below -

+ +

Enable Manual Mode

+ +
+

NOTE - You will only be able to Run specific test cases as and when required only when autoRun is off. However, you’ll be able to Debug any test cases in all available modes.

+
+

+ + + Debugging Tests + + +

+ +

Once the extension is setup and confirmed to be working, we can now begin debugging the tests. You can do so by opening the testing tab from the left sidebar in VSCode to view all the tests present in the current project.

+ +

You can also navigate to the specific test file manually, and you will notice some icons/action buttons in the editor next to each test. Right clicking on the icons will bring up a test-specific context menu which will have options to debug the specific test. Be sure to place breakpoints in the specific pieces of code by clicking to the left of the line numbers.

+ +

Debug Test

+ +

If everything is setup correctly, the debugger in VSCode will pause at the breakpoint you set and you can check the values of the variables by hovering over the said variable.

+ +

Debugging in Action

+

+ + + General Guidelines For Writing Tests + + +

+ + +
+

Note: The following will be used as a reference when grading assignments.

+
+ +
    +
  1. Tests should be hermetic. +
      +
    • Reduce flakiness.
    • +
    • Flaky tests are those that fail intermittently: +
        +
      • Nondeterminism (e.g., hash codes, random numbers);
      • +
      • Timing issues (e.g., threads, network).
      • +
      • Availability of Resources
      • +
      +
    • +
    +
  2. +
  3. Tests should be clear. +
      +
    • After failure, should be clear what went wrong.
    • +
    +
  4. +
  5. Tests should be scoped as small as possible. +
      +
    • Faster and more reliable.
    • +
    +
  6. +
  7. Tests should make calls against public APIs. +
      +
    • Or they become brittle
    • +
    • Brittle tests are those that are not self-contained: +
        +
      • Ordering of tests (e.g., assume prior state)
      • +
      +
    • +
    +
  8. +
+ +
+

Note: The following will not be used as a reference when grading assignments. But you should try following these guidelines as much as you can.

+
+ +
    +
  1. Write tests based on the expected behavior, not based on the interpretation/implementation of it.
  2. +
  3. Test assertion (expect) should match the test description.
  4. +
  5. Each spec should test only 1 thing (preferably with 1 assertion per test).
  6. +
  7. Organize tests using suites (i.e. each method has it’s own suite).
  8. +
  9. Use setup and teardown functions to reduce code duplicity.
  10. +
  11. Code duplicity in tests is preferred over complicated logic to reduce it. +
      +
    • If your tests need tests, they have no value.
    • +
    +
  12. +
  13. Cover the happy path for your code first. +
      +
    • Follow up with edge cases.
    • +
    • End with error scenarios.
    • +
    +
  14. +
  15. Mock/Stub all external dependencies. +
      +
    • Clear the mocks after each test.
    • +
    +
  16. +
  17. If large test data is being used, ensure clean-up after tests to prevent memory leaks.
  18. +
  19. Code coverage is a deceptive measure. 100% coverage does not mean 100% tested code.
  20. +
  21. A well designed test suite improves the quality and reliability of code.
  22. +
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/tutorials/week1-user-stories.html b/tutorials/week1-user-stories.html new file mode 100644 index 00000000..011920d8 --- /dev/null +++ b/tutorials/week1-user-stories.html @@ -0,0 +1,434 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +User Stories and Conditions of Satisfaction | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+ +

This tutorial provides examples for user stories, conditions of satisfaction and how a minimum viable product can be defined.

+ +

Contents:

+ +

+ + + User Stories and Conditions of Satisfaction + + +

+ + +

A user story is an informal, general explanation of a software feature written from the perspective of the end user or customer. A user story is always of the following form:

+ +

As a <role> I can <perform action> so that I can <receive benefit>

+ +

User stories represent something the user/customer might want. There will be many ways to give the user/customer the benefit that they want.

+ +

We need to refine these in order to determine what to build. We call these refinements “conditions of satisfaction” (COS)

+ +

A COS should be a specific capability or behavior that the user expects, in the user’s terms. It should be visible to and verifiable by the user.

+ +

The COS is a guide to the implementation team. It should be specific enough so that the implementation team has a clear idea of what they are building. +There still may be many ways to implement a COS. For example, a COS probably would not specify any of the graphic or layout details; these would likely be left to the implementation team.

+

+ + + User Stories and Project Planning + + +

+ + +

In planning a project, need to assign priorities to each user story and Condition of Satisfaction. Priorities tell us the order in which COS and their associated engineering tasks should be addressed, and how much effort should be devoted to each of them. +There are many ways to describe priorities. For example, a user story might be described as Essential, Desirable, or Extension:

+
    +
  • Essential means the project is useless without it.
  • +
  • Desirable means the project is less usable without it, but is still usable.
  • +
  • Extension describes a User story or COS that is desirable, but may not be achievable within the scope of the project.
  • +
+

+ + + Minimum Viable Product (MVP) + + +

+ +

An MVP is a product that consists of all essential user stories. Developers should prioritize those above others.

+

+ + + User Stories and Test-Driven Development + + +

+ + +

We model the development process as a cycle of refinements:

+ +
    +
  1. User Stories
  2. +
  3. Conditions of Satisfaction
  4. +
  5. Testable behaviors
  6. +
  7. Executable Tests
  8. +
  9. Engineering Tasks (Code)
  10. +
+ +

As we proceed down these refinements, we will likely go back and revisit design decisions that we made at earlier stages. This is the topic of Module 02.

+

+ + + Examples + + +

+ +

+ + + User Stories: + + +

+ +

+ + + User Story #1: + + +

+ +

As a manager, I want to track my subordinates’ progress, so that the organization’s business goals are met. [Probably too general; in the agile terminology, this might be an “epic”, which is then refined into a set of user stories]

+

+ + + User Story #2: + + +

+ +

As a player in covey.town, I want to be able to play a game of connect4 with another player, so that I can pass the time enjoyably. (Essential)

+

+ + + User Story #3: + + +

+ +

As a player in covey.town, I want to see how well I’m doing at connect4 compared to other players, so I can feel superior to them. (Desirable)

+

+ + + User Story #4: + + +

+ +

As a player in covey.town, I want to learn different strategies for connect4, so I can win more games. (Extension)

+

+ + + User Story #5: + + +

+ +

As a user of Covey.Town, I want to be able to interact with other users in different activities like private chat, playing games, etc. while providing ability to customize user avatars to make the town more fun. (Essential but too general. Best to split it in 3 different stories one for private chat, one for games and one for customization.)

+

+ + + Conditions of Satisfaction: + + +

+ +

+ + + For User Story #2: + + +

+ +
    +
  • There should be a Connect4 area in the town, where players can play Connect4. (Essential)
  • +
  • Whenever there are two players in the same Connect4 area, a Connect4 game is started between them. (Essential)
  • +
  • The status of the Connect4 game is presented in graphical form on the screen (Essential).
  • +
  • When one player wins (or there is a draw), both players are notified of the result. (Essential)
  • +
  • When a player makes a move in the Connect4 game, the tile visibly drops onto the game board, and a suitable sound effect is emitted (Extension)
  • +
  • If other players enter a Connect4 area while a game is going on, they can see the status of the game also (Desirable)
  • +
+ +

[Note: these COSs do not describe the game board in detail, though that is pretty standard. They also do not describe what happens to the two players when the game is over. Do they stay in the area and spectate? Do the next two players play? Does the first spectator play the winner of the game? etc. etc.]

+
    +
  • There should be a way for one player to challenge another to play the game of Connect4, no matter where they are in the town. (This might be an Extension, or it might be a different user story).
  • +
+

+ + + For User Story #3: + + +

+ +
    +
  • There should be a leaderboard showing the Connect4 ranking of all the players in the town. (Essential).
  • +
  • The leaderboard should show the current score of all the Connect4 games currently in progress. (Desirable)
  • +
  • The leaderboard should show the current score of all the Connect4 games currently in progress, and any player can click on the entry for a game and spectate that game. (Desirable; might be Extension, depending on the expertise of the team).
  • +
  • The leaderboard should show the lifetime standings of all the players who have ever logged in to this town. (Extension)
  • +
+

+ + + For User Story #5: + + +

+ +
    +
  • User will be able to join an interactable area which includes leaderboards for all activities (This is too general and should be split in several conditions of satisfactions. For example, joining an area itself can be independent of being able to view leaderboard. Also each activity’s leaderboard should have its own condition of satisfaction. There could be several conditions for leaderboard alone to describe how data is organized)
  • +
  • User is able interact with an instance of interactable area created by another player (This COS is vague. What does interact mean? Does it mean enter an area or do something else?)
  • +
  • User is allowed to join an interactable area of another player in a town if the user was not invited (this sounds wrong, opposite to what the system should be doing)
  • +
  • User is allowed to join an interactable area (Desirable, The priority is wrong. For a user story that requires joining an interable area before anything else, this condition should be considered essential, instead of desirable)
  • +
+ +

Reference

+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/tutorials/week4-react-basics.html b/tutorials/week4-react-basics.html new file mode 100644 index 00000000..0f82cc05 --- /dev/null +++ b/tutorials/week4-react-basics.html @@ -0,0 +1,1123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +React Tutorial | CS4530, Spring 2024 + + + + + + + + + + + + + + + + + + + + Skip to main content + + + Link + + + + + + + Menu + + + + + + + Expand + + + + + + + + (external link) + + + + + + Document + + + + + + + Search + + + + + + + + + + Copy + + + + + + + Copied + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ +
+
+ +
Last updated: Mar 01, 24 21:00 UTC | Permalink
+

+ + + React Basics + + +

+ +

This tutorial covers the basic concepts of react. By the end of this tutorial, you will be able to create a new react app, understand the basic concepts of react such as states and props, understand React hooks and handling events.

+ + +

+ + + Creating a New Next Js App + + +

+ + +

Let’s use npx and create-next-app to create a new Next.Js project

+ +
    +
  • +

    npx stands for Node Package Execute (Part of the npm package since version 5.2). It is a runner that can execute any package that you want from the npm registry without even installing that package. In this case npx tool temporarily installs the create-next-app npm package and uses it to create our project.

    +
  • +
  • +

    Please check the following reference to the Next.Js boiler plate and getting started with framework +Next.Js official documentation

    +
  • +
+ +
    +
  1. +

    We use the create-next-app npx package to create an application that creates a fully-featured TS package.

    + +
     npx create-next-app my-app
    +
    + +
      +
    • Note 1: This will create a new project directory called my-app under the current directory.
    • +
    • Note 2: This will create a git repo in my-app, so you probably shouldn’t do this inside a pre-existing git repository.
    • +
    • Note 3: Create the project with the following details after executing the command + image
    • +
    +
  2. +
  3. Navigate to the project directory using the command: +
     cd my-app
    +
    +
  4. +
  5. To start the development server for React, run the command: +
     npm run dev
    +
    +
      +
    • Note: Next.Js renders the content within the app/pages.tsx file as the root component.
    • +
    +
  6. +
  7. Navigate to http://localhost:3000/ to see the default react page. +
      +
    • After a few seconds, a browser window opens, with our app running: +image
    • +
    +
  8. +
  9. Chakra UI components can be reused and the library can be installed for Next.js with following command: +
     npm i --save @chakra-ui/react @chakra-ui/next-js
    +
    +

    image

    +
    +

    The save flag is used to add the dependency in the package.json file.

    +
    +
  10. +
+ + +

+ + + React Component + + +

+ +

React follows a Component based architecture. A component is a +repeatable html element with built-in state, business logic, and a +lifecycle. The component may be something as simple as a single html +element such as an input box, or a button, or a complex entity made up +of other components.

+ +

Components are the basic building blocks of a React application and they allow the developer to split the UI into independent and reusable +pieces where each piece can be used in isolation.

+

+ + + Creating a Function Component in React + + +

+ + +

view in sandbox

+ +

The simplest method to define a component is to write a function in Javascript. These components are also widely referred as functional stateless components but in the recent versions of React, they have the capabilities to contain state as well.

+
import * as React from "react";
+
+interface Props {
+    name?: string
+};
+
+function App (props: Props) {
+  return (
+      <div className="App">
+        <h1>Welcome to React with Typescript Tutorial.</h1>
+      </div>
+  );
+}
+
+export default App;
+
+

+ + + A Few Things to Note About React Components: + + +

+ + +
    +
  • The root (App) component is the entry point for the React App and all other components are nested in it.
  • +
  • We define a function component using a javascript function, passing the props type in as a generic parameter.
  • +
  • The import statement is used to import the public classes/functions from the react library.
  • +
  • A function can return a single top level element. +
      +
    • div is the top level element in this case and other elements can be nested in it. +
        +
      • The attribute className is used to specify a CSS class name if CSS properties have been defined seperately for a class.
      • +
      • className attribute is used to set the value of an element’s class attribute. Using this property, the user can set the class of an element to the desired class.
      • +
      +
    • +
    +
  • +
  • The round brackets (()) after return are used to span a JSX/TSX element across multiple lines.
  • +
  • At last , the component needs to be exported from the current file, so that it can be imported somewhere else and can be used either in isolation or combination with other components for rendering on the UI.
  • +
  • Elements on one line can be returned directly.
  • +
  • Each instance of a component creates a new element independent of other instances of the component.
  • +
  • Each component has its own state, props, and lifecycle (which will be explored later in the tutorial).
  • +
+

+ + + Template For Structure of Function Component + + +

+ + +

view in sandbox

+ +
import * as React from "react";
+
+interface Props {
+    property1?: value1, 
+    property2?: value2
+}
+
+function ComponentName (props: Props) {
+  const handler = () => {
+    /* ... */
+  };
+    
+  return (
+    <div>Our TSX</div>
+  );
+};
+
+ComponentName.defaultProps = {
+  /* ... */  
+};
+
+export default ComponentName;
+
+

+ + + Props + + +

+ +

view in sandbox

+ +

React components are similar to JavaScript functions and can accept arbitrary arguments called props. Since components are reusable, props are especially useful to display different content in each instance of the component. Let us extract the header elements from the previous code snippet into a new component called Header. We can then use props to say “hello” to different users.

+ +
    +
  • Create a new file in src/ directory called Header.tsx
  • +
  • Create and export a function called Header in the file as below:
  • +
+ +
import React from "react";
+
+interface IProps {
+  name?: string;
+}
+
+const Header = (props: IProps) => {
+  return <h1>Hello, {props.name}</h1>;
+};
+
+Header.defaultProps = {
+  name: "World"
+};
+
+export default Header;
+
+ +
    +
  • The above code snippet creates a new function component Header and prints the value of the name passed in the props.
  • +
  • +

    It defines a defaultProps for the component Header where the default value for name is used in case the value for props for name is not passed in any instance of the component.

    +
  • +
  • In App.tsx: +
      +
    • Remove the code in h1 tags.
    • +
    • Import the Header component as below: +
        +
      • +
         import Header from './Header';
        +
        +
      • +
      +
    • +
    • Update the contents of return as below: +
        +
      • +
         <div className="App-header">
        +   <Header />
        +   <Header name="John" />
        +   <Header name="Jane" />
        + </div>
        +
        +
      • +
      +
    • +
    • Save all files and run npm start
    • +
    +
  • +
+ +

A few things to note from the above example:

+ +
    +
  • Component.defaultProps can be used to specify default values for props.
  • +
  • Components are rendered to the user interface and the component’s logic contains the data to be displayed in the view(UI).
  • +
  • Curly braces ({}) in JSX/TSX are used as a connection between the data to be displayed in the view and the component’s logic for displaying the data in the view. +
      +
    • In our example, {props.name} will reflect the value of the property name in the view(html) for the cases when the values “John” and “Jane” are passed as props for the name property.
    • +
    • If no props are passed for an instance of the component, then it will display the default value of props. +
      +

      Note: React uses a special syntax called JSX which allows us to mix HTML with JavaScript. The curly brackets {} lets the JSX parser know that the content inside the brackets is JavaScript.

      +
      +
    • +
    +
  • +
+

+ + + State + + +

+ +

State management is just a means of facilitating data sharing and communication among components. It creates a concrete data structure that you can read and write to reflect the state of your program.

+ +
const [counter, setCounter] = useState(0)
+
+ +

The above snippet shows creation of counter state with an intial value of 0. Using the array destructuring syntax we extract out the state variable and the function to update the counter value.

+ +

In its most basic form, a State object is a JavaScript object that represents the part of a component that can change as a result of a user’s action. States can also be thought of as a component’s memory.

+ +

State update calls are asynchronous. As one cannot expect to call the update state function on one line and expect the state to be updated on the next. The reason for this is that update state methods are more of a request than an immediate order to update state. So React schedules an update to a component’s state object. When state changes, the component responds by re-rendering. Also multiple update request may be batched into one for performance reasons.

+ +

Changes in state and/or props will both cause our React component to re-render. Changes in state, on the other hand, can only occur internally as a result of components modifying their own state. 

+

+ + + Communication Between Components: + + +

+ +

+ + + Parent To Child Component Communication: + + +

+ +

view in sandbox

+ +

Passing values from a parent component to a child component is simple. We only have to pass the values as props of the child component. The child component can then use the props object to output results. In the example code you will see that CounterContent component accepts a counter prop which is then used to display the value inside div element.

+
import { useState } from "react";
+
+interface CounterContentProps {
+  counter: Number;
+}
+
+function CounterContent({ counter }: CounterContentProps) {
+  return <div>Counter: {counter}</div>;
+}
+
+function Counter() {
+  const [counter, setCounter] = useState<number>(0);
+
+  return (
+    <>
+      <CounterContent counter={counter} />
+      <button onClick={() => setCounter(counter + 1)}>Increment Count</button>
+    </>
+  );
+}
+
+

+ + + Child to Parent Component Communication + + +

+ + +

view in sandbox

+ +

For passing data from child component to parent component do the following steps:

+
    +
  1. Declare a callback function inside the parent component. This function will get data from the child component.
  2. +
  3. Pass the callback function to the child component as props.
  4. +
  5. Child then sends the update to the parent through the use of the callback function.
  6. +
+ +

In the example below we have four children components:

+
    +
  1. CounterContent: Displays the counter value
  2. +
  3. IncrementCounterButton: Increments the counter value
  4. +
  5. DecrementCounterButton: Decrements the counter value
  6. +
  7. CustomCounterButton: Sets the counter to a particular value.
  8. +
+ +

All callback functions passed to the children component are declared in Counter function which is the parent component that maintains the state value for counter.

+ +
import { useState } from "react";
+
+interface CounterContentProps {
+  counter: Number;
+}
+
+interface IncrementCounterButtonProps {
+  incrementCount(): void;
+}
+
+interface DecrementCounterButtonProps {
+  decrementCount(): void;
+}
+
+interface SetCounterButtonProps {
+  setCount(value: number): void;
+}
+
+function CounterContent({ counter }: CounterContentProps) {
+  return <div>Counter: {counter}</div>;
+}
+
+/**
+ * Child component accepts a incrementCount callback function as a props.
+ * The callback which is declared inside the parent component triggers a state update.
+ */
+function IncrementCounterButton({
+  incrementCount
+}: IncrementCounterButtonProps) {
+  return <button onClick={() => incrementCount()}>increment Count</button>;
+}
+
+/**
+ * Child component accepts a decrementCount callback function as a props.
+ * The callback which is declared inside the parent component triggers a state update.
+ */
+function DecrementCounterButton({
+  decrementCount
+}: DecrementCounterButtonProps) {
+  return <button onClick={() => decrementCount()}>Decrement Count</button>;
+}
+
+function CustomCounterButton({ setCount }: SetCounterButtonProps) {
+  // change this value to see how child passes count value data to parent through
+  // the use of callback function
+
+  const dummyValue = 100;
+
+  return (
+    <button onClick={() => setCount(dummyValue)}>
+      Set Count to {dummyValue}
+    </button>
+  );
+}
+
+/**
+* Parent Component where state and callbacks are maintained. 
+**/
+function Counter() {
+  const [counter, setCounter] = useState(0);
+
+  const decrementCount = () => {
+    if (counter === 0) return;
+    setCounter(counter - 1);
+  };
+
+  const incrementCount = () => {
+    setCounter(counter + 1);
+  };
+
+  const setCount = (value: number) => {
+    setCounter(value);
+  };
+
+  return (
+    <>
+      <CounterContent counter={counter} />
+      <IncrementCounterButton incrementCount={incrementCount} />
+      <DecrementCounterButton decrementCount={decrementCount} />
+      <CustomCounterButton setCount={setCount} />
+    </>
+  );
+}
+
+

+ + + Handling Events + + +

+ + +

view in sandbox

+ +
    +
  • React impelemnts its own system of handling events that is very similar to handling events on DOM elements. There are some syntax differences: +
      +
    • React events are named using camelCase, rather than lower case.
    • +
    • With JSX a function can be passed as an event handler instead of a string.
    • +
    +
  • +
+ +

For example, the HTML

+ +
<button onclick="incrementCounter()">Increment Counter</button>
+
+ +

is slightly different in React:

+ +
<button onClick={incrementCounter}>Increment Counter</button>
+
+ +
    +
  • React provides an equivalent of each event that you might be familiar with from +standard HTML DOM. Events typing can be imported directly from react as shown in the example below:
  • +
+ +
import { MouseEvent } from "react";
+
+function HelloWorld() {
+    const sayHello = (event: MouseEvent) => {
+        event.preventDefault();
+        console.log(`${event.currentTarget.tagName}: says hello, world.`);
+    };
+
+    return (
+        <div>
+            <button onClick={sayHello}>Submit</button>
+        </div>
+    );
+}
+export default HelloWorld;
+
+ +

For the list of all the supported events refer here

+ +

Please check the list of common events and instances in which they are used here

+

+ + + React Hooks + + +

+ + +

React hooks are built-in functions which allows us to use state and other lifecycle features. The most basic hooks used by react are useState() which adds a state variable to a react component and useEffect() which is the lifecycle hook for a component. State of component refers to the data it is holding at a particular moment in time.

+

+ + + useState(): + + +

+ + +

view in sandbox

+ +

In this section we will see how we can add state to a React Component using the useState() hook provided by React. The useState hook takes the initial value of the state variable as an argument, the initial state can be any type you want (a string, a number, an array, an object or a function). Only on the first render will the initial value be assigned. Each useState call returns a two-element array. The state variable is the first element of the array, followed by a function to change the variable’s value.

+ +
    +
  1. We’ll start by defining and initializing state for the number of times the button is clicked, by adding the state variable as follows:
  2. +
+ +
import {useState} from 'react';
+
+function Counter() {
+    const [count, setCount] = useState(0);
+    ...
+}
+
+ +

This line of code looks a little strange, so let’s break it down:

+ +
    +
  • When the hook useState() is invoked, it returns an array. Where the first item is the state value, and the second item is a function that updates the state.
  • +
  • First, we import the useState from react library.
  • +
  • useState is a React function that lets us create state, passing in a default value as a parameter. In our case, we pass it a default value of 0.
  • +
  • The useState function returns an array containing two elements:
  • +
  • +
      +
    • The first array element contains the current value of state.
    • +
    +
  • +
  • +
      +
    • The second array element contains a function to set state to a different value.
    • +
    +
  • +
  • We destructure the array and store the first array element (the state value) in count, and the second array element (the function to set state) in setCount.
  • +
  • The rest of the function now has access to the count, via the count variable. The function is also able to increment the count, via the setCount variable.
  • +
+ +
    +
  1. Let’s add a function to update the state of the variable:
  2. +
+ +
function incrementCount() {
+    setCount(count + 1);
+}
+
+ +
    +
  • Now, functions to set the piece of state take in the new state as their parameter.
  • +
+ +
    +
  1. Next we’ll make the component return the count along with a button to increment the count when clicked. Final function code will be as follows:
  2. +
+ +
import { useState } from "react";
+function Counter() {
+    const [count, setCount] = useState(0);
+    function incrementCount() {
+        setCount(count + 1);
+    }
+    return (
+        <div>
+            <h1>Count: {count}</h1>
+            <button onClick={incrementCount}>Click me!</button>
+        </div>
+    );
+}
+
+export default Counter;
+
+ +

If we give this a try in the running app, we should find the count variable’s value to keep incrementing by one every time the button is clicked. +After we’ve got our heads around the code needed to define state, accessing and setting state is fairly simple and elegant.

+

+ + + Updating Arrays in State + + +

+ +

Suppose we want to maintain an array in our state object.

+
const [list, setList] = useState([]);
+
+

Now suppose we want to add an element to this list and have the component re-render to reflect the changes. We might try pushing an item to the list like so:

+
list.push(element); // doesn't work
+
+

However, React will not re-render the component as it does not detect a change in the state. This is because the object referenced in the state is the same before and after pushing the element, and so to React, the object looks the same because the reference is the same. To get around this, we must invoke the setList function to create a new reference so that React will detect the change and re-render the component. The easiest way in this case would be to use the spread operator (...) to make a copy of the existing list and push a new element to the new list:

+
const newList = [...list]; // copy the current list
+newList.push(element);
+setList(newList)
+
+

+ + + useEffect(): + + +

+ +

view in sandbox

+ +

Now let’s have a look at how to invoke the code to execute at a certain point in the component’s lifecycle. +Older versions of React consisted of different Lifecycle hooks that allowed a user to hook into various phases of component rendering such as componentDidMount, ComponentDidUpdate, etc. which have all been condensed into a single function called useEffect(). Let us observe how this hook behaves by printing out the count in browser console.

+ +
+

The new functional component system in React (moving away from class based) added amazing syntactic sugar to the React ecosystem. Use of a hook to control component lifecycle is an example such as the useEffect() hook.

+
+ +
    +
  1. As always, we will start by importing the function from the react library:
  2. +
+ +
import { useEffect } from "react";
+
+ +
    +
  1. Next we will add the function to our existing counter component:
  2. +
+ +
useEffect(() => {
+    console.log(`The current count is ${count}`);
+}, []);
+
+ +

Let’s break down this code to understand what is happening:

+ +
    +
  • We use React’s useEffect function to hook into the component life cycle.
  • +
  • The function takes in an arrow function, which executes when the component is first rendered.
  • +
  • The function takes in a second parameter, which determines when our arrow function is called. This parameter is an array of values that, when changed, will cause the arrow function to be invoked. In our case, we pass in an empty array, so our arrow function will never be called after the first render.
  • +
  • If we now try the running app and open the console, we’ll see that the log within the useEffect only appears once.
  • +
+ +
    +
  1. Let’s remove the second parameter into useEffect now:
  2. +
+ +
    +
  • If no second parameter is provided, it will cause the arrow function to be invoked first on mount and then on every re-render.
  • +
+ +
useEffect(() => {
+    console.log(`The current count is ${count}`);
+});
+
+ +
    +
  • Additionally we can also pass the variable in the array to observe only changes related to a particular value (as below).
  • +
  • Now we can see that the component will render everytime the value of count is changed.
  • +
+ +
useEffect(() => {
+    console.log(`The current count is ${count}`);
+}, [count]);
+
+ +
    +
  • We can also return a function from the useEffect hook which will be called when the component is unmounted. This is useful for cleaning up any resources that the component may have used.
  • +
+ +
useEffect(() => {
+    console.log(`The current count is ${count}`);
+    return () => {
+        console.log("The component is unmounting");
+    };
+}, [count]);
+
+ +
+

Let’s break this down:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
 useEffect pattern
Run on every renderuseEffect(() => { /* ... */ });
Run when some changesuseEffect(() => { /* ... */ }, [array of Deps]);
Run on mountuseEffect(() => { /* ... */ }, []);
Run on cleanupuseEffect(() => { return ( /* ... */ ) });
+
+ +
    +
  1. Finally, our component counter will look like:
  2. +
+ +
import { useState } from "react";
+import { useEffect } from "react";
+
+function Counter() {
+    const [count, setCount] = useState(0);
+
+    function incrementCount() {
+        setCount(count + 1);
+    }
+
+    useEffect(() => {
+        console.log(`The current count is ${count}`);
+    }, [count]);
+
+    return (
+        <div>
+            <h1>Count: {count}</h1>
+            <button onClick={incrementCount}>Click me!</button>
+        </div>
+    );
+}
+
+export default Counter;
+
+

+ + + Object Dependencies in UseEffect + + +

+ +

Consider a case where useEffect depends on an object:

+
import { useState } from "react";
+import { useEffect } from "react";
+
+function Counter() {
+    const [counter, setCounter] = useState({ count: 0, increment: 1 });
+
+    function incrementCount() {
+        counter.count += counter.increment;
+        setCounter(counter); // will not cause component to rerender
+    }
+
+  // Will not get called when incrementCount is called
+    useEffect(() => {
+        console.log(`The current count is ${counter.count}`);
+    }, [counter]);
+
+    return (
+        <div>
+            <h1>Count: {count}</h1>
+            <button onClick={incrementCount}>Click me!</button>
+        </div>
+    );
+}
+
+export default Counter;
+
+

In the example above, we’ve modified the counter to be an object containing both the current count and the amount to increment the counter by when the button is clicked.

+ +

Similar to the list case for useState, updating an attribute of an object and setting the state variable to that same object reference will not cause a re-render, and useEffect will not detect a change in its dependencies, even though we have changed one of the attributes. To properly invoke a useEffect call, we must change the object reference itself:

+ +
function Counter() {
+    const [counter, setCounter] = useState({ count: 0, increment: 1 });
+
+    function incrementCount() {
+      // we use the spread operator (...) to make a copy of the object
+        setCounter({...counter, count: counter.count + counter.increment}); 
+    }
+
+    useEffect(() => {
+        console.log(`The current count is ${counter.count}`);
+    }, [counter]);
+
+    return (
+        <div>
+            <h1>Count: {count}</h1>
+            <button onClick={incrementCount}>Click me!</button>
+        </div>
+    );
+}
+
+export default Counter;
+
+ +

The useEffect hook will now be invoked with each button click. However, our useEffect is dependent on the entire counter object, meaning that a change to any of its attributes will cause the useEffect hook to be called, which is not always a desired behavior. We can get around this by being more specific in our dependency list:

+ +
function Counter() {
+    const [counter, setCounter] = useState({ count: 0, increment: 1 });
+
+    function incrementCount() {
+      // we use the spread operator (...) to make a copy of the object
+        setCounter({...counter, count: counter.count + counter.increment}); 
+    }
+
+    function incrementIncrement() {
+      // we use the spread operator (...) to make a copy of the object
+        setCounter({...counter, increment: counter.increment + 1}); 
+    }
+
+    useEffect(() => {
+        console.log(`The current count is ${counter.count}`);
+    }, [counter.count]); // Now that we only depend on count, this function will not be called if the incrementIncrement function is called
+
+    return (
+        <div>
+            <h1>Count: {count}</h1>
+            <button onClick={incrementCount}>Click me to increment the count!</button>
+            <button onClick={incrementIncrement}>Click me to increase the increment amount!</button>
+        </div>
+    );
+}
+
+export default Counter;
+
+ + + + +
+ + +
+
+ + +

© 2024 Jon Bell, Adeel Bhutta and Mitch Wand. Released under the CC BY-SA license

+ + +
+ + +
+
+ + + +
+ + +
+ + + + +