diff --git a/src/client/App.css b/src/client/App.css index ea85bf4..53bf59d 100644 --- a/src/client/App.css +++ b/src/client/App.css @@ -1,12 +1,13 @@ #root { - background-color: var(--color-neutral1); + background-color: var(--color-background1); + color: var(--color-text); display: grid; padding: 8px; gap: 8px; > div { border-radius: 16px; - background-color: var(--color-neutral0); + background-color: var(--color-background0); padding: 16px; width: 100%; height: max-content; diff --git a/src/client/components/chart-cards/common/chart/cartesian-grid.tsx b/src/client/components/chart-cards/common/chart/cartesian-grid.tsx index fdc5dca..9d496dd 100644 --- a/src/client/components/chart-cards/common/chart/cartesian-grid.tsx +++ b/src/client/components/chart-cards/common/chart/cartesian-grid.tsx @@ -22,7 +22,7 @@ export const CartesianGrid = () => ( key={index} y1={Math.max(1, Math.min(height - 1, (height * index) / 4))} y2={Math.max(1, Math.min(height - 1, (height * index) / 4))} - stroke='var(--color-neutral1)' + stroke='var(--color-background1)' strokeWidth={1} /> ))} diff --git a/src/client/components/chart-cards/common/chart/index.css b/src/client/components/chart-cards/common/chart/index.css index 137e46a..b042cfd 100644 --- a/src/client/components/chart-cards/common/chart/index.css +++ b/src/client/components/chart-cards/common/chart/index.css @@ -1,5 +1,5 @@ .chart { - color: var(--color-neutral2); + color: var(--color-text-subdued); display: grid; font-size: 16px; gap: 4px; diff --git a/src/client/components/chart-cards/static.tsx b/src/client/components/chart-cards/static.tsx index 530952a..20e5b15 100644 --- a/src/client/components/chart-cards/static.tsx +++ b/src/client/components/chart-cards/static.tsx @@ -1,6 +1,7 @@ import { useAnimationFrame } from '@/hooks/use-animation-frame'; import { useQuery } from '@tanstack/react-query'; -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { Switch } from '../switch'; const formatUptime = (value: number) => { const seconds = String(Math.floor(value % 60)).padStart(2, '0'); @@ -37,6 +38,12 @@ const Uptime = ({ boot_time }: Pick) => { export const Static = () => { const { data: staticData } = useQuery({ queryKey: ['static'] }); + const root = useRef(document.getElementById('root')!); + const [dark, setDark] = useState(window.matchMedia('(prefers-color-scheme: dark)').matches); + + useEffect(() => { + root.current.setAttribute('data-theme', dark ? 'dark' : 'light'); + }, [dark]); return ( staticData && ( @@ -51,6 +58,12 @@ export const Static = () => { Kernel

{staticData.kernel_version}

{staticData.boot_time && } + + setDark(target.checked)} + /> ) ); diff --git a/src/client/components/switch/index.css b/src/client/components/switch/index.css new file mode 100644 index 0000000..afd7139 --- /dev/null +++ b/src/client/components/switch/index.css @@ -0,0 +1,46 @@ +.switch-wrapper { + display: grid; + grid-template-columns: repeat(2, max-content); + align-items: center; + gap: 8px; +} + +.switch { + position: relative; + width: 64px; + height: 32px; +} + +.switch input { + visibility: hidden; +} + + +.slider { + position: absolute; + cursor: pointer; + inset: 0; + background-color: var(--color-background2); + transition: 200ms background-color; + border-radius: 32px; +} + +.slider:before { + position: absolute; + content: ""; + aspect-ratio: 1; + left: 4px; + top: 4px; + bottom: 4px; + background-color: var(--color-background0); + transition: 200ms transform; + border-radius: 50%; +} + +input:checked + .slider { + background-color: var(--color-primary); +} + +input:checked + .slider:before { + transform: translateX(32px); +} diff --git a/src/client/components/switch/index.tsx b/src/client/components/switch/index.tsx new file mode 100644 index 0000000..716667e --- /dev/null +++ b/src/client/components/switch/index.tsx @@ -0,0 +1,18 @@ +import type { ComponentPropsWithoutRef } from 'react'; +import './index.css'; + +type Props = ComponentPropsWithoutRef<'input'> & { + label: string; +}; + +export const Switch = ({ label, ...rest }: Props) => { + return ( + + ); +}; diff --git a/src/client/index.css b/src/client/index.css index 69b7e80..5418f26 100644 --- a/src/client/index.css +++ b/src/client/index.css @@ -7,19 +7,34 @@ --color-neutral5: #93a1a1; --color-neutral6: #eee8d5; --color-neutral7: #fdf6e3; + --color-primary: #2aa198; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; - color: var(--color-neutral6); - font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } +[data-theme="light"] { + --color-background0: var(--color-neutral7); + --color-background1: var(--color-neutral6); + --color-background2: var(--color-neutral5); + --color-text: var(--color-neutral1); + --color-text-subdued: var(--color-neutral5); +} + +[data-theme="dark"] { + --color-background0: var(--color-neutral0); + --color-background1: var(--color-neutral1); + --color-background2: var(--color-neutral2); + --color-text: var(--color-neutral6); + --color-text-subdued: var(--color-neutral2); +} + body { margin: 0; }