├── app
├── favicon.ico
├── globals.css
├── CardTokens.stylex.ts
├── components
│ ├── ButtonTokens.stylex.ts
│ ├── ThemeableButton.tsx
│ ├── ButtonsDemo.tsx
│ └── Counter.tsx
├── about
│ ├── layout.tsx
│ └── page.mdx
├── layout.tsx
├── Card.tsx
├── page.tsx
└── globalTokens.stylex.ts
├── .eslintrc.js
├── next.config.js
├── .babelrc.js
├── .gitignore
├── public
├── vercel.svg
└── next.svg
├── __tests__
└── button.test.tsx
├── tsconfig.json
├── package.json
├── README.md
└── mdx-components.tsx
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nmn/nextjs-app-dir-stylex/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | padding: 0;
4 | margin: 0;
5 | }
6 |
--------------------------------------------------------------------------------
/app/CardTokens.stylex.ts:
--------------------------------------------------------------------------------
1 | import * as stylex from "@stylexjs/stylex";
2 |
3 | export const tokens = stylex.defineVars({
4 | arrowTransform: "translateX(0)",
5 | });
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: "next/core-web-vitals",
3 | plugins: ["@stylexjs"],
4 | rules: {
5 | // The Eslint rule still needs work, but you can
6 | // enable it to test things out.
7 | "@stylexjs/valid-styles": "error",
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/app/components/ButtonTokens.stylex.ts:
--------------------------------------------------------------------------------
1 | import * as stylex from "@stylexjs/stylex";
2 |
3 | export const buttonTokens = stylex.defineVars({
4 | bgColor: "blue",
5 | textColor: "white",
6 | cornerRadius: "4px",
7 | paddingBlock: "4px",
8 | paddingInline: "8px",
9 | });
10 |
--------------------------------------------------------------------------------
/app/about/layout.tsx:
--------------------------------------------------------------------------------
1 | import * as stylex from "@stylexjs/stylex";
2 |
3 | export default function RootLayout({
4 | children,
5 | }: {
6 | children: React.ReactNode;
7 | }) {
8 | return
{children}
;
9 | }
10 |
11 | const styles = stylex.create({
12 | container: {
13 | width: "100%",
14 | maxWidth: 768,
15 | marginInline: "auto",
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const stylexPlugin = require("@stylexjs/nextjs-plugin");
3 | const withMDX = require("@next/mdx")();
4 |
5 | const nextConfig = {
6 | // Configure `pageExtensions` to include MDX files
7 | pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"],
8 | transpilePackages: ["@stylexjs/open-props"],
9 | // Optionally, add any other Next.js config below
10 | };
11 |
12 | module.exports = stylexPlugin({
13 | rootDir: __dirname,
14 | })(withMDX(nextConfig));
15 |
--------------------------------------------------------------------------------
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["next/babel"],
3 | plugins: [
4 | [
5 | "@stylexjs/babel-plugin",
6 | {
7 | dev: process.env.NODE_ENV === "development",
8 | test: process.env.NODE_ENV === "test",
9 | runtimeInjection: false,
10 | genConditionalClasses: true,
11 | treeshakeCompensation: true,
12 | unstable_moduleResolution: {
13 | type: "commonJS",
14 | rootDir: __dirname,
15 | },
16 | },
17 | ],
18 | ],
19 | };
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/app/about/page.mdx:
--------------------------------------------------------------------------------
1 | import Card from "../Card";
2 |
3 | # Welcome to my MDX page!
4 |
5 | ## H2 level right Here
6 |
7 | ### this is a level 3
8 |
9 | This is some **bold** and _italics_ text.
10 |
11 | This is a list in markdown:
12 |
13 | - One
14 | - One and a half
15 | - One and three quarters
16 | - Two
17 | - Three
18 |
19 | Checkout my React component:
20 |
21 |
26 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/__tests__/button.test.tsx:
--------------------------------------------------------------------------------
1 | import * as stylex from "@stylexjs/stylex";
2 |
3 | import ThemeableButton from "../app/components/ThemeableButton";
4 | import renderer from "react-test-renderer";
5 | import React from "react";
6 |
7 | describe("ThemeableButton", () => {
8 | it("renders default state", () => {
9 | const btn = renderer
10 | .create( {}}>ClickMe)
11 | .toJSON();
12 |
13 | expect(btn).toMatchInlineSnapshot(`
14 |
20 | `);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "types": ["jest"],
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "strict": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noEmit": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve",
17 | "incremental": true,
18 | "plugins": [
19 | {
20 | "name": "next"
21 | }
22 | ],
23 | "paths": {
24 | "@/*": ["./*"]
25 | }
26 | },
27 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/layout.js"],
28 | "exclude": ["node_modules"]
29 | }
30 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./globals.css";
2 |
3 | import { globalTokens as $ } from "./globalTokens.stylex";
4 | import * as stylex from "@stylexjs/stylex";
5 |
6 | export const metadata = {
7 | title: "Create Next App",
8 | description: "Generated by create next app",
9 | };
10 |
11 | export default function RootLayout({
12 | children,
13 | }: {
14 | children: React.ReactNode;
15 | }) {
16 | return (
17 |
18 | {children}
19 |
20 | );
21 | }
22 |
23 | const DARK = "@media (prefers-color-scheme: dark)";
24 | const fgColor = `rgba(${$.foregroundR}, ${$.foregroundG}, ${$.foregroundB}, 1)`;
25 |
26 | const styles = stylex.create({
27 | html: {
28 | colorScheme: "light dark",
29 | },
30 | reset: {
31 | minHeight: "100%",
32 | margin: 0,
33 | padding: 0,
34 | },
35 | body: {
36 | color: fgColor,
37 | backgroundImage: {
38 | default: "linear-gradient(to bottom, rgb(214, 219, 220), white)",
39 | [DARK]: "linear-gradient(to bottom, rgb(20, 22, 27), black)",
40 | },
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-app-dir-test",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "test": "jest",
7 | "predev": "rimraf .next",
8 | "prebuild": "rimraf .next",
9 | "dev": "next dev",
10 | "build": "next build",
11 | "start": "next start",
12 | "lint": "next lint"
13 | },
14 | "dependencies": {
15 | "@mdx-js/loader": "^3.0.0",
16 | "@mdx-js/react": "^3.0.0",
17 | "@next/mdx": "^14.0.3",
18 | "@stylexjs/open-props": "^0.4.1",
19 | "@stylexjs/stylex": "^0.4.1",
20 | "@types/mdx": "^2.0.10",
21 | "bright": "^0.8.4",
22 | "next": "14.0.1",
23 | "react": "18.2.0",
24 | "react-dom": "18.2.0"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "^7.23.2",
28 | "@babel/plugin-syntax-flow": "^7.22.5",
29 | "@babel/plugin-syntax-jsx": "^7.22.5",
30 | "@babel/plugin-syntax-typescript": "^7.22.5",
31 | "@stylexjs/babel-plugin": "^0.4.1",
32 | "@stylexjs/eslint-plugin": "^0.4.1",
33 | "@stylexjs/nextjs-plugin": "^0.4.1",
34 | "@testing-library/react": "^14.1.2",
35 | "@types/jest": "^29.5.11",
36 | "@types/node": "20.8.10",
37 | "@types/react": "18.2.20",
38 | "@types/react-dom": "18.2.7",
39 | "@types/react-test-renderer": "^18.0.7",
40 | "eslint": "8.52.0",
41 | "eslint-config-next": "14.0.1",
42 | "jest": "^30.0.0-alpha.1",
43 | "prettier": "^3.1.1",
44 | "react-test-renderer": "^18.2.0",
45 | "rimraf": "^5.0.5",
46 | "ts_dependency_graph": "^2.0.0",
47 | "typescript": "5.2.2"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/components/ThemeableButton.tsx:
--------------------------------------------------------------------------------
1 | import * as stylex from "@stylexjs/stylex";
2 | import type { StyleXStyles, Theme } from "@stylexjs/stylex/lib/StyleXTypes";
3 |
4 | import "./ButtonTokens.stylex";
5 | import { buttonTokens } from "./ButtonTokens.stylex";
6 |
7 | type Props = Readonly<{
8 | onClick: () => void;
9 | children: React.ReactNode;
10 | // for Overrides
11 | style?: StyleXStyles;
12 | theme?: Theme;
13 | variant?: "primary" | "danger";
14 | em?: boolean;
15 | }>;
16 |
17 | export default function Card({
18 | onClick,
19 | children,
20 | style,
21 | theme,
22 | variant,
23 | em = false,
24 | }: Props) {
25 | return (
26 |
38 | );
39 | }
40 |
41 | const styles = stylex.create({
42 | base: {
43 | appearance: "none",
44 | borderWidth: 0,
45 | borderStyle: "none",
46 | backgroundColor: buttonTokens.bgColor,
47 | color: buttonTokens.textColor,
48 | borderRadius: buttonTokens.cornerRadius,
49 | paddingBlock: buttonTokens.paddingBlock,
50 | paddingInline: buttonTokens.paddingInline,
51 | },
52 | emphasise: {
53 | transform: "scale(1.1)",
54 | },
55 | });
56 |
57 | const variantStyles = stylex.create({
58 | danger: {
59 | backgroundColor: "red",
60 | color: "white",
61 | },
62 | primary: {
63 | fontWeight: "bold",
64 | },
65 | });
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | ```
14 |
15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16 |
17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
18 |
19 | [http://localhost:3000/api/hello](http://localhost:3000/api/hello) is an endpoint that uses [Route Handlers](https://beta.nextjs.org/docs/routing/route-handlers). This endpoint can be edited in `app/api/hello/route.ts`.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/app/components/ButtonsDemo.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as stylex from "@stylexjs/stylex";
4 | import { buttonTokens } from "./ButtonTokens.stylex";
5 | import ThemeableButton from "./ThemeableButton";
6 |
7 | type Props = Readonly<{}>;
8 |
9 | export default function ButtonsDemo(_props: Props) {
10 | const onClick = () => {
11 | console.log("click");
12 | };
13 | return (
14 |
15 |
Vanilla Button
16 |
17 |
18 | Bordered Button
19 |
20 |
21 |
22 | Red Button
23 |
24 |
25 |
26 |
27 | Red Button By inheritance
28 |
29 |
30 |
31 |
36 | Red - Bordered Button
37 |
38 |
39 | );
40 | }
41 |
42 | const redTheme = stylex.createTheme(buttonTokens, {
43 | bgColor: "red",
44 | textColor: "white",
45 | cornerRadius: "4px",
46 | paddingBlock: "4px",
47 | paddingInline: "8px",
48 | });
49 |
50 | const styles = stylex.create({
51 | container: {
52 | display: "flex",
53 | flexDirection: "column",
54 | alignItems: "center",
55 | justifyContent: "center",
56 | gap: 16,
57 | paddingBottom: 64,
58 | },
59 | bordered: {
60 | borderWidth: 2,
61 | borderStyle: "solid",
62 | borderColor: "red",
63 | },
64 | greenBorder: {
65 | borderColor: "green",
66 | },
67 | });
68 |
--------------------------------------------------------------------------------
/mdx-components.tsx:
--------------------------------------------------------------------------------
1 | import type { MDXComponents } from "mdx/types";
2 |
3 | import * as stylex from "@stylexjs/stylex";
4 | import { globalTokens as $, spacing, text } from "./app/globalTokens.stylex";
5 | import { colors } from "@stylexjs/open-props/lib/colors.stylex";
6 |
7 | const MOBILE = "@media (max-width: 700px)";
8 | const styles = stylex.create({
9 | base: {
10 | fontFamily: $.fontSans,
11 | padding: 0,
12 | margin: 0,
13 | marginTop: "0.5em",
14 | },
15 | h1: {
16 | color: colors.blue3,
17 | fontSize: text.h3,
18 | fontWeight: 200,
19 | },
20 | h2: {
21 | fontSize: text.h4,
22 | fontWeight: 400,
23 | },
24 | h3: {
25 | fontSize: text.h5,
26 | fontWeight: 600,
27 | },
28 | p: {
29 | marginTop: "1em",
30 | fontSize: text.p,
31 | },
32 | li: {
33 | marginInlineStart: "1.1em",
34 | },
35 | });
36 |
37 | function H1(props: any) {
38 | return ;
39 | }
40 |
41 | function H2(props: any) {
42 | return ;
43 | }
44 |
45 | function H3(props: any) {
46 | return ;
47 | }
48 |
49 | function P(props: any) {
50 | return ;
51 | }
52 |
53 | function Ul(props: any) {
54 | return ;
55 | }
56 | function Ol(props: any) {
57 | return
;
58 | }
59 |
60 | function Li(props: any) {
61 | return ;
62 | }
63 |
64 | export function useMDXComponents(components: MDXComponents): MDXComponents {
65 | return {
66 | h1: H1,
67 | h2: H2,
68 | h3: H3,
69 | p: P,
70 | ul: Ul,
71 | ol: Ol,
72 | li: Li,
73 | ...components,
74 | };
75 | }
76 |
--------------------------------------------------------------------------------
/app/components/Counter.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | *
7 | *
8 | */
9 |
10 | "use client";
11 |
12 | import * as stylex from "@stylexjs/stylex";
13 | import { spacing, text, globalTokens as $ } from "../globalTokens.stylex";
14 | import { colors } from "@stylexjs/open-props/lib/colors.stylex";
15 | import { shadows } from "@stylexjs/open-props/lib/shadows.stylex";
16 | import { useState } from "react";
17 |
18 | export default function Counter() {
19 | const [count, setCount] = useState(0);
20 |
21 | return (
22 |
23 |
29 |
99 || count < -9) && styles.largeNumber
33 | )}
34 | >
35 | {count}
36 |
37 |
43 |
44 | );
45 | }
46 |
47 | const DARK = "@media (prefers-color-scheme: dark)" as const;
48 |
49 | const styles = stylex.create({
50 | container: {
51 | display: "flex",
52 | alignItems: "center",
53 | justifyContent: "center",
54 | flexDirection: "row",
55 | borderRadius: spacing.md,
56 | borderWidth: 1,
57 | borderStyle: "solid",
58 | borderColor: colors.blue7,
59 | padding: spacing.xxxs,
60 | fontFamily: $.fontSans,
61 | gap: spacing.xs,
62 | boxShadow: shadows.shadow6,
63 | },
64 | button: {
65 | display: "flex",
66 | alignItems: "center",
67 | justifyContent: "center",
68 | height: "6rem",
69 | aspectRatio: 1,
70 | color: colors.blue7,
71 | backgroundColor: {
72 | default: colors.gray3,
73 | ":hover": colors.gray4,
74 | [DARK]: {
75 | default: colors.gray9,
76 | ":hover": colors.gray8,
77 | },
78 | },
79 | borderWidth: 0,
80 | borderStyle: "none",
81 | borderRadius: spacing.xs,
82 | padding: spacing.xs,
83 | margin: spacing.xs,
84 | cursor: "pointer",
85 | fontSize: text.h2,
86 | transform: {
87 | default: null,
88 | ":hover": "scale(1.025)",
89 | ":active": "scale(0.975)",
90 | },
91 | },
92 | count: {
93 | fontSize: text.h2,
94 | fontWeight: 100,
95 | color: colors.lime7,
96 | minWidth: "6rem",
97 | textAlign: "center",
98 | fontFamily: $.fontMono,
99 | },
100 | largeNumber: {
101 | fontSize: text.h3,
102 | },
103 | });
104 |
--------------------------------------------------------------------------------
/app/Card.tsx:
--------------------------------------------------------------------------------
1 | import * as stylex from "@stylexjs/stylex";
2 | import { globalTokens as $, spacing, text } from "./globalTokens.stylex";
3 | import { colors } from "@stylexjs/open-props/lib/colors.stylex";
4 | import { tokens } from "./CardTokens.stylex";
5 |
6 | type Props = Readonly<{
7 | title: string;
8 | body: string;
9 | href: string;
10 | }>;
11 |
12 | export default function Card({ title, body, href }: Props) {
13 | return (
14 |
20 |
21 | {title} →
22 |
23 | {body}
24 |
25 | );
26 | }
27 |
28 | type TMobile = "@media (max-width: 700px)";
29 |
30 | const MOBILE = "@media (max-width: 700px)" satisfies TMobile;
31 | const REDUCE_MOTION = "@media (prefers-reduced-motion: reduce)";
32 |
33 | const cardBgTransparent = `rgba(${$.cardR}, ${$.cardG}, ${$.cardB}, 0)`;
34 | const CardBgTranslucent = `rgba(${$.cardR}, ${$.cardG}, ${$.cardB}, 0.1)`;
35 | const cardBorderTransparent = `rgba(${$.cardBorderR}, ${$.cardBorderG}, ${$.cardBorderB}, 0)`;
36 | const cardBorderHover = `rgba(${$.cardBorderR}, ${$.cardBorderG}, ${$.cardBorderB}, 0.1)`;
37 |
38 | const styles = stylex.create({
39 | link: {
40 | display: {
41 | default: "flex",
42 | [MOBILE]: "block",
43 | },
44 | alignItems: "center",
45 | justifyContent: "flex-start",
46 | flexDirection: "column",
47 | borderRadius: spacing.xs,
48 | backgroundColor: {
49 | default: cardBgTransparent,
50 | ":hover": CardBgTranslucent,
51 | },
52 | borderWidth: 1,
53 | borderStyle: "solid",
54 | borderColor: {
55 | default: cardBorderTransparent,
56 | ":hover": cardBorderHover,
57 | },
58 | color: "inherit",
59 | fontFamily: $.fontSans,
60 | padding: spacing.sm,
61 | transitionProperty: "background-color, border-color",
62 | transitionDuration: "400ms",
63 | [tokens.arrowTransform]: {
64 | // eslint-disable-next-line @stylexjs/valid-styles
65 | default: "translateX(0)",
66 | // eslint-disable-next-line @stylexjs/valid-styles
67 | ":hover": "translateX(4px)",
68 | },
69 | textAlign: "center",
70 | textDecoration: "none",
71 | },
72 | h2: {
73 | color: colors.blue3,
74 | fontSize: text.h4,
75 | fontWeight: 600,
76 | marginBottom: {
77 | default: spacing.xs,
78 | [MOBILE]: spacing.xxs,
79 | },
80 | },
81 | span: {
82 | display: "inline-block",
83 | transitionProperty: "transform",
84 | transitionDuration: {
85 | default: "200ms",
86 | [REDUCE_MOTION]: "0s",
87 | },
88 | transform: tokens.arrowTransform,
89 | },
90 | p: {
91 | margin: 0,
92 | opacity: 0.6,
93 | fontSize: text.p,
94 | textWrap: "balance",
95 | lineHeight: 1.5,
96 | maxWidth: "30ch",
97 | },
98 | dynamic: (color: string) => ({
99 | color: color,
100 | }),
101 | });
102 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import * as stylex from "@stylexjs/stylex";
2 | import Card from "./Card";
3 | import { globalTokens as $, spacing, text } from "./globalTokens.stylex";
4 | import ButtonsDemo from "./components/ButtonsDemo";
5 | import Counter from "./components/Counter";
6 |
7 | const HOMEPAGE = "http://stylex-docusaurus.vercel.app";
8 |
9 | export default function Home() {
10 | return (
11 |
12 |
13 |
14 | Get started by editing
15 | app/page.tsx
16 |
17 |
18 |
19 |
20 | Next.js App Dir♥️️StyleX
21 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
37 |
42 |
47 |
48 |
49 | );
50 | }
51 |
52 | const MEDIA_MOBILE: "@media (max-width: 700px)" = "@media (max-width: 700px)";
53 | const MEDIA_TABLET: "@media (min-width: 701px) and (max-width: 1120px)" =
54 | "@media (min-width: 701px) and (max-width: 1120px)";
55 |
56 | const bgImage = `linear-gradient(to bottom, ${$.bgStartRGB}, ${$.calloutRGB50})`;
57 | const xBorderColor = `rgba(${$.calloutBorderR}, ${$.calloutBorderG}, ${$.calloutBorderB}, 0.3)`;
58 | const xBorderBottomColor = `rgba(${$.calloutBorderR}, ${$.calloutBorderG}, ${$.calloutBorderB}, 0.25)`;
59 |
60 | const s = stylex.create({
61 | main: {
62 | display: "flex",
63 | flexDirection: "column",
64 | alignItems: "center",
65 | justifyContent: "space-between",
66 | minHeight: "100vh",
67 | paddingTop: spacing.xxl,
68 | paddingBottom: {
69 | default: spacing.xxl,
70 | [MEDIA_MOBILE]: spacing.md,
71 | },
72 | },
73 | hero: {
74 | flexGrow: 1,
75 | display: "flex",
76 | alignItems: "center",
77 | justifyContent: "center",
78 | flexDirection: "column",
79 | gap: spacing.xl,
80 | },
81 | h1: {
82 | fontSize: text.h1,
83 | lineHeight: 1,
84 | fontFamily: $.fontSans,
85 | fontWeight: 400,
86 | textAlign: "center",
87 | display: "flex",
88 | gap: spacing.md,
89 | whiteSpace: "nowrap",
90 | flexDirection: {
91 | default: "row",
92 | [MEDIA_MOBILE]: "column",
93 | },
94 | },
95 | emoji: {
96 | position: "relative",
97 | fontFamily: "sans-serif",
98 | top: {
99 | default: 0,
100 | [MEDIA_MOBILE]: spacing.xxxs,
101 | },
102 | },
103 | description: {
104 | display: "inherit",
105 | justifyContent: "inherit",
106 | alignItems: "inherit",
107 | fontSize: text.sm,
108 | maxWidth: $.maxWidth,
109 | width: "100%",
110 | zIndex: 2,
111 | fontFamily: $.fontMono,
112 | },
113 | descLink: {
114 | display: "flex",
115 | alignItems: "center",
116 | justifyContent: "center",
117 | gap: spacing.xxs,
118 | padding: { [MEDIA_MOBILE]: spacing.sm },
119 | },
120 | descP: {
121 | display: { [MEDIA_MOBILE]: "flex" },
122 | position: {
123 | default: "relative",
124 | [MEDIA_MOBILE]: "fixed",
125 | },
126 | justifyContent: { [MEDIA_MOBILE]: "center" },
127 | alignItems: { [MEDIA_MOBILE]: "center" },
128 | width: { [MEDIA_MOBILE]: "100%" },
129 | margin: 0,
130 | paddingInline: spacing.sm,
131 | paddingTop: {
132 | default: spacing.sm,
133 | [MEDIA_MOBILE]: spacing.lg,
134 | },
135 | paddingBottom: {
136 | default: spacing.sm,
137 | [MEDIA_MOBILE]: spacing.md,
138 | },
139 | backgroundColor: $.calloutRGB50,
140 | backgroundImage: {
141 | default: null,
142 | [MEDIA_MOBILE]: bgImage,
143 | },
144 | borderWidth: {
145 | default: "1px",
146 | [MEDIA_MOBILE]: "0",
147 | },
148 | borderStyle: "solid",
149 | borderColor: xBorderColor,
150 | borderBottomColor: {
151 | default: null,
152 | [MEDIA_MOBILE]: xBorderBottomColor,
153 | },
154 | borderRadius: {
155 | default: spacing.xs,
156 | [MEDIA_MOBILE]: 0,
157 | },
158 | inset: { [MEDIA_MOBILE]: "0 0 auto" },
159 | },
160 | code: {
161 | fontWeight: 700,
162 | fontFamily: $.fontMono,
163 | },
164 | grid: {
165 | display: "grid",
166 | gridTemplateColumns: {
167 | default: "repeat(4, minmax(25%, auto))",
168 | [MEDIA_MOBILE]: "1fr",
169 | [MEDIA_TABLET]: "repeat(2, 50%)",
170 | },
171 | width: $.maxWidth,
172 | maxWidth: {
173 | default: "100%",
174 | [MEDIA_MOBILE]: 320,
175 | },
176 | textAlign: { [MEDIA_MOBILE]: "center" },
177 | },
178 | });
179 |
--------------------------------------------------------------------------------
/app/globalTokens.stylex.ts:
--------------------------------------------------------------------------------
1 | import stylex from "@stylexjs/stylex";
2 |
3 | /**
4 | * o--o o o o o-O-o o-o o--o o-o o o o-O-o o-o
5 | * | | | | | | \ | o o |\ | | |
6 | * O-o | | | | | O O-o | | | \ | | o-o
7 | * | | | | | | / | o o | \| | |
8 | * o O---o o-o o-O-o o-o o o-o o o o o--o
9 | *
10 | * Reference: https://utopia.fyi/type/calculator
11 | *
12 | * The following constants are used to calculate fluid typography.
13 | * Feel free to change these initial numbers to suit your needs.
14 | *
15 | * StyleX can compute all of this at compile time as all the information
16 | * is statically available in the same file and the only functions used are
17 | * the Math.pow and Math.round functions.
18 | *
19 | * NOTE: Any custom functions will not be able to be computed at compile time.
20 | */
21 | const MIN_WIDTH = 320;
22 | const MAX_WIDTH = 1240;
23 | const MIN_SCALE = 1.2;
24 | const MAX_SCALE = 1.333;
25 | const MIN_BASE_SIZE = 16;
26 | const MAX_BASE_SIZE = 20;
27 |
28 | // Font sizes in `rem` units
29 | const MIN_FONT = {
30 | xxs: Math.round(MIN_BASE_SIZE / Math.pow(MIN_SCALE, 3) / 0.16) / 100,
31 | xs: Math.round(MIN_BASE_SIZE / Math.pow(MIN_SCALE, 2) / 0.16) / 100,
32 | sm: Math.round(MIN_BASE_SIZE / MIN_SCALE / 0.16) / 100,
33 | p: Math.round(MIN_BASE_SIZE / 4) / 4,
34 | h5: Math.round((MIN_BASE_SIZE * MIN_SCALE) / 0.16) / 100,
35 | h4: Math.round((MIN_BASE_SIZE * Math.pow(MIN_SCALE, 2)) / 0.16) / 100,
36 | h3: Math.round((MIN_BASE_SIZE * Math.pow(MIN_SCALE, 3)) / 0.16) / 100,
37 | h2: Math.round((MIN_BASE_SIZE * Math.pow(MIN_SCALE, 4)) / 0.16) / 100,
38 | h1: Math.round((MIN_BASE_SIZE * Math.pow(MIN_SCALE, 5)) / 0.16) / 100,
39 | };
40 | // Font sizes in `rem` units
41 | const MAX_FONT = {
42 | xxs: Math.round(MAX_BASE_SIZE / Math.pow(MAX_SCALE, 3) / 0.16) / 100,
43 | xs: Math.round(MAX_BASE_SIZE / Math.pow(MAX_SCALE, 2) / 0.16) / 100,
44 | sm: Math.round(MAX_BASE_SIZE / MAX_SCALE / 0.16) / 100,
45 | p: Math.round(MAX_BASE_SIZE / 4) / 4,
46 | h5: Math.round((MAX_BASE_SIZE * MAX_SCALE) / 0.16) / 100,
47 | h4: Math.round((MAX_BASE_SIZE * Math.pow(MAX_SCALE, 2)) / 0.16) / 100,
48 | h3: Math.round((MAX_BASE_SIZE * Math.pow(MAX_SCALE, 3)) / 0.16) / 100,
49 | h2: Math.round((MAX_BASE_SIZE * Math.pow(MAX_SCALE, 4)) / 0.16) / 100,
50 | h1: Math.round((MAX_BASE_SIZE * Math.pow(MAX_SCALE, 5)) / 0.16) / 100,
51 | };
52 | const SLOPE = {
53 | xxs: (16 * (MAX_FONT.xxs - MIN_FONT.xxs)) / (MAX_WIDTH - MIN_WIDTH),
54 | xs: (16 * (MAX_FONT.xs - MIN_FONT.xs)) / (MAX_WIDTH - MIN_WIDTH),
55 | sm: (16 * (MAX_FONT.sm - MIN_FONT.sm)) / (MAX_WIDTH - MIN_WIDTH),
56 | p: (16 * (MAX_FONT.p - MIN_FONT.p)) / (MAX_WIDTH - MIN_WIDTH),
57 | h5: (16 * (MAX_FONT.h5 - MIN_FONT.h5)) / (MAX_WIDTH - MIN_WIDTH),
58 | h4: (16 * (MAX_FONT.h4 - MIN_FONT.h4)) / (MAX_WIDTH - MIN_WIDTH),
59 | h3: (16 * (MAX_FONT.h3 - MIN_FONT.h3)) / (MAX_WIDTH - MIN_WIDTH),
60 | h2: (16 * (MAX_FONT.h2 - MIN_FONT.h2)) / (MAX_WIDTH - MIN_WIDTH),
61 | h1: (16 * (MAX_FONT.h1 - MIN_FONT.h1)) / (MAX_WIDTH - MIN_WIDTH),
62 | };
63 | const INTERCEPT = {
64 | xxs: Math.round(100 * (MIN_FONT.xxs - SLOPE.xxs * (MIN_WIDTH / 16))) / 100,
65 | xs: Math.round(100 * (MIN_FONT.xs - SLOPE.xs * (MIN_WIDTH / 16))) / 100,
66 | sm: Math.round(100 * (MIN_FONT.sm - SLOPE.sm * (MIN_WIDTH / 16))) / 100,
67 | p: Math.round(100 * (MIN_FONT.p - SLOPE.p * (MIN_WIDTH / 16))) / 100,
68 | h5: Math.round(100 * (MIN_FONT.h5 - SLOPE.h5 * (MIN_WIDTH / 16))) / 100,
69 | h4: Math.round(100 * (MIN_FONT.h4 - SLOPE.h4 * (MIN_WIDTH / 16))) / 100,
70 | h3: Math.round(100 * (MIN_FONT.h3 - SLOPE.h3 * (MIN_WIDTH / 16))) / 100,
71 | h2: Math.round(100 * (MIN_FONT.h2 - SLOPE.h2 * (MIN_WIDTH / 16))) / 100,
72 | h1: Math.round(100 * (MIN_FONT.h1 - SLOPE.h1 * (MIN_WIDTH / 16))) / 100,
73 | };
74 |
75 | // prettier-ignore
76 | export const text = stylex.defineVars({
77 | xxs: `clamp(${ Math.min(MIN_FONT.xxs) }rem, calc(${ INTERCEPT.xxs }rem + ${ Math.round(10000 * SLOPE.xxs) / 100 }vw), ${ Math.max(MAX_FONT.xxs) }rem)`,
78 | xs: `clamp(${ Math.min(MIN_FONT.xs ) }rem, calc(${ INTERCEPT.xs }rem + ${ Math.round(10000 * SLOPE.xs ) / 100 }vw), ${ Math.max(MAX_FONT.xs ) }rem)`,
79 | sm: `clamp(${ Math.min(MIN_FONT.sm ) }rem, calc(${ INTERCEPT.sm }rem + ${ Math.round(10000 * SLOPE.sm ) / 100 }vw), ${ Math.max(MAX_FONT.sm ) }rem)`,
80 | p: `clamp(${ Math.min(MIN_FONT.p ) }rem, calc(${ INTERCEPT.p }rem + ${ Math.round(10000 * SLOPE.p ) / 100 }vw), ${ Math.max(MAX_FONT.p ) }rem)`,
81 | h5: `clamp(${ Math.min(MIN_FONT.h5 ) }rem, calc(${ INTERCEPT.h5 }rem + ${ Math.round(10000 * SLOPE.h5 ) / 100 }vw), ${ Math.max(MAX_FONT.h5 ) }rem)`,
82 | h4: `clamp(${ Math.min(MIN_FONT.h4 ) }rem, calc(${ INTERCEPT.h4 }rem + ${ Math.round(10000 * SLOPE.h4 ) / 100 }vw), ${ Math.max(MAX_FONT.h4 ) }rem)`,
83 | h3: `clamp(${ Math.min(MIN_FONT.h3 ) }rem, calc(${ INTERCEPT.h3 }rem + ${ Math.round(10000 * SLOPE.h3 ) / 100 }vw), ${ Math.max(MAX_FONT.h3 ) }rem)`,
84 | h2: `clamp(${ Math.min(MIN_FONT.h2 ) }rem, calc(${ INTERCEPT.h2 }rem + ${ Math.round(10000 * SLOPE.h2 ) / 100 }vw), ${ Math.max(MAX_FONT.h2 ) }rem)`,
85 | h1: `clamp(${ Math.min(MIN_FONT.h1 ) }rem, calc(${ INTERCEPT.h1 }rem + ${ Math.round(10000 * SLOPE.h1 ) / 100 }vw), ${ Math.max(MAX_FONT.h1 ) }rem)`,
86 | });
87 |
88 | /**
89 | * o--o o o o o-O-o o-o o-o o--o O o-o o--o
90 | * | | | | | | \ | | | / \ / |
91 | * O-o | | | | | O o-o O--o o---oO O-o
92 | * | | | | | | / | | | | \ |
93 | * o O---o o-o o-O-o o-o o--o o o o o-o o--o
94 | *
95 | * Reference: https://utopia.fyi/space/calculator
96 | *
97 | * Similar to the fluid typography, we can create fluid values for spacing.
98 | * Using similar formulas and similar scales.
99 | *
100 | * NOTE: It is common to have more varied needs for spacing than for font-size.
101 | * So feel free to add some more values by following the pattern below.
102 | *
103 | * EXCEPT: We are using `px` instead of `rem`
104 | * ------------------------------------------
105 | * When talking about font-size, it is the best practice to use
106 | * `rem` so that an end user can change the font-size using the
107 | * browser's font-size setting.
108 | *
109 | * However, when talking about spacing, it is the best practice to
110 | * use `px` because using `rems` here makes font-size behave like zoom.
111 | *
112 | * Users that prefer larger text, don't neccessarily want larger spacing as well.
113 | *
114 | */
115 |
116 | const MULT = {
117 | xxxs: 0.25,
118 | xxs: 0.5,
119 | xs: 0.75,
120 | sm: 1,
121 | md: 1.5,
122 | lg: 2,
123 | xl: 3,
124 | xxl: 4,
125 | xxxl: 6,
126 | xxxxl: 8,
127 | };
128 | const MIN_SPACE = {
129 | xxxs: MULT.xxxs * MIN_BASE_SIZE,
130 | xxs: MULT.xxs * MIN_BASE_SIZE,
131 | xs: MULT.xs * MIN_BASE_SIZE,
132 | sm: MULT.sm * MIN_BASE_SIZE,
133 | md: MULT.md * MIN_BASE_SIZE,
134 | lg: MULT.lg * MIN_BASE_SIZE,
135 | xl: MULT.xl * MIN_BASE_SIZE,
136 | xxl: MULT.xxl * MIN_BASE_SIZE,
137 | xxxl: MULT.xxxl * MIN_BASE_SIZE,
138 | xxxxl: MULT.xxxxl * MIN_BASE_SIZE,
139 | };
140 | const MAX_SPACE = {
141 | xxxs: MULT.xxxs * MAX_BASE_SIZE,
142 | xxs: MULT.xxs * MAX_BASE_SIZE,
143 | xs: MULT.xs * MAX_BASE_SIZE,
144 | sm: MULT.sm * MAX_BASE_SIZE,
145 | md: MULT.md * MAX_BASE_SIZE,
146 | lg: MULT.lg * MAX_BASE_SIZE,
147 | xl: MULT.xl * MAX_BASE_SIZE,
148 | xxl: MULT.xxl * MAX_BASE_SIZE,
149 | xxxl: MULT.xxxl * MAX_BASE_SIZE,
150 | xxxxl: MULT.xxxxl * MAX_BASE_SIZE,
151 | };
152 | const SLOPE_SPACE = {
153 | xxxs: (MAX_SPACE.xxxs - MIN_SPACE.xxxs) / (MAX_WIDTH - MIN_WIDTH),
154 | xxs: (MAX_SPACE.xxs - MIN_SPACE.xxs) / (MAX_WIDTH - MIN_WIDTH),
155 | xs: (MAX_SPACE.xs - MIN_SPACE.xs) / (MAX_WIDTH - MIN_WIDTH),
156 | sm: (MAX_SPACE.sm - MIN_SPACE.sm) / (MAX_WIDTH - MIN_WIDTH),
157 | md: (MAX_SPACE.md - MIN_SPACE.md) / (MAX_WIDTH - MIN_WIDTH),
158 | lg: (MAX_SPACE.lg - MIN_SPACE.lg) / (MAX_WIDTH - MIN_WIDTH),
159 | xl: (MAX_SPACE.xl - MIN_SPACE.xl) / (MAX_WIDTH - MIN_WIDTH),
160 | xxl: (MAX_SPACE.xxl - MIN_SPACE.xxl) / (MAX_WIDTH - MIN_WIDTH),
161 | xxxl: (MAX_SPACE.xxxl - MIN_SPACE.xxxl) / (MAX_WIDTH - MIN_WIDTH),
162 | xxxxl: (MAX_SPACE.xxxxl - MIN_SPACE.xxxxl) / (MAX_WIDTH - MIN_WIDTH),
163 | };
164 | // rounded to the nearest 0.25px
165 | const INTERCEPT_SPACE = {
166 | xxxs: Math.round(4 * (MIN_SPACE.xxxs - SLOPE_SPACE.xxxs * MIN_WIDTH)) / 4,
167 | xxs: Math.round(4 * (MIN_SPACE.xxs - SLOPE_SPACE.xxs * MIN_WIDTH)) / 4,
168 | xs: Math.round(4 * (MIN_SPACE.xs - SLOPE_SPACE.xs * MIN_WIDTH)) / 4,
169 | sm: Math.round(4 * (MIN_SPACE.sm - SLOPE_SPACE.sm * MIN_WIDTH)) / 4,
170 | md: Math.round(4 * (MIN_SPACE.md - SLOPE_SPACE.md * MIN_WIDTH)) / 4,
171 | lg: Math.round(4 * (MIN_SPACE.lg - SLOPE_SPACE.lg * MIN_WIDTH)) / 4,
172 | xl: Math.round(4 * (MIN_SPACE.xl - SLOPE_SPACE.xl * MIN_WIDTH)) / 4,
173 | xxl: Math.round(4 * (MIN_SPACE.xxl - SLOPE_SPACE.xxl * MIN_WIDTH)) / 4,
174 | xxxl: Math.round(4 * (MIN_SPACE.xxxl - SLOPE_SPACE.xxxl * MIN_WIDTH)) / 4,
175 | xxxxl: Math.round(4 * (MIN_SPACE.xxxxl - SLOPE_SPACE.xxxxl * MIN_WIDTH)) / 4,
176 | };
177 |
178 | // prettier-ignore
179 | export const spacing = stylex.defineVars({
180 | xxxs: `clamp(${MIN_SPACE.xxxs }px, calc(${INTERCEPT_SPACE.xxxs }px + ${ Math.round(10000 * SLOPE_SPACE.xxxs ) / 100 }vw), ${MAX_SPACE.xxxs }px)`,
181 | xxs: `clamp(${MIN_SPACE.xxs }px, calc(${INTERCEPT_SPACE.xxs }px + ${ Math.round(10000 * SLOPE_SPACE.xxs ) / 100 }vw), ${MAX_SPACE.xxs }px)`,
182 | xs: `clamp(${MIN_SPACE.xs }px, calc(${INTERCEPT_SPACE.xs }px + ${ Math.round(10000 * SLOPE_SPACE.xs ) / 100 }vw), ${MAX_SPACE.xs }px)`,
183 | sm: `clamp(${MIN_SPACE.sm }px, calc(${INTERCEPT_SPACE.sm }px + ${ Math.round(10000 * SLOPE_SPACE.sm ) / 100 }vw), ${MAX_SPACE.sm }px)`,
184 | md: `clamp(${MIN_SPACE.md }px, calc(${INTERCEPT_SPACE.md }px + ${ Math.round(10000 * SLOPE_SPACE.md ) / 100 }vw), ${MAX_SPACE.md }px)`,
185 | lg: `clamp(${MIN_SPACE.lg }px, calc(${INTERCEPT_SPACE.lg }px + ${ Math.round(10000 * SLOPE_SPACE.lg ) / 100 }vw), ${MAX_SPACE.lg }px)`,
186 | xl: `clamp(${MIN_SPACE.xl }px, calc(${INTERCEPT_SPACE.xl }px + ${ Math.round(10000 * SLOPE_SPACE.xl ) / 100 }vw), ${MAX_SPACE.xl }px)`,
187 | xxl: `clamp(${MIN_SPACE.xxl }px, calc(${INTERCEPT_SPACE.xxl }px + ${ Math.round(10000 * SLOPE_SPACE.xxl ) / 100 }vw), ${MAX_SPACE.xxl }px)`,
188 | xxxl: `clamp(${MIN_SPACE.xxxl }px, calc(${INTERCEPT_SPACE.xxxl }px + ${ Math.round(10000 * SLOPE_SPACE.xxxl ) / 100 }vw), ${MAX_SPACE.xxxl }px)`,
189 | xxxxl: `clamp(${MIN_SPACE.xxxxl }px, calc(${INTERCEPT_SPACE.xxxxl }px + ${ Math.round(10000 * SLOPE_SPACE.xxxxl ) / 100 }vw), ${MAX_SPACE.xxxxl }px)`,
190 | });
191 |
192 | /**
193 | * Color Tokens
194 | */
195 | const DARK_MODE = "@media (prefers-color-scheme: dark)";
196 |
197 | export const globalTokens = stylex.defineVars({
198 | maxWidth: `${MAX_WIDTH}px`,
199 | fontMono: [
200 | "ui-monospace",
201 | "Menlo",
202 | "Monaco",
203 | '"Cascadia Mono"',
204 | '"Segoe UI Mono"',
205 | '"Roboto Mono"',
206 | '"Oxygen Mono"',
207 | '"Ubuntu Monospace"',
208 | '"Source Code Pro"',
209 | '"Fira Mono"',
210 | '"Droid Sans Mono"',
211 | '"Courier New"',
212 | "monospace",
213 | ].join(", "),
214 | fontSans: [
215 | "--apple-system",
216 | "BlinkMacSystemFont",
217 | '"Segoe UI"',
218 | "Roboto",
219 | '"Helvetica Neue"',
220 | "Arial",
221 | '"Noto Sans"',
222 | "sans-serif",
223 | '"Apple Color Emoji"',
224 | '"Segoe UI Emoji"',
225 | '"Segoe UI Symbol"',
226 | '"Noto Color Emoji"',
227 | ].join(", "),
228 |
229 | foregroundR: { default: "0", [DARK_MODE]: "255" },
230 | foregroundG: { default: "0", [DARK_MODE]: "255" },
231 | foregroundB: { default: "0", [DARK_MODE]: "255" },
232 |
233 | bgStartRGB: { default: "rgb(214, 219, 220)", [DARK_MODE]: "rgb(0, 0, 0)" },
234 |
235 | bgEndR: { default: "255", [DARK_MODE]: "0" },
236 | bgEndG: { default: "255", [DARK_MODE]: "0" },
237 | bgEndB: { default: "255", [DARK_MODE]: "0" },
238 |
239 | calloutRGB: { default: "rgb(238, 240, 241)", [DARK_MODE]: "rgb(20, 20, 20)" },
240 | calloutRGB50: {
241 | default: "rgba(238, 240, 241, 0.5)",
242 | [DARK_MODE]: "rgba(20, 20, 20, 0.5)",
243 | },
244 |
245 | calloutBorderR: { default: "172", [DARK_MODE]: "108" },
246 | calloutBorderG: { default: "175", [DARK_MODE]: "108" },
247 | calloutBorderB: { default: "176", [DARK_MODE]: "108" },
248 |
249 | cardR: { default: "180", [DARK_MODE]: "100" },
250 | cardG: { default: "185", [DARK_MODE]: "100" },
251 | cardB: { default: "188", [DARK_MODE]: "100" },
252 |
253 | cardBorderR: { default: "131", [DARK_MODE]: "200" },
254 | cardBorderG: { default: "134", [DARK_MODE]: "200" },
255 | cardBorderB: { default: "135", [DARK_MODE]: "200" },
256 |
257 | primaryGlow: {
258 | default:
259 | "conic-gradient(from 180deg at 50% 50%, #16abff33 0deg, #0885ff33 55deg, #54d6ff33 120deg, #0071ff33 160deg, transparent 360deg)",
260 | [DARK_MODE]: "radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0))",
261 | },
262 | secondaryGlow: {
263 | default: "radial-gradient(rgba(255, 255, 255, 1), rgba(255, 255, 255, 0))",
264 | [DARK_MODE]: `linear-gradient(to bottom right, rgba(1, 65, 255, 0), rgba(1, 65, 255, 0), rgba(1, 65, 255, 0.3))`,
265 | },
266 | });
267 |
--------------------------------------------------------------------------------