mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-25 04:40:15 +01:00
feat: integrate Tailwind CSS and update theme context for improved styling
This commit is contained in:
parent
1c3f5b972d
commit
6e7fdeb3a3
8 changed files with 476 additions and 143 deletions
208
package-lock.json
generated
208
package-lock.json
generated
|
|
@ -4595,44 +4595,6 @@
|
|||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/autoprefixer": {
|
||||
"version": "10.4.20",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.23.3",
|
||||
"caniuse-lite": "^1.0.30001646",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"autoprefixer": "bin/autoprefixer"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/babel-plugin-polyfill-corejs2": {
|
||||
"version": "0.4.12",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz",
|
||||
|
|
@ -4746,23 +4708,6 @@
|
|||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"client/node_modules/electron-to-chromium": {
|
||||
"version": "1.5.103",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.103.tgz",
|
||||
"integrity": "sha512-P6+XzIkfndgsrjROJWfSvVEgNHtPgbhVyTkwLjUM2HU/h7pZRORgaTlHqfAikqxKmdJMLW8fftrdGWbd/Ds0FA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"client/node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"client/node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
|
|
@ -4773,13 +4718,6 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"client/node_modules/node-releases": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"client/node_modules/react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
|
|
@ -27104,6 +27042,36 @@
|
|||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
|
||||
"integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"postcss-selector-parser": "6.0.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/match-sorter-utils": {
|
||||
"version": "8.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.11.8.tgz",
|
||||
|
|
@ -28855,9 +28823,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.17",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz",
|
||||
"integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==",
|
||||
"version": "10.4.21",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
||||
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -28873,12 +28841,13 @@
|
|||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.22.2",
|
||||
"caniuse-lite": "^1.0.30001578",
|
||||
"browserslist": "^4.24.4",
|
||||
"caniuse-lite": "^1.0.30001702",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -28891,6 +28860,70 @@
|
|||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer/node_modules/browserslist": {
|
||||
"version": "4.25.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
||||
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer/node_modules/update-browserslist-db": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
||||
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"escalade": "^3.2.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"update-browserslist-db": "cli.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
|
|
@ -29668,9 +29701,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001706",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001706.tgz",
|
||||
"integrity": "sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==",
|
||||
"version": "1.0.30001727",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
|
||||
"integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -31607,9 +31640,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.28",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.28.tgz",
|
||||
"integrity": "sha512-VufdJl+rzaKZoYVUijN13QcXVF5dWPZANeFTLNy+OSpHdDL5ynXTF35+60RSBbaQYB1ae723lQXHCrf4pyLsMw=="
|
||||
"version": "1.5.187",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz",
|
||||
"integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/elliptic": {
|
||||
"version": "6.6.1",
|
||||
|
|
@ -32044,9 +32078,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
|
@ -37623,6 +37658,13 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
||||
},
|
||||
"node_modules/lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
|
|
@ -39964,9 +40006,10 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
||||
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-stdlib-browser": {
|
||||
"version": "1.3.1",
|
||||
|
|
@ -49823,15 +49866,20 @@
|
|||
"@rollup/plugin-node-resolve": "^15.0.0",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.31",
|
||||
"react-hook-form": "^7.60.0",
|
||||
"rimraf": "^5.0.1",
|
||||
"rollup": "^4.0.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-typescript2": "^0.35.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tailwindcss-animate": "^1.0.5",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -70,15 +70,20 @@
|
|||
"@rollup/plugin-node-resolve": "^15.0.0",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.31",
|
||||
"react-hook-form": "^7.60.0",
|
||||
"rimraf": "^5.0.1",
|
||||
"rollup": "^4.0.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-typescript2": "^0.35.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tailwindcss-animate": "^1.0.5",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
8
packages/client/postcss.config.js
Normal file
8
packages/client/postcss.config.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
module.exports = {
|
||||
plugins: [
|
||||
require('postcss-import'),
|
||||
require('postcss-preset-env'),
|
||||
require('tailwindcss'),
|
||||
require('autoprefixer'),
|
||||
],
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@ export * from './AlertDialog';
|
|||
export * from './Breadcrumb';
|
||||
export * from './Button';
|
||||
export * from './Checkbox';
|
||||
export * from './Collapsible';
|
||||
export * from './DataTableColumnHeader';
|
||||
export * from './Dialog';
|
||||
export * from './DropdownMenu';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
//ThemeContext.js
|
||||
// source: https://plainenglish.io/blog/light-and-dark-mode-in-react-web-application-with-tailwind-css-89674496b942
|
||||
import { useSetAtom } from 'jotai';
|
||||
import React, { createContext, useState, useEffect } from 'react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { getInitialTheme, applyFontSize } from '~/utils';
|
||||
import { fontSizeAtom } from '~/store';
|
||||
|
||||
|
|
@ -10,21 +8,10 @@ type ProviderValue = {
|
|||
setTheme: React.Dispatch<React.SetStateAction<string>>;
|
||||
};
|
||||
|
||||
const defaultContextValue: ProviderValue = {
|
||||
theme: getInitialTheme(),
|
||||
setTheme: () => {
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
export const isDark = (theme: string): boolean => {
|
||||
if (theme === 'system') {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return theme === 'dark';
|
||||
};
|
||||
|
||||
export const ThemeContext = createContext<ProviderValue>(defaultContextValue);
|
||||
export const ThemeContext = createContext<ProviderValue>({
|
||||
theme: 'light',
|
||||
setTheme: () => {},
|
||||
});
|
||||
|
||||
export const ThemeProvider = ({
|
||||
initialTheme,
|
||||
|
|
@ -33,56 +20,35 @@ export const ThemeProvider = ({
|
|||
initialTheme?: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const [theme, setTheme] = useState(getInitialTheme);
|
||||
const [theme, setTheme] = useState<string>(() => initialTheme ?? getInitialTheme());
|
||||
const setFontSize = useSetAtom(fontSizeAtom);
|
||||
|
||||
const rawSetTheme = (rawTheme: string) => {
|
||||
const root = window.document.documentElement;
|
||||
const darkMode = isDark(rawTheme);
|
||||
|
||||
root.classList.remove(darkMode ? 'light' : 'dark');
|
||||
root.classList.add(darkMode ? 'dark' : 'light');
|
||||
|
||||
localStorage.setItem('color-theme', rawTheme);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const changeThemeOnSystemChange = () => {
|
||||
rawSetTheme(mediaQuery.matches ? 'dark' : 'light');
|
||||
};
|
||||
const root = document.documentElement;
|
||||
const darkMode =
|
||||
theme === 'system'
|
||||
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
: theme === 'dark';
|
||||
|
||||
mediaQuery.addEventListener('change', changeThemeOnSystemChange);
|
||||
|
||||
return () => {
|
||||
mediaQuery.removeEventListener('change', changeThemeOnSystemChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fontSize = localStorage.getItem('fontSize');
|
||||
if (fontSize == null) {
|
||||
setFontSize('text-base');
|
||||
applyFontSize('text-base');
|
||||
localStorage.setItem('fontSize', JSON.stringify('text-base'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
applyFontSize(JSON.parse(fontSize));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
// Reason: This effect should only run once, and `setFontSize` is a stable function
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (initialTheme) {
|
||||
rawSetTheme(initialTheme);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
rawSetTheme(theme);
|
||||
root.classList.toggle('dark', darkMode);
|
||||
root.classList.toggle('light', !darkMode);
|
||||
localStorage.setItem('color-theme', theme);
|
||||
}, [theme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (theme !== 'system') return;
|
||||
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const handler = (e: MediaQueryListEvent) => setTheme(e.matches ? 'dark' : 'light');
|
||||
|
||||
mq.addEventListener('change', handler);
|
||||
return () => mq.removeEventListener('change', handler);
|
||||
}, [theme]);
|
||||
|
||||
useEffect(() => {
|
||||
const saved = localStorage.getItem('fontSize') || 'text-base';
|
||||
applyFontSize(saved);
|
||||
setFontSize(saved);
|
||||
}, [setFontSize]);
|
||||
|
||||
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
|
||||
};
|
||||
|
|
|
|||
172
packages/client/src/index.css
Normal file
172
packages/client/src/index.css
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* Custom Variables */
|
||||
:root {
|
||||
--white: #fff;
|
||||
--black: #000;
|
||||
--gray-20: #ececf1;
|
||||
--gray-50: #f7f7f8;
|
||||
--gray-100: #ececec;
|
||||
--gray-200: #e3e3e3;
|
||||
--gray-300: #cdcdcd;
|
||||
--gray-400: #999696;
|
||||
--gray-500: #595959;
|
||||
--gray-600: #424242;
|
||||
--gray-700: #2f2f2f;
|
||||
--gray-800: #212121;
|
||||
--gray-850: #171717;
|
||||
--gray-900: #0d0d0d;
|
||||
--green-50: #ecfdf5;
|
||||
--green-100: #d1fae5;
|
||||
--green-200: #a7f3d0;
|
||||
--green-300: #6ee7b7;
|
||||
--green-400: #34d399;
|
||||
--green-500: #10b981;
|
||||
--green-600: #059669;
|
||||
--green-700: #047857;
|
||||
--green-800: #065f46;
|
||||
--green-900: #064e3b;
|
||||
--green-950: #022c22;
|
||||
--red-50: #fef2f2;
|
||||
--red-100: #fee2e2;
|
||||
--red-200: #fecaca;
|
||||
--red-300: #fca5a5;
|
||||
--red-400: #f87171;
|
||||
--red-500: #ef4444;
|
||||
--red-600: #dc2626;
|
||||
--red-700: #b91c1c;
|
||||
--red-800: #991b1b;
|
||||
--red-900: #7f1d1d;
|
||||
--red-950: #450a0a;
|
||||
--amber-50: #fffbeb;
|
||||
--amber-100: #fef3c7;
|
||||
--amber-200: #fde68a;
|
||||
--amber-300: #fcd34d;
|
||||
--amber-400: #fbbf24;
|
||||
--amber-500: #f59e0b;
|
||||
--amber-600: #d97706;
|
||||
--amber-700: #b45309;
|
||||
--amber-800: #92400e;
|
||||
--amber-900: #78350f;
|
||||
--amber-950: #451a03;
|
||||
}
|
||||
|
||||
html {
|
||||
--presentation: var(--white);
|
||||
--text-primary: var(--gray-800);
|
||||
--text-secondary: var(--gray-600);
|
||||
--text-secondary-alt: var(--gray-500);
|
||||
--text-tertiary: var(--gray-500);
|
||||
--text-warning: var(--amber-500);
|
||||
--ring-primary: var(--gray-500);
|
||||
--header-primary: var(--white);
|
||||
--header-hover: var(--gray-50);
|
||||
--header-button-hover: var(--gray-50);
|
||||
--surface-active: var(--gray-100);
|
||||
--surface-active-alt: var(--gray-200);
|
||||
--surface-hover: var(--gray-200);
|
||||
--surface-hover-alt: var(--gray-300);
|
||||
--surface-primary: var(--white);
|
||||
--surface-primary-alt: var(--gray-50);
|
||||
--surface-primary-contrast: var(--gray-100);
|
||||
--surface-secondary: var(--gray-50);
|
||||
--surface-secondary-alt: var(--gray-200);
|
||||
--surface-tertiary: var(--gray-100);
|
||||
--surface-tertiary-alt: var(--white);
|
||||
--surface-dialog: var(--white);
|
||||
--surface-submit: var(--green-700);
|
||||
--surface-submit-hover: var(--green-800);
|
||||
--surface-destructive: var(--red-700);
|
||||
--surface-destructive-hover: var(--red-800);
|
||||
--surface-chat: var(--white);
|
||||
--border-light: var(--gray-200);
|
||||
--border-medium-alt: var(--gray-300);
|
||||
--border-medium: var(--gray-300);
|
||||
--border-heavy: var(--gray-400);
|
||||
--border-xheavy: var(--gray-500);
|
||||
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 0 0% 3.9%;
|
||||
--primary: 0 0% 9%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 0 0% 96.1%;
|
||||
--secondary-foreground: 0 0% 9%;
|
||||
--muted: 0 0% 96.1%;
|
||||
--muted-foreground: 0 0% 45.1%;
|
||||
--accent: 0 0% 96.1%;
|
||||
--accent-foreground: 0 0% 9%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 89.8%;
|
||||
--input: 0 0% 89.8%;
|
||||
--ring: 0 0% 3.9%;
|
||||
--radius: 0.5rem;
|
||||
--chart-1: 12 76% 61%;
|
||||
--chart-2: 173 58% 39%;
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
--switch-unchecked: 0 0% 58%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--presentation: var(--gray-800);
|
||||
--text-primary: var(--gray-100);
|
||||
--text-secondary: var(--gray-300);
|
||||
--text-secondary-alt: var(--gray-400);
|
||||
--text-tertiary: var(--gray-500);
|
||||
--text-warning: var(--amber-500);
|
||||
--header-primary: var(--gray-700);
|
||||
--header-hover: var(--gray-600);
|
||||
--header-button-hover: var(--gray-700);
|
||||
--surface-active: var(--gray-500);
|
||||
--surface-active-alt: var(--gray-700);
|
||||
--surface-hover: var(--gray-600);
|
||||
--surface-hover-alt: var(--gray-600);
|
||||
--surface-primary: var(--gray-900);
|
||||
--surface-primary-alt: var(--gray-850);
|
||||
--surface-primary-contrast: var(--gray-850);
|
||||
--surface-secondary: var(--gray-800);
|
||||
--surface-secondary-alt: var(--gray-800);
|
||||
--surface-tertiary: var(--gray-700);
|
||||
--surface-tertiary-alt: var(--gray-700);
|
||||
--surface-dialog: var(--gray-850);
|
||||
--surface-submit: var(--green-700);
|
||||
--surface-submit-hover: var(--green-800);
|
||||
--surface-destructive: var(--red-800);
|
||||
--surface-destructive-hover: var(--red-900);
|
||||
--surface-chat: var(--gray-700);
|
||||
--border-light: var(--gray-700);
|
||||
--border-medium-alt: var(--gray-600);
|
||||
--border-medium: var(--gray-600);
|
||||
--border-heavy: var(--gray-500);
|
||||
--border-xheavy: var(--gray-400);
|
||||
|
||||
--background: 0 0% 7%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 0 0% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 0 0% 9%;
|
||||
--secondary: 0 0% 14.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 14.9%;
|
||||
--muted-foreground: 0 0% 63.9%;
|
||||
--accent: 0 0% 14.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 40.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 14.9%;
|
||||
--input: 0 0% 14.9%;
|
||||
--ring: 0 0% 83.1%;
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-2: 160 60% 45%;
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
--switch-unchecked: 0 0% 40%;
|
||||
}
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
// Styles
|
||||
import './index.css';
|
||||
|
||||
// Components
|
||||
export * from './components';
|
||||
|
||||
|
|
|
|||
130
packages/client/tailwind.config.js
Normal file
130
packages/client/tailwind.config.js
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||
darkMode: ['class'],
|
||||
theme: {
|
||||
extend: {
|
||||
width: {
|
||||
authPageWidth: '370px',
|
||||
},
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: { height: 0 },
|
||||
to: { height: 'var(--radix-accordion-content-height)' },
|
||||
},
|
||||
'accordion-up': {
|
||||
from: { height: 'var(--radix-accordion-content-height)' },
|
||||
to: { height: 0 },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'fade-in': 'fadeIn 0.5s ease-out forwards',
|
||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
||||
},
|
||||
colors: {
|
||||
gray: {
|
||||
20: '#ececf1',
|
||||
50: '#f7f7f8',
|
||||
100: '#ececec',
|
||||
200: '#e3e3e3',
|
||||
300: '#cdcdcd',
|
||||
400: '#999696',
|
||||
500: '#595959',
|
||||
600: '#424242',
|
||||
700: '#2f2f2f',
|
||||
800: '#212121',
|
||||
850: '#171717',
|
||||
900: '#0d0d0d',
|
||||
},
|
||||
green: {
|
||||
50: '#f1f9f7',
|
||||
100: '#def2ed',
|
||||
200: '#a6e5d6',
|
||||
300: '#6dc8b9',
|
||||
400: '#41a79d',
|
||||
500: '#10a37f',
|
||||
550: '#349072',
|
||||
600: '#126e6b',
|
||||
700: '#0a4f53',
|
||||
800: '#06373e',
|
||||
900: '#031f29',
|
||||
},
|
||||
'brand-purple': '#ab68ff',
|
||||
presentation: 'var(--presentation)',
|
||||
'text-primary': 'var(--text-primary)',
|
||||
'text-secondary': 'var(--text-secondary)',
|
||||
'text-secondary-alt': 'var(--text-secondary-alt)',
|
||||
'text-tertiary': 'var(--text-tertiary)',
|
||||
'text-warning': 'var(--text-warning)',
|
||||
'ring-primary': 'var(--ring-primary)',
|
||||
'header-primary': 'var(--header-primary)',
|
||||
'header-hover': 'var(--header-hover)',
|
||||
'header-button-hover': 'var(--header-button-hover)',
|
||||
'surface-active': 'var(--surface-active)',
|
||||
'surface-active-alt': 'var(--surface-active-alt)',
|
||||
'surface-hover': 'var(--surface-hover)',
|
||||
'surface-hover-alt': 'var(--surface-hover-alt)',
|
||||
'surface-primary': 'var(--surface-primary)',
|
||||
'surface-primary-alt': 'var(--surface-primary-alt)',
|
||||
'surface-primary-contrast': 'var(--surface-primary-contrast)',
|
||||
'surface-secondary': 'var(--surface-secondary)',
|
||||
'surface-secondary-alt': 'var(--surface-secondary-alt)',
|
||||
'surface-tertiary': 'var(--surface-tertiary)',
|
||||
'surface-tertiary-alt': 'var(--surface-tertiary-alt)',
|
||||
'surface-dialog': 'var(--surface-dialog)',
|
||||
'surface-submit': 'var(--surface-submit)',
|
||||
'surface-submit-hover': 'var(--surface-submit-hover)',
|
||||
'surface-destructive': 'var(--surface-destructive)',
|
||||
'surface-destructive-hover': 'var(--surface-destructive-hover)',
|
||||
'surface-chat': 'var(--surface-chat)',
|
||||
'border-light': 'var(--border-light)',
|
||||
'border-medium': 'var(--border-medium)',
|
||||
'border-medium-alt': 'var(--border-medium-alt)',
|
||||
'border-heavy': 'var(--border-heavy)',
|
||||
'border-xheavy': 'var(--border-xheavy)',
|
||||
/* These are test styles */
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
['switch-unchecked']: 'hsl(var(--switch-unchecked))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))',
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))',
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))',
|
||||
},
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))',
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('tailwindcss-animate'),
|
||||
require('tailwindcss-radix')(),
|
||||
// require('@tailwindcss/typography'),
|
||||
],
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue