This commit is contained in:
notmd
2023-01-25 16:02:20 +07:00
parent 31630be319
commit 97cd57a300
10 changed files with 147 additions and 213 deletions
+3
View File
@@ -0,0 +1,3 @@
.eslintrc.json
tailwind.config.js
.storybook/*
+67 -100
View File
@@ -2,8 +2,7 @@
## Purpose ## Purpose
This provides a comprehensive webapp interface for LAION's Open Assistant This provides a comprehensive webapp interface for LAION's Open Assistant project. Initially it will support:
project. Initially it will support:
1. User registration using either Discord or Email. 1. User registration using either Discord or Email.
1. Adding responses to incomplete Open Assistant tasks. 1. Adding responses to incomplete Open Assistant tasks.
@@ -11,8 +10,7 @@ project. Initially it will support:
1. Viewing an activity leaderboard. 1. Viewing an activity leaderboard.
1. Tracking community wide updates. 1. Tracking community wide updates.
This interface compliments the Discord bot and will give access to the same This interface compliments the Discord bot and will give access to the same underlying tasks.
underlying tasks.
## Contributing ## Contributing
@@ -22,67 +20,54 @@ This website is built using:
1. [npm](https://www.npmjs.com/): The node package manager for building. 1. [npm](https://www.npmjs.com/): The node package manager for building.
1. [React](https://reactjs.org/): The core frontend framework. 1. [React](https://reactjs.org/): The core frontend framework.
1. [Next.js](https://nextjs.org/): A React scaffolding framework to streamline 1. [Next.js](https://nextjs.org/): A React scaffolding framework to streamline development.
development. 1. [Prisma](https://www.prisma.io/): An ORM to interact with a web specific [Postgres](https://www.postgresql.org/)
1. [Prisma](https://www.prisma.io/): An ORM to interact with a web specific database.
[Postgres](https://www.postgresql.org/) database. 1. [NextAuth.js](https://next-auth.js.org/): A user authentication framework to ensure we handle accounts with best
1. [NextAuth.js](https://next-auth.js.org/): A user authentication framework to practices.
ensure we handle accounts with best practices. 1. [TailwindCSS](https://tailwindcss.com/): A general purpose framework for styling any component.
1. [TailwindCSS](https://tailwindcss.com/): A general purpose framework for 1. [Chakra-UI](https://chakra-ui.com/): A wide collection of pre-built UI components that generally look pretty good.
styling any component.
1. [Chakra-UI](https://chakra-ui.com/): A wide collection of pre-built UI
components that generally look pretty good.
### Set up your environment ### Set up your environment
To contribute to the website, make sure you have the following setup and To contribute to the website, make sure you have the following setup and installed:
installed:
1. [NVM](https://github.com/nvm-sh/nvm): The Node Version Manager makes it easy 1. [NVM](https://github.com/nvm-sh/nvm): The Node Version Manager makes it easy to ensure you have the right NodeJS
to ensure you have the right NodeJS version installed. Once installed, run version installed. Once installed, run `nvm use 16` to use Node 16.x. The website is known to be stable with NodeJS
`nvm use 16` to use Node 16.x. The website is known to be stable with NodeJS
version 16.x. This will install both Node and NPM. version 16.x. This will install both Node and NPM.
1. [Docker](https://www.docker.com/): We use docker to simplify running 1. [Docker](https://www.docker.com/): We use docker to simplify running dependent services.
dependent services.
### Getting everything up and running ### Getting everything up and running
If you're doing active development we suggest the following workflow: If you're doing active development we suggest the following workflow:
1. In one tab, navigate to the project root. 1. In one tab, navigate to the project root.
1. Run `docker compose up frontend-dev --build --attach-dependencies`. You can 1. Run `docker compose up frontend-dev --build --attach-dependencies`. You can optionally include `-d` to detach and
optionally include `-d` to detach and later track the logs if desired. later track the logs if desired.
1. In another tab navigate to `${OPEN_ASSISTANT_ROOT/website`. 1. In another tab navigate to `${OPEN_ASSISTANT_ROOT/website`.
1. Run `npm ci` 1. Run `npm ci`
1. Run `npx prisma db push` (This is also needed when you restart the docker 1. Run `npx prisma db push` (This is also needed when you restart the docker stack from scratch).
stack from scratch). 1. Run `npm run dev`. Now the website is up and running locally at `http://localhost:3000`.
1. Run `npm run dev`. Now the website is up and running locally at 1. To create an account, login via the user using email authentication and navigate to `http://localhost:1080`. Check
`http://localhost:3000`. the email listed and click the log in link. You're now logged in and authenticated.
1. To create an account, login via the user using email authentication and
navigate to `http://localhost:1080`. Check the email listed and click the
log in link. You're now logged in and authenticated.
### Using debug user credentials ### Using debug user credentials
You can use the debug credentials provider to log in without fancy emails or You can use the debug credentials provider to log in without fancy emails or OAuth.
OAuth.
1. This feature is automatically on in development mode, i.e. when you run 1. This feature is automatically on in development mode, i.e. when you run `npm run dev`. In case you want to do the
`npm run dev`. In case you want to do the same with a production build (for same with a production build (for example, the docker image), then run the website with environment variable
example, the docker image), then run the website with environment variable
`DEBUG_LOGIN=true`. `DEBUG_LOGIN=true`.
1. Use the `Login` button in the top right to go to the login page. 1. Use the `Login` button in the top right to go to the login page.
1. You should see a section for debug credentials. Enter any username you wish, 1. You should see a section for debug credentials. Enter any username you wish, you will be logged in as that user.
you will be logged in as that user.
### Using Storybook ### Using Storybook
To develop components using [Storybook](https://storybook.js.org/) run To develop components using [Storybook](https://storybook.js.org/) run `npm run storybook`. Then navigate to in your
`npm run storybook`. Then navigate to in your browser to browser to `http://localhost:6006`.
`http://localhost:6006`.
To create a new story create a file named `[componentName].stories.js`. An To create a new story create a file named `[componentName].stories.js`. An example how such a story could look like, see
example how such a story could look like, see `Header.stories.jsx`. `Header.stories.jsx`.
## Code Layout ## Code Layout
@@ -90,12 +75,10 @@ example how such a story could look like, see `Header.stories.jsx`.
All react code is under `src/` with a few sub directories: All react code is under `src/` with a few sub directories:
1. `pages/`: All pages a user could navigate too and API URLs which are under 1. `pages/`: All pages a user could navigate too and API URLs which are under `pages/api/`.
`pages/api/`. 1. `components/`: All re-usable React components. If something gets used twice we should create a component and put it
1. `components/`: All re-usable React components. If something gets used twice here.
we should create a component and put it here. 1. `lib/`: A generic place to store library files that are used anywhere. This doesn't have much structure yet.
1. `lib/`: A generic place to store library files that are used anywhere. This
doesn't have much structure yet.
NOTE: `styles/` can be ignored for now. NOTE: `styles/` can be ignored for now.
@@ -113,25 +96,20 @@ We're not really using CSS styles. `styles/` can be ignored.
## Testing the UI ## Testing the UI
Cypress is used for end-to-end (e2e) and component testing and is configured in Cypress is used for end-to-end (e2e) and component testing and is configured in `./cypress.config.ts`. The `./cypress`
`./cypress.config.ts`. The `./cypress` folder is used for supporting folder is used for supporting configuration files etc.
configuration files etc.
- Store e2e tests in the `./cypress/e2e` folder. - Store e2e tests in the `./cypress/e2e` folder.
- Store component tests adjacent to the component being tested. If you want to - Store component tests adjacent to the component being tested. If you want to wriite a test for
wriite a test for `./src/components/Layout.tsx` then store the test file at `./src/components/Layout.tsx` then store the test file at `./src/components/Layout.cy.tsx`.
`./src/components/Layout.cy.tsx`.
A few npm scripts are available for convenience: A few npm scripts are available for convenience:
- `npm run cypress`: Useful for development, it opens Cypress and allows you to - `npm run cypress`: Useful for development, it opens Cypress and allows you to explore, run and debug tests. It assumes
explore, run and debug tests. It assumes you have the NextJS site running at you have the NextJS site running at `localhost:3000`.
`localhost:3000`. - `npm run cypress:run`: Runs all tests. Useful for a quick sanity check before sending a PR or to run in CI pipelines.
- `npm run cypress:run`: Runs all tests. Useful for a quick sanity check before - `npm run cypress:image-baseline`: If you have tests failing because of visual changes that was expected, this command
sending a PR or to run in CI pipelines. will update the baseline images stored in `./cypress-visual-screenshots/baseline` with those from the adjacent
- `npm run cypress:image-baseline`: If you have tests failing because of visual
changes that was expected, this command will update the baseline images stored
in `./cypress-visual-screenshots/baseline` with those from the adjacent
comparison folder. More can be found in the comparison folder. More can be found in the
[docs of `uktrade/cypress-image-diff`](https://github.com/uktrade/cypress-image-diff/blob/main/docs/CLI.md#update-all-baseline-images-for-failing-tests). [docs of `uktrade/cypress-image-diff`](https://github.com/uktrade/cypress-image-diff/blob/main/docs/CLI.md#update-all-baseline-images-for-failing-tests).
@@ -141,10 +119,9 @@ Read more in the [./cypress README](cypress/).
Jest and React Testing Library are used for unit testing JS/TS/TSX code. Jest and React Testing Library are used for unit testing JS/TS/TSX code.
- Store unit test files adjacent to the file being tested and have the filename - Store unit test files adjacent to the file being tested and have the filename end with `.test.ts` for non-React code
end with `.test.ts` for non-React code or `.test.tsx` for React code. or `.test.tsx` for React code.
- `npm run jest`: automatically runs tests and watches for any relevant changes - `npm run jest`: automatically runs tests and watches for any relevant changes to rerun tests.
to rerun tests.
Read more in the [./src/README.md](src/README.md). Read more in the [./src/README.md](src/README.md).
@@ -152,30 +129,25 @@ Read more in the [./src/README.md](src/README.md).
When writing code for the website, we have a few best practices: When writing code for the website, we have a few best practices:
1. When importing packages import external dependencies first then local 1. When importing packages import external dependencies first then local dependencies. Order them alphabetically
dependencies. Order them alphabetically according to the package name. according to the package name.
1. When trying to implement something new, check if 1. When trying to implement something new, check if [Chakra-UI](https://chakra-ui.com/) has components that are close
[Chakra-UI](https://chakra-ui.com/) has components that are close enough to enough to your need. For example Sliders, Radio Buttons, Progress indicators, etc. They have a lot and we can save
your need. For example Sliders, Radio Buttons, Progress indicators, etc. time by re-using what they have and tweaking the style as needed.
They have a lot and we can save time by re-using what they have and tweaking 1. Format everything with [Prettier](https://prettier.io/). This is done by default with pre-submits. We currently
the style as needed. don't have any custom settings.
1. Format everything with [Prettier](https://prettier.io/). This is done by 1. Define functional React components (with types for all properties when feasible).
default with pre-submits. We currently don't have any custom settings.
1. Define functional React components (with types for all properties when
feasible).
### Developing New Features ### Developing New Features
When working on new features or making significant changes that can't be done When working on new features or making significant changes that can't be done within a single Pull Request, we ask that
within a single Pull Request, we ask that you make use of Feature Flags. you make use of Feature Flags.
We've set up We've set up [`react-feature-flags`](https://www.npmjs.com/package/react-feature-flags) to make this easier. To get
[`react-feature-flags`](https://www.npmjs.com/package/react-feature-flags) to started:
make this easier. To get started:
1. Add a new flag entry to `website/src/flags.ts`. We have an example flag you 1. Add a new flag entry to `website/src/flags.ts`. We have an example flag you can copy as an example. Be sure to
can copy as an example. Be sure to `isActive` to true when testing your `isActive` to true when testing your features but false when submitting your PR.
features but false when submitting your PR.
1. Use your flag wherever you add a new UI element. This can be done with: 1. Use your flag wherever you add a new UI element. This can be done with:
```js ```js
@@ -188,29 +160,24 @@ import { Flags } from "react-feature-flags";
You can see an example of how this works by checking `website/src/components/Header/Headers.tsx` where we use `flagTest`. You can see an example of how this works by checking `website/src/components/Header/Headers.tsx` where we use `flagTest`.
1. Once you've finished building out the feature and it is ready for everyone 1. Once you've finished building out the feature and it is ready for everyone to use, it's safe to remove the `Flag`
to use, it's safe to remove the `Flag` wrappers around your component and wrappers around your component and the entry in `flags.ts`.
the entry in `flags.ts`.
### URL Paths ### URL Paths
To use stable and consistent URL paths, we recommend the following strategy for To use stable and consistent URL paths, we recommend the following strategy for new tasks:
new tasks:
1. For any task that involves writing a free-form response, put the page under 1. For any task that involves writing a free-form response, put the page under `website/src/pages/create` with a page
`website/src/pages/create` with a page name matching the task type, such as name matching the task type, such as `initial_prompt.tsx`.
`initial_prompt.tsx`. 1. For any task that evaluates, rates, or ranks content, put the page under `website/src/pages/evaluate` with a page
1. For any task that evaluates, rates, or ranks content, put the page under name matching the task type such as `rank_initial_prompts.tsx`.
`website/src/pages/evaluate` with a page name matching the task type such as
`rank_initial_prompts.tsx`.
With this we'll be able to ensure these contribution pages are hidden from With this we'll be able to ensure these contribution pages are hidden from logged out users but accessible to logged in
logged out users but accessible to logged in users. users.
## Learn More ## Learn More
To learn more about Next.js, take a look at the following resources: To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+40 -58
View File
@@ -1,24 +1,19 @@
# Component and e2e testing with Cypress # Component and e2e testing with Cypress
[Cypress](https://www.cypress.io/) is used for both component- and end-to-end [Cypress](https://www.cypress.io/) is used for both component- and end-to-end testing. Below there's a few examples for
testing. Below there's a few examples for the context of this site. To learn the context of this site. To learn more, the
more, the [Cypress documentation](https://docs.cypress.io/guides/getting-started/opening-the-app) has it all.
[Cypress documentation](https://docs.cypress.io/guides/getting-started/opening-the-app)
has it all.
Don't get scared by the commercial offerings they offer. Their core is open Don't get scared by the commercial offerings they offer. Their core is open source, the cloud offering is not necesarry
source, the cloud offering is not necesarry at all and can be replaced by CI at all and can be replaced by CI tooling and [community efforts](https://sorry-cypress.dev/).
tooling and [community efforts](https://sorry-cypress.dev/).
# Component testing # Component testing
To write a new component test, you either create a new `.tsx` adjacent to the To write a new component test, you either create a new `.tsx` adjacent to the component you want to test or you can use
component you want to test or you can use the guide presented yo you when the guide presented yo you when running `npm run cypress` which allows you to easily create the skeleton test for an
running `npm run cypress` which allows you to easily create the skeleton test existing component.
for an existing component.
If you have a `Button.tsx` component, create a file next to it called If you have a `Button.tsx` component, create a file next to it called `Button.cy.tsx` which could look like this:
`Button.cy.tsx` which could look like this:
```typescript ```typescript
import React from "react"; import React from "react";
@@ -35,28 +30,24 @@ describe("<Button />", () => {
## What's happening here? ## What's happening here?
First we use `cy.mount` to mount our component under test. Notive how we specify First we use `cy.mount` to mount our component under test. Notive how we specify `className` and inner text - this is
`className` and inner text - this is where we arrange our component with fake where we arrange our component with fake data that we could assert on later.
data that we could assert on later.
In the example above, we also use `cy.get` to select the rendered `button` In the example above, we also use `cy.get` to select the rendered `button` element. Cypress has multiple ways to
element. Cypress has multiple ways to [select elements](https://docs.cypress.io/guides/references/best-practices), `get` is just one of them (and often not
[select elements](https://docs.cypress.io/guides/references/best-practices), recommended).
`get` is just one of them (and often not recommended).
At last, we use `captureSnapshot` which is a plugin that snaps a photo of the At last, we use `captureSnapshot` which is a plugin that snaps a photo of the `button` element and compares it to a
`button` element and compares it to a baseline located in the baseline located in the `./cypress-visual-screenshots/baseline/` folder. If there's too many unidentical pixels between
`./cypress-visual-screenshots/baseline/` folder. If there's too many unidentical the two, it will fail the test.
pixels between the two, it will fail the test.
# End-to-end (e2e) testing # End-to-end (e2e) testing
e2e tests are stored in the `./cypress/e2e` folder and should be named e2e tests are stored in the `./cypress/e2e` folder and should be named `{page}.cy.ts` and located in a relative folder
`{page}.cy.ts` and located in a relative folder structure that mirrors the page structure that mirrors the page under test.
under test.
When running `npm run cypress` and selecting e2e testing, we assume you have the When running `npm run cypress` and selecting e2e testing, we assume you have the NextJS site running at
NextJS site running at `localhost:3000`. `localhost:3000`.
An example test could look as follows: An example test could look as follows:
@@ -74,39 +65,33 @@ export {};
## What's happening here? ## What's happening here?
First we use [`cy.visit`](https://docs.cypress.io/api/commands/visit) to point First we use [`cy.visit`](https://docs.cypress.io/api/commands/visit) to point the browser at the desired page. It
the browser at the desired page. It appends relative paths to the configured appends relative paths to the configured `baseUrl` (found in `./cypress.config.ts`).
`baseUrl` (found in `./cypress.config.ts`).
Cypress will Cypress will [automatically await](https://docs.cypress.io/guides/core-concepts/introduction-to-cypress#Timeouts) almost
[automatically await](https://docs.cypress.io/guides/core-concepts/introduction-to-cypress#Timeouts) anything you do, but fail if the default timeout is reached.
almost anything you do, but fail if the default timeout is reached.
Then we get the email input field and type our email address. We find the input Then we get the email input field and type our email address. We find the input field using the data-cy attribute that
field using the data-cy attribute that we added in the source code of the we added in the source code of the element on the page.
element on the page.
```jsx ```jsx
<Input data-cy="email-address" placeholder="Email Address" /> <Input data-cy="email-address" placeholder="Email Address" />
``` ```
Using `data-cy` is how we ensure that selecting the element is robust to changes Using `data-cy` is how we ensure that selecting the element is robust to changes in page design or function and is one
in page design or function and is one of the of the
[best practices recommended by Cypress](https://docs.cypress.io/guides/references/best-practices#Selecting-Elements). [best practices recommended by Cypress](https://docs.cypress.io/guides/references/best-practices#Selecting-Elements).
Next we call `type()` to use the keyboard, cypress will automatically focus the Next we call `type()` to use the keyboard, cypress will automatically focus the element and send the keypress events.
element and send the keypress events. Notice the `{enter}` keyword, this will Notice the `{enter}` keyword, this will cause Cypress to hit the return key which we expect to submit the form.
cause Cypress to hit the return key which we expect to submit the form.
We then assert that the URL should contain `/auth/verify`. Again the timeout We then assert that the URL should contain `/auth/verify`. Again the timeout will make sure we are not waiting forever,
will make sure we are not waiting forever, and the test will fail if we do not and the test will fail if we do not manage to get there in a reasonable time.
manage to get there in a reasonable time.
## Authenticating in e2e tests ## Authenticating in e2e tests
For end-to-end tests almost every test will need to first sign in to the For end-to-end tests almost every test will need to first sign in to the website. To make this easier we have a custom
website. To make this easier we have a custom command for Cypress that makes command for Cypress that makes logging in with an email address a single command, `cy.signInWithEmail()`.
logging in with an email address a single command, `cy.signInWithEmail()`.
```typescript ```typescript
describe("replying as the assistant", () => { describe("replying as the assistant", () => {
@@ -115,16 +100,13 @@ describe("replying as the assistant", () => {
cy.visit("/create/assistant_reply"); cy.visit("/create/assistant_reply");
cy.get('[data-cy="reply"').type( cy.get('[data-cy="reply"').type("You need to run pre-commit to make the reviewer happy.");
"You need to run pre-commit to make the reviewer happy."
);
cy.get('[data-cy="submit"]').click(); cy.get('[data-cy="submit"]').click();
}); });
}); });
``` ```
In this example we sign in as `cypress@example.com` before visiting the In this example we sign in as `cypress@example.com` before visiting the `/create/assistant_reply` page that is only
`/create/assistant_reply` page that is only available when authenticated. We can available when authenticated. We can then continue on with our test as normal. Note: using `cy.signInWithEmail()`
then continue on with our test as normal. Note: using `cy.signInWithEmail()` requires that the maildev is running, which should have been started as part of the `docker compose up` command that is
requires that the maildev is running, which should have been started as part of required to do any end-to-end testing.
the `docker compose up` command that is required to do any end-to-end testing.
+1 -4
View File
@@ -7,9 +7,6 @@ describe("<Container />", () => {
const className = "my-class"; const className = "my-class";
const text = "test_container"; const text = "test_container";
cy.mount(<Container className={className}>{text}</Container>); cy.mount(<Container className={className}>{text}</Container>);
cy.get(`div.${className}`) cy.get(`div.${className}`).should("have.class", className).should("be.visible").should("contain", text);
.should("have.class", className)
.should("be.visible")
.should("contain", text);
}); });
}); });
@@ -17,7 +17,7 @@ describe("Contract test for Oasst API", function () {
it("can ack a task", async () => { it("can ack a task", async () => {
const task = await oasstApiClient.fetchTask("random", testUser, "en"); const task = await oasstApiClient.fetchTask("random", testUser, "en");
expect(await oasstApiClient.ackTask(task.id, "321")).to.be.undefined; expect(await oasstApiClient.ackTask(task.id, "321")).to.be.null;
}); });
it("can record a taskInteraction", async () => { it("can record a taskInteraction", async () => {
+1 -3
View File
@@ -56,9 +56,7 @@ describe("handles random tasks", () => {
break; break;
} }
case undefined: { case undefined: {
throw new Error( throw new Error("No tasks available, but at least create initial prompt expected");
"No tasks available, but at least create initial prompt expected"
);
} }
default: default:
throw new Error(`Unexpected task type: ${type}`); throw new Error(`Unexpected task type: ${type}`);
+5 -10
View File
@@ -37,19 +37,14 @@
// } // }
Cypress.Commands.add("signInUsingEmailedLink", (emailAddress) => { Cypress.Commands.add("signInUsingEmailedLink", (emailAddress) => {
const mailDevApi = `${Cypress.env("MAILDEV_PROTOCOL")}://${Cypress.env( const mailDevApi = `${Cypress.env("MAILDEV_PROTOCOL")}://${Cypress.env("MAILDEV_HOST")}:${Cypress.env(
"MAILDEV_HOST" "MAILDEV_API_PORT"
)}:${Cypress.env("MAILDEV_API_PORT")}`; )}`;
cy.request( cy.request("GET", `${mailDevApi}/email?headers.to=${emailAddress.toLowerCase()}`).then((response) => {
"GET",
`${mailDevApi}/email?headers.to=${emailAddress.toLowerCase()}`
).then((response) => {
const emails = response.body; const emails = response.body;
// Find and use login link // Find and use login link
const loginLink = emails const loginLink = emails.pop().html.match(/href="[^"]+(\/api\/auth\/callback\/[^"]+?)"/)[1];
.pop()
.html.match(/href="[^"]+(\/api\/auth\/callback\/[^"]+?)"/)[1];
cy.visit(loginLink); cy.visit(loginLink);
}); });
}); });
+4 -4
View File
@@ -34,14 +34,14 @@ export class OasstApiClient {
}); });
} }
async ackTask(taskId: string, messageId: string): Promise<void> { async ackTask(taskId: string, messageId: string): Promise<null> {
await this.post(`/api/v1/tasks/${taskId}/ack`, { return this.post(`/api/v1/tasks/${taskId}/ack`, {
message_id: messageId, message_id: messageId,
}); });
} }
async nackTask(taskId: string, reason: string): Promise<void> { async nackTask(taskId: string, reason: string): Promise<null> {
await this.post(`/api/v1/tasks/${taskId}/nack`, { return this.post(`/api/v1/tasks/${taskId}/nack`, {
reason, reason,
}); });
} }
+24 -28
View File
@@ -1,45 +1,41 @@
.App { .App {
text-align: center; text-align: center;
} }
.App-logo { .App-logo {
height: 40vmin; height: 40vmin;
pointer-events: none; pointer-events: none;
} }
@media (prefers-reduced-motion: no-preference) { @media (prefers-reduced-motion: no-preference) {
.App-logo { .App-logo {
animation: App-logo-spin infinite 20s linear; animation: App-logo-spin infinite 20s linear;
} }
} }
.AppHeader { .AppHeader {
background: linear-gradient( background: linear-gradient(217deg, rgba(255, 0, 0, 0.8), rgba(255, 0, 0, 0) 70.71%),
217deg, linear-gradient(127deg, rgba(0, 255, 0, 0.8), rgba(0, 255, 0, 0) 70.71%),
rgba(255, 0, 0, 0.8), linear-gradient(336deg, rgba(0, 0, 255, 0.8), rgba(0, 0, 255, 0) 70.71%);
rgba(255, 0, 0, 0) 70.71% background: black;
), min-height: 100vh;
linear-gradient(127deg, rgba(0, 255, 0, 0.8), rgba(0, 255, 0, 0) 70.71%), display: flex;
linear-gradient(336deg, rgba(0, 0, 255, 0.8), rgba(0, 0, 255, 0) 70.71%); flex-direction: column;
background: black; align-items: center;
min-height: 100vh; justify-content: center;
display: flex; font-size: calc(10px + 2vmin);
flex-direction: column; color: white;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
} }
.AppLink { .AppLink {
color: #61dafb; color: #61dafb;
} }
@keyframes App-logo-spin { @keyframes App-logo-spin {
from { from {
transform: rotate(0deg); transform: rotate(0deg);
} }
to { to {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
+1 -5
View File
@@ -1,8 +1,4 @@
import { import { type ThemeConfig, extendTheme, usePrefersReducedMotion } from "@chakra-ui/react";
type ThemeConfig,
extendTheme,
usePrefersReducedMotion,
} from "@chakra-ui/react";
import { containerTheme } from "./Components/Container"; import { containerTheme } from "./Components/Container";
import { StyleFunctionProps, Styles } from "@chakra-ui/theme-tools"; import { StyleFunctionProps, Styles } from "@chakra-ui/theme-tools";