add components

This commit is contained in:
andres
2023-09-09 17:03:16 +02:00
parent 4e301916f4
commit 450d664f34
35 changed files with 550 additions and 25 deletions

View File

@@ -18,6 +18,8 @@
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-tabs": "^1.0.4",
"@reduxjs/toolkit": "^1.9.5",
"@storybook/theming": "^7.2.1",
"clsx": "^2.0.0",
@@ -51,6 +53,7 @@
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.3",
"eslint": "^8.45.0",
"eslint-plugin-myPlugin": "file:./eslint",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"eslint-plugin-storybook": "^0.6.13",
@@ -58,7 +61,6 @@
"storybook": "^7.2.1",
"stylelint": "^15.10.2",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"eslint-plugin-myPlugin": "file:./eslint"
"vite": "^4.4.5"
}
}

66
pnpm-lock.yaml generated
View File

@@ -20,6 +20,12 @@ dependencies:
'@radix-ui/react-radio-group':
specifier: ^1.1.3
version: 1.1.3(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slider':
specifier: ^1.1.2
version: 1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-tabs':
specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
'@reduxjs/toolkit':
specifier: ^1.9.5
version: 1.9.5(react-redux@8.1.2)(react@18.2.0)
@@ -2547,6 +2553,37 @@ packages:
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.15)(react@18.2.0)
/@radix-ui/react-slider@1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.6
'@radix-ui/number': 1.0.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@types/react': 18.2.15
'@types/react-dom': 18.2.7
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-slot@1.0.2(@types/react@18.2.15)(react@18.2.0):
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
peerDependencies:
@@ -2561,6 +2598,34 @@ packages:
'@types/react': 18.2.15
react: 18.2.0
/@radix-ui/react-tabs@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-context': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.15)(react@18.2.0)
'@types/react': 18.2.15
'@types/react-dom': 18.2.7
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.15)(react@18.2.0):
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
peerDependencies:
@@ -10217,5 +10282,4 @@ packages:
file:eslint:
resolution: {directory: eslint, type: directory}
name: eslint
version: 0.0.0
dev: true

View File

@@ -9,3 +9,4 @@ export { default as Logo } from './logo'
export { default as PersonOutline } from './person-outline'
export { default as ChevronUp } from './chevron-up'
export { default as Close } from './close'
export { default as Search } from './search'

View File

@@ -0,0 +1,26 @@
import { SVGProps, Ref, forwardRef, memo } from 'react'
const SvgComponent = (props: SVGProps<SVGSVGElement>, ref: Ref<SVGSVGElement>) => (
<svg
width={20}
height={20}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
ref={ref}
{...props}
>
<g clipPath="url(#clip0_21547_180)">
<path
d="M17.2583 16.075L14.425 13.25C15.3392 12.0854 15.8352 10.6472 15.8333 9.16667C15.8333 7.84813 15.4423 6.5592 14.7098 5.46287C13.9773 4.36654 12.9361 3.51206 11.7179 3.00747C10.4997 2.50289 9.15927 2.37087 7.86607 2.6281C6.57286 2.88534 5.38497 3.52027 4.45262 4.45262C3.52027 5.38497 2.88534 6.57286 2.6281 7.86607C2.37087 9.15927 2.50289 10.4997 3.00747 11.7179C3.51206 12.9361 4.36654 13.9773 5.46287 14.7098C6.5592 15.4423 7.84813 15.8333 9.16667 15.8333C10.6472 15.8352 12.0854 15.3392 13.25 14.425L16.075 17.2583C16.1525 17.3364 16.2446 17.3984 16.3462 17.4407C16.4477 17.4831 16.5567 17.5048 16.6667 17.5048C16.7767 17.5048 16.8856 17.4831 16.9871 17.4407C17.0887 17.3984 17.1809 17.3364 17.2583 17.2583C17.3364 17.1809 17.3984 17.0887 17.4407 16.9871C17.4831 16.8856 17.5048 16.7767 17.5048 16.6667C17.5048 16.5567 17.4831 16.4477 17.4407 16.3462C17.3984 16.2446 17.3364 16.1525 17.2583 16.075ZM4.16667 9.16667C4.16667 8.17776 4.45991 7.21106 5.00932 6.38882C5.55873 5.56657 6.33962 4.92571 7.25325 4.54727C8.16688 4.16883 9.17222 4.06982 10.1421 4.26274C11.112 4.45567 12.0029 4.93187 12.7022 5.63114C13.4015 6.3304 13.8777 7.22131 14.0706 8.19122C14.2635 9.16112 14.1645 10.1665 13.7861 11.0801C13.4076 11.9937 12.7668 12.7746 11.9445 13.324C11.1223 13.8734 10.1556 14.1667 9.16667 14.1667C7.84059 14.1667 6.56882 13.6399 5.63114 12.7022C4.69345 11.7645 4.16667 10.4928 4.16667 9.16667Z"
fill="#808080"
/>
</g>
<defs>
<clipPath id="clip0_21547_180">
<rect width={20} height={20} fill="white" />
</clipPath>
</defs>
</svg>
)
export default memo(forwardRef(SvgComponent))

View File

View File

@@ -52,7 +52,6 @@
width: 36px;
height: 36px;
background-color: var(--color-dark-900);
border-radius: 50%;
&.disabled {

View File

@@ -3,5 +3,8 @@ export * from './card'
export * from './typography'
export * from './checkbox'
export * from './text-field'
export * from './table'
export * from './controlled'
export * from './radio-group'
export * from './page'
export * from './slider'

View File

@@ -0,0 +1 @@
export * from './page'

View File

@@ -0,0 +1,6 @@
.root {
display: flex;
justify-content: center;
margin-top: 36px;
padding-inline: 24px;
}

View File

@@ -0,0 +1,15 @@
import { ComponentPropsWithoutRef, forwardRef } from 'react'
import { clsx } from 'clsx'
import s from './page.module.scss'
export type PageProps = ComponentPropsWithoutRef<'div'>
export const Page = forwardRef<HTMLDivElement, PageProps>(({ className, ...props }, ref) => {
const classNames = {
root: clsx(s.root, className),
}
return <div {...props} ref={ref} className={classNames.root} />
})

View File

@@ -0,0 +1 @@
export * from './slider'

View File

@@ -0,0 +1,52 @@
.container {
display: flex;
gap: 10px;
align-items: center;
justify-content: center;
width: 100%;
}
.root {
touch-action: none;
user-select: none;
position: relative;
display: flex;
align-items: center;
width: 100%;
}
.track {
position: relative;
width: 100%;
height: 4px;
opacity: 0.5;
background-color: var(--color-accent-500);
border-radius: 2px;
}
.range {
position: absolute;
height: 100%;
background-color: var(--color-accent-500);
}
.thumb {
touch-action: pan-x;
cursor: pointer;
display: block;
width: 16px;
height: 16px;
background-color: var(--color-light-100);
border-radius: 9999px;
transition: transform 0.2s ease-in-out;
}

View File

@@ -0,0 +1,26 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react'
import * as SliderPrimitive from '@radix-ui/react-slider'
import { clsx } from 'clsx'
import s from './slider.module.scss'
const Slider = forwardRef<
ElementRef<typeof SliderPrimitive.Root>,
ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<div className={s.container}>
<span>{props?.value?.[0]}</span>
<SliderPrimitive.Root ref={ref} className={clsx(s.root, className)} {...props}>
<SliderPrimitive.Track className={s.track}>
<SliderPrimitive.Range className="absolute h-full bg-primary" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className={s.thumb} />
<SliderPrimitive.Thumb className={s.thumb} />
</SliderPrimitive.Root>
<span>{props?.value?.[1]}</span>
</div>
))
Slider.displayName = SliderPrimitive.Root.displayName
export { Slider }

View File

@@ -1,5 +1,6 @@
.table {
border-collapse: collapse;
width: 100%;
color: var(--color-light-100);
border: 1px solid var(--color-dark-500);
}

View File

@@ -75,3 +75,49 @@ export const TableEmpty: FC<ComponentProps<'div'> & { mt?: string; mb?: string }
</Typography>
)
}
export type Column = {
key: string
title: string
sortable?: boolean
}
export type Sort = {
key: string
direction: 'asc' | 'desc'
} | null
export const TableHeader: FC<
Omit<
ComponentPropsWithoutRef<'thead'> & {
columns: Column[]
sort?: Sort
onSort?: (sort: Sort) => void
},
'children'
>
> = ({ columns, sort, onSort, ...restProps }) => {
const handleSort = (key: string, sortable?: boolean) => () => {
if (!onSort || !sortable) return
if (sort?.key !== key) return onSort({ key, direction: 'asc' })
if (sort.direction === 'desc') return onSort(null)
return onSort({
key,
direction: sort?.direction === 'asc' ? 'desc' : 'asc',
})
}
return (
<TableHead {...restProps}>
<TableRow>
{columns.map(({ title, key, sortable = true }) => (
<TableHeadCell key={key} onClick={handleSort(key, sortable)}>
{title}
{sort && sort.key === key && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
</TableHeadCell>
))}
</TableRow>
</TableHead>
)
}

View File

@@ -0,0 +1 @@
export * from './tabs'

View File

@@ -0,0 +1,34 @@
.list {
display: inline-flex;
background-color: var(--color-dark-900);
}
.trigger {
cursor: pointer;
display: inline-flex;
flex-shrink: 0;
flex-wrap: nowrap;
padding: 6px 24px;
background-color: var(--color-dark-900);
border: 1px solid var(--color-dark-300);
&:last-of-type {
border-radius: 0 2px 2px 0;
}
&:first-of-type {
border-radius: 2px 0 0 2px;
}
&[data-state='active'] {
background-color: var(--color-accent-500);
border-color: var(--color-accent-500);
}
}
.content {
color: inherit;
}

View File

@@ -0,0 +1,37 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from 'react'
import * as TabsPrimitive from '@radix-ui/react-tabs'
import { clsx } from 'clsx'
import s from './tabs.module.scss'
const Tabs = TabsPrimitive.Root
const TabsList = forwardRef<
ElementRef<typeof TabsPrimitive.List>,
ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List ref={ref} className={clsx(s.list, className)} {...props} />
))
TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = forwardRef<
ElementRef<typeof TabsPrimitive.Trigger>,
ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger ref={ref} className={clsx(s.trigger, className)} {...props} />
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
const TabsContent = forwardRef<
ElementRef<typeof TabsPrimitive.Content>,
ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content ref={ref} className={clsx(s.content, className)} {...props} />
))
TabsContent.displayName = TabsPrimitive.Content.displayName
export { Tabs, TabsList, TabsTrigger, TabsContent }

View File

@@ -40,6 +40,10 @@
color: var(--color-danger-300);
border-color: var(--color-danger-300);
}
&.hasLeadingIcon {
padding-left: 41px;
}
}
.label {
@@ -47,6 +51,27 @@
color: var(--color-dark-100);
}
.leadingIcon {
position: absolute;
top: 50%;
bottom: 50%;
left: 0;
transform: translateY(-50%);
width: 20px;
height: 20px;
margin-left: 12px;
padding: 0;
background: transparent;
border: 0;
outline: 0;
&:focus-visible {
outline: var(--outline-focus);
}
}
.showPassword {
cursor: pointer;

View File

@@ -4,7 +4,7 @@ import { clsx } from 'clsx'
import s from './text-field.module.scss'
import { VisibilityOff, Eye } from '@/assets'
import { VisibilityOff, Eye, Search } from '@/assets'
import { Typography } from '@/components'
export type TextFieldProps = {
@@ -13,6 +13,7 @@ export type TextFieldProps = {
labelProps?: ComponentProps<'label'>
errorMessage?: string
label?: string
search?: boolean
} & ComponentPropsWithoutRef<'input'>
export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
@@ -27,6 +28,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
label,
onChange,
onValueChange,
search,
...restProps
},
ref
@@ -45,9 +47,10 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
const classNames = {
root: clsx(s.root, containerProps?.className),
fieldContainer: clsx(s.fieldContainer),
field: clsx(s.field, !!errorMessage && s.error, className),
field: clsx(s.field, !!errorMessage && s.error, search && s.hasLeadingIcon, className),
label: clsx(s.label, labelProps?.className),
error: clsx(s.error),
leadingIcon: s.leadingIcon,
}
return (
@@ -58,6 +61,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
</Typography>
)}
<div className={classNames.fieldContainer}>
{search && <Search className={classNames.leadingIcon} />}
<input
className={classNames.field}
placeholder={placeholder}

View File

@@ -6,7 +6,7 @@ import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { App } from '@/App'
import { App } from './App'
createRoot(document.getElementById('root')!).render(
<StrictMode>

View File

@@ -0,0 +1,17 @@
.root {
width: 100%;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.filters {
display: flex;
grid-template-columns: repeat(4, 1fr);
column-gap: 16px;
margin-bottom: 16px;
}

View File

@@ -0,0 +1,87 @@
import { useState } from 'react'
import s from './decks-page.module.scss'
import {
Button,
Page,
Typography,
Column,
Table,
TableBody,
TableCell,
TableHeader,
TableRow,
TextField,
Slider,
} from '@/components'
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { useGetDecksQuery } from '@/services/decks'
const columns: Column[] = [
{
key: 'name',
title: 'Name',
},
{
key: 'cardsCount',
title: 'Cards',
},
{
key: 'updated',
title: 'Last Updated',
},
{
key: 'author',
title: 'Created By',
},
{
key: 'actions',
title: '',
},
]
export const DecksPage = () => {
const { data: decks } = useGetDecksQuery()
const [activeTab, setActiveTab] = useState('my')
const [range, setRange] = useState([0, 100])
const [rangeValue, setRangeValue] = useState([0, 1])
if (!decks) return <div>loading...</div>
return (
<Page>
<div className={s.root}>
<div className={s.header}>
<Typography variant="large">Decks</Typography>
<Button>Add new deck</Button>
</div>
<div className={s.filters}>
<TextField placeholder="Search" search />
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
<TabsTrigger value={'my'}>My decks</TabsTrigger>
<TabsTrigger value={'all'}>All decks</TabsTrigger>
</TabsList>
</Tabs>
<Slider onValueCommit={setRange} value={rangeValue} onValueChange={setRangeValue} />
<Button variant={'secondary'}>Clear filters</Button>
</div>
<Table>
<TableHeader columns={columns} />
<TableBody>
{decks?.items.map(deck => (
<TableRow key={deck.id}>
<TableCell>{deck.name}</TableCell>
<TableCell>{deck.cardsCount}</TableCell>
<TableCell>{deck.updated}</TableCell>
<TableCell>{deck.author.name}</TableCell>
<TableCell>...</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</Page>
)
}

View File

@@ -0,0 +1 @@
export * from './decks-page'

2
src/pages/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './sign-in-page'
export * from './decks-page'

View File

@@ -0,0 +1 @@
export * from './sign-in-page'

View File

@@ -0,0 +1,9 @@
import { SignIn, Page } from '@/components'
export const SignInPage = () => {
return (
<Page>
<SignIn onSubmit={() => {}} />
</Page>
)
}

View File

@@ -6,21 +6,27 @@ import {
RouterProvider,
} from 'react-router-dom'
import { useGetDecksQuery } from '@/services/base-api'
import { SignInPage, DecksPage } from './pages'
const publicRoutes: RouteObject[] = [
{
element: <Outlet />,
children: [
{
path: '/login',
element: <div>login</div>,
element: <SignInPage />,
},
],
},
]
const privateRoutes: RouteObject[] = [
{
path: '/',
element: <div>hello</div>,
element: <DecksPage />,
},
]
const router = createBrowserRouter([
{
element: <PrivateRoutes />,
@@ -30,14 +36,11 @@ const router = createBrowserRouter([
])
export const Router = () => {
const result = useGetDecksQuery()
console.log(result)
return <RouterProvider router={router} />
}
function PrivateRoutes() {
const isAuthenticated = false
const isAuthenticated = true
return isAuthenticated ? <Outlet /> : <Navigate to="/login" />
}

View File

@@ -9,13 +9,5 @@ export const baseApi = createApi({
headers.append('x-auth-skip', 'true')
},
}),
endpoints: builder => {
return {
getDecks: builder.query<any, void>({
query: () => `v1/decks`,
}),
}
},
endpoints: () => ({}),
})
export const { useGetDecksQuery } = baseApi

View File

@@ -0,0 +1,13 @@
import { DecksResponse } from './decks.types'
import { baseApi } from '@/services'
const decksService = baseApi.injectEndpoints({
endpoints: builder => ({
getDecks: builder.query<DecksResponse, void>({
query: () => `v1/decks`,
}),
}),
})
export const { useGetDecksQuery } = decksService

View File

@@ -0,0 +1,33 @@
export type Pagination = {
totalPages: number
currentPage: number
itemsPerPage: number
totalItems: number
}
export type Author = {
id: string
name: string
}
export type Deck = {
id: string
userId: string
name: string
isPrivate: boolean
shots: number
cover?: string | null
rating: number
isDeleted: boolean | null
isBlocked?: boolean | null
created: string
updated: string
cardsCount: number
author: Author
}
export type DecksResponse = {
maxCardsCount: number
pagination: Pagination
items: Deck[]
}

View File

@@ -0,0 +1,2 @@
export * from './decks.service'
export * from './decks.types'

1
src/services/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './base-api'

View File

@@ -39,3 +39,23 @@ body {
background-color: var(--color-dark-900);
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus input:-webkit-autofill,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
background: linear-gradient(
rgb(255 255 255 / 0%) 0%,
rgb(0 174 255 / 4%) 50%,
rgb(255 255 255 / 0%) 51%,
rgb(0 174 255 / 3%) 100%
);
box-shadow: 0 0 0 1000px transparent inset;
transition: background-color 5000s ease-in-out 0s;
-webkit-text-fill-color: var(--color-light-100);
}