Merge pull request #4886 from h3poteto/iss-4795/color-theme

refs #4795 Change color theme
This commit is contained in:
AkiraFukushima 2024-03-09 22:16:03 +09:00 committed by GitHub
commit 845c6381bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 445 additions and 134 deletions

View File

@ -166,7 +166,8 @@
"settings": {
"title": "Settings",
"language": "Language",
"font_size": "Font size"
"font_size": "Font size",
"theme": "Color theme"
},
"thirdparty": {
"title": "Third-party licenses"

View File

@ -57,6 +57,7 @@
"postcss": "^8.4.31",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.71.1",
"tailwindcss": "^3.3.3",
"typescript": "^5.2.2"
},

View File

@ -1,114 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.emojione {
display: inline-block;
width: 1.2rem;
height: 1.2rem;
}
.timeline-wrapper {
width: calc(100% - 16rem);
}
.timeline {
width: 100%;
}
.timeline-with-drawer {
width: calc(100% - 384px);
}
.detail {
width: 384px;
}
@media screen and (max-width: 1112px) {
.sidebar-menu {
display: none;
}
.my-profile {
visibility: hidden;
}
.sidebar {
width: 64px;
}
.timeline-wrapper {
width: calc(100% - 64px);
}
.sidebar-menu-item {
position: relative;
}
.sidebar-menu-item > span {
display: none;
}
.sidebar-toolchip {
position: absolute;
top: 2px;
right: 2px;
font-size: 7px;
}
.sidebar-toolchip > div {
padding: 2px 4px;
}
}
@media screen and (max-width: 996px) {
.timeline-with-drawer {
display: none;
}
.detail {
width: 100%;
}
}
.raw-html .invisible {
display: none;
}
.timeline-scrollable {
overflow-y: auto;
overflow-x: auto;
}
.timeline-scrollable::-webkit-scrollbar {
width: 6px !important;
height: 6px !important;
}
.timeline-scrollable::-webkit-scrollbar-thumb {
background-color: rgb(203 213 225);
border-radius: 4px !important;
opacity: 0;
}
.timeline-scrollable::-webkit-scrollbar-track {
border-radius: 4px !important;
background-color: rgb(241 245 249);
}
.timeline-scrollable:hover::-webkit-scrollbar-thumb {
opacity: 1;
}
button:focus {
outline: none;
}
a {
color: blue;
}
a:hover {
text-decoration: underline;
}

331
renderer/app.scss Normal file
View File

@ -0,0 +1,331 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.emojione {
display: inline-block;
width: 1.2rem;
height: 1.2rem;
}
.timeline-wrapper {
width: calc(100% - 16rem);
}
.timeline {
width: 100%;
}
.timeline-with-drawer {
width: calc(100% - 384px);
}
.detail {
width: 384px;
}
@media screen and (max-width: 1112px) {
.sidebar-menu {
display: none;
}
.my-profile {
visibility: hidden;
}
.sidebar {
width: 64px;
}
.timeline-wrapper {
width: calc(100% - 64px);
}
.sidebar-menu-item {
position: relative;
}
.sidebar-menu-item > span {
display: none;
}
.sidebar-toolchip {
position: absolute;
top: 2px;
right: 2px;
font-size: 7px;
}
.sidebar-toolchip > div {
padding: 2px 4px;
}
}
@media screen and (max-width: 996px) {
.timeline-with-drawer {
display: none;
}
.detail {
width: 100%;
}
}
.raw-html .invisible {
display: none;
}
.timeline-scrollable {
overflow-y: auto;
overflow-x: auto;
}
.timeline-scrollable::-webkit-scrollbar {
width: 6px !important;
height: 6px !important;
}
.timeline-scrollable::-webkit-scrollbar-thumb {
background-color: rgb(203 213 225);
border-radius: 4px !important;
opacity: 0;
}
.timeline-scrollable::-webkit-scrollbar-track {
border-radius: 4px !important;
background-color: rgb(241 245 249);
}
.timeline-scrollable:hover::-webkit-scrollbar-thumb {
opacity: 1;
}
button:focus {
outline: none;
}
a {
color: blue;
}
a:hover {
text-decoration: underline;
}
.theme-blue {
.theme-account-bg {
@apply bg-gradient-to-b;
@apply from-indigo-800;
@apply to-indigo-700;
}
.theme-bg {
@apply bg-blue-950;
}
.theme-bg-secondary {
@apply bg-blue-900;
}
.theme-text-primary {
@apply text-blue-100;
}
.theme-text-subtle {
@apply text-blue-600;
}
.theme-badge {
@apply bg-blue-600;
}
.theme-enabled {
@apply text-blue-400;
}
.theme-enabled:hover {
@apply text-blue-600;
}
}
.theme-orange {
.theme-account-bg {
@apply bg-gradient-to-b;
@apply from-orange-900;
@apply to-orange-800;
}
.theme-bg {
@apply bg-deep-orange-900;
}
.theme-bg-secondary {
@apply bg-orange-900;
}
.theme-text-primary {
@apply text-deep-orange-100;
}
.theme-text-subtle {
@apply text-deep-orange-600;
}
.theme-badge {
@apply bg-deep-orange-600;
}
.theme-enabled {
@apply text-deep-orange-400;
}
.theme-enabled:hover {
@apply text-deep-orange-600;
}
}
.theme-purple {
.theme-account-bg {
@apply bg-gradient-to-b;
@apply from-purple-900;
@apply to-purple-800;
}
.theme-bg {
@apply bg-deep-purple-900;
}
.theme-bg-secondary {
@apply bg-purple-900;
}
.theme-text-primary {
@apply text-deep-purple-100;
}
.theme-text-subtle {
@apply text-deep-purple-600;
}
.theme-badge {
@apply bg-deep-purple-600;
}
.theme-enabled {
@apply text-deep-purple-400;
}
.theme-enabled:hover {
@apply text-deep-purple-600;
}
}
.theme-green {
.theme-account-bg {
@apply bg-gradient-to-b;
@apply from-teal-900;
@apply to-teal-800;
}
.theme-bg {
@apply bg-green-900;
}
.theme-bg-secondary {
@apply bg-green-900;
}
.theme-text-primary {
@apply text-green-100;
}
.theme-text-subtle {
@apply text-green-600;
}
.theme-badge {
@apply bg-green-600;
}
.theme-enabled {
@apply text-green-400;
}
.theme-enabled:hover {
@apply text-green-600;
}
}
.theme-brown {
.theme-account-bg {
@apply bg-gradient-to-b;
@apply from-gray-900;
@apply to-gray-800;
}
.theme-bg {
@apply bg-brown-900;
}
.theme-bg-secondary {
@apply bg-brown-900;
}
.theme-text-primary {
@apply text-brown-100;
}
.theme-text-subtle {
@apply text-brown-600;
}
.theme-badge {
@apply bg-brown-600;
}
.theme-enabled {
@apply text-brown-400;
}
.theme-enabled:hover {
@apply text-brown-600;
}
}
.theme-gray {
.theme-account-bg {
@apply bg-gradient-to-b;
@apply from-gray-900;
@apply to-gray-800;
}
.theme-bg {
@apply bg-blue-gray-900;
}
.theme-bg-secondary {
@apply bg-blue-gray-900;
}
.theme-text-primary {
@apply text-blue-gray-100;
}
.theme-text-subtle {
@apply text-blue-gray-600;
}
.theme-badge {
@apply bg-blue-gray-600;
}
.theme-enabled {
@apply text-blue-gray-400;
}
.theme-enabled:hover {
@apply text-blue-gray-600;
}
}

View File

@ -24,9 +24,37 @@ const languages = [
}
]
const themes = [
{
label: 'Blue',
value: 'theme-blue'
},
{
label: 'Orange',
value: 'theme-orange'
},
{
label: 'Purple',
value: 'theme-purple'
},
{
label: 'Green',
value: 'theme-green'
},
{
label: 'Brown',
value: 'theme-brown'
},
{
label: 'Gray',
value: 'theme-gray'
}
]
export default function Settings(props: Props) {
const [language, setLanguage] = useState<localeType>('en')
const [fontSize, setFontSize] = useState<number>(16)
const [theme, setTheme] = useState<string>('theme-blue')
useEffect(() => {
if (typeof localStorage !== 'undefined') {
@ -55,6 +83,14 @@ export default function Settings(props: Props) {
props.reloadSettings()
}
const themeChanged = (e: string) => {
setTheme(e)
if (typeof localStorage !== 'undefined') {
localStorage.setItem('theme', e)
}
props.reloadSettings()
}
return (
<Dialog open={props.opened} handler={props.close} size="sm">
<DialogHeader>
@ -88,6 +124,22 @@ export default function Settings(props: Props) {
</Select>
</div>
</div>
<div>
<div className="mb-2">
<Typography>
<FormattedMessage id="settings.theme" />
</Typography>
</div>
<div>
<Select id="theme" onChange={themeChanged} value={theme}>
{themes.map(t => (
<Option key={t.value} value={t.value}>
{t.label}
</Option>
))}
</Select>
</div>
</div>
</div>
</DialogBody>
</Dialog>

View File

@ -349,12 +349,7 @@ export default function Compose(props: Props) {
</Popover>
{cw ? (
<IconButton
variant="text"
size="sm"
className="text-blue-400 hover:text-blue-600 leading-4 text-base"
onClick={() => setCW(false)}
>
<IconButton variant="text" size="sm" className="theme-enabled leading-4 text-base" onClick={() => setCW(false)}>
CW
</IconButton>
) : (

View File

@ -46,7 +46,7 @@ export default function Detail(props: Props) {
<>
{target && (
<div className={`bg-white ${props.className}`} style={props.style}>
<div className="bg-blue-900 text-gray-200 flex justify-between p-2 items-center" style={{ height: '44px' }}>
<div className="theme-bg-secondary text-gray-200 flex justify-between p-2 items-center" style={{ height: '44px' }}>
<div className="flex gap-4 items-center">
<button className="text-lg" title={formatMessage({ id: 'detail.back' })}>
<FaChevronLeft onClick={back} />

View File

@ -33,6 +33,7 @@ export default function Layout({ children }: LayoutProps) {
const [openThirdparty, setOpenThirdparty] = useState(false)
const [style, setStyle] = useState<CSSProperties>({})
const [openPopover, setOpenPopover] = useState(false)
const [theme, setTheme] = useState('theme-blue')
const { switchLang } = useContext(Context)
const router = useRouter()
@ -100,7 +101,7 @@ export default function Layout({ children }: LayoutProps) {
const selectedClassName = (id: number) => {
if (id === parseInt(router.query.id as string)) {
return 'bg-blue-950 cursor-pointer text-center'
return 'theme-bg cursor-pointer text-center'
} else {
return 'cursor-pointer text-center'
}
@ -116,13 +117,17 @@ export default function Layout({ children }: LayoutProps) {
fontSize: `${fontSize}px`
})
}
const t = localStorage.getItem('theme')
if (t && t.length > 0) {
setTheme(t)
}
}
}
return (
<div className="app flex flex-col min-h-screen" style={style}>
<div className={`app flex flex-col min-h-screen ${theme}`} style={style}>
<main className="flex w-full box-border my-0 mx-auto min-h-screen">
<aside className="w-16 bg-gray-900 flex flex-col justify-between">
<aside className="w-16 theme-account-bg flex flex-col justify-between">
<div>
{accounts.map(account => (
<div key={account.id} className={selectedClassName(account.id)}>

View File

@ -94,7 +94,7 @@ export default function Layout({ children }: LayoutProps) {
return (
<section className="flex h-screen w-full overflow-hidden">
<Jump opened={openJump} close={() => setOpenJump(false)} timelines={pages} />
<Card className="text-blue-100 sidebar w-64 bg-blue-950 rounded-none">
<Card className="theme-text-primary sidebar w-64 theme-bg rounded-none">
<div className="max-w-full pl-4 mt-2 mb-4 my-profile">
<p>{account?.username}</p>
<p>@{account?.domain}</p>
@ -105,7 +105,7 @@ export default function Layout({ children }: LayoutProps) {
key={page.id}
selected={router.asPath.includes(page.path)}
onClick={() => router.push(page.path)}
className="sidebar-menu-item text-blue-100 overflow-hidden whitespace-nowrap"
className="sidebar-menu-item theme-text-primary overflow-hidden whitespace-nowrap"
title={page.title}
>
<ListItemPrefix>{page.icon}</ListItemPrefix>
@ -116,7 +116,7 @@ export default function Layout({ children }: LayoutProps) {
value={unreads[account.id.toString()]}
variant="ghost"
size="sm"
className="rounded-full text-blue-100 bg-blue-600"
className="rounded-full theme-text-primary theme-badge"
/>
</ListItemSuffix>
) : null}
@ -127,7 +127,7 @@ export default function Layout({ children }: LayoutProps) {
key={list.id}
selected={router.asPath.includes(`list_${list.id}`)}
onClick={() => router.push({ pathname: `/accounts/${router.query.id}/list_${list.id}` })}
className="sidebar-menu-item text-blue-100 overflow-hidden whitespace-nowrap"
className="sidebar-menu-item theme-text-primary overflow-hidden whitespace-nowrap"
title={list.title}
>
<ListItemPrefix>

View File

@ -183,7 +183,7 @@ export default function Notifications(props: Props) {
return (
<div className="flex timeline-wrapper">
<section className={`h-full ${timelineClass()}`}>
<div className="w-full bg-blue-950 text-blue-100 p-2 flex justify-between">
<div className="w-full theme-bg theme-text-primary p-2 flex justify-between">
<div className="text-lg font-bold cursor-pointer" onClick={() => backToTop()}>
<FormattedMessage id="timeline.notifications" />
</div>

View File

@ -244,7 +244,7 @@ export default function Timeline(props: Props) {
return (
<div className="flex timeline-wrapper">
<section className={`h-full ${timelineClass()}`}>
<div className="w-full bg-blue-950 text-blue-100 p-2 flex justify-between">
<div className="w-full theme-bg theme-text-primary p-2 flex justify-between">
<div className="text-lg font-bold cursor-pointer" onClick={() => backToTop()}>
{props.timeline.match(/list_(\d+)/) ? <>{list && list.title}</> : <FormattedMessage id={`timeline.${props.timeline}`} />}
</div>

View File

@ -81,7 +81,7 @@ export default function Status(props: Props) {
return (
<div className="border-b mr-2 py-2 text-center">
<FormattedMessage id="timeline.status.filtered" />
<span className="text-blue-700 cursor-pointer pl-4" onClick={() => setIgnoreFilter(true)}>
<span className="theme-text-subtle cursor-pointer pl-4" onClick={() => setIgnoreFilter(true)}>
<FormattedMessage id="timeline.status.show_anyway" />
</span>
</div>

View File

@ -1,5 +1,5 @@
import type { AppProps } from 'next/app'
import '../app.css'
import '../app.scss'
import AccountLayout from '@/components/layouts/account'
import TimelineLayout from '@/components/layouts/timelines'
import { IntlProviderWrapper } from '@/provider/i18n'

View File

@ -2724,6 +2724,7 @@ __metadata:
react-intl: ^6.5.1
react-virtuoso: ^4.6.2
sanitize-html: ^2.11.0
sass: ^1.71.1
tailwindcss: ^3.3.3
typescript: ^5.2.2
languageName: unknown
@ -3580,6 +3581,25 @@ __metadata:
languageName: node
linkType: hard
"chokidar@npm:>=3.0.0 <4.0.0":
version: 3.6.0
resolution: "chokidar@npm:3.6.0"
dependencies:
anymatch: ~3.1.2
braces: ~3.0.2
fsevents: ~2.3.2
glob-parent: ~5.1.2
is-binary-path: ~2.1.0
is-glob: ~4.0.1
normalize-path: ~3.0.0
readdirp: ~3.6.0
dependenciesMeta:
fsevents:
optional: true
checksum: d2f29f499705dcd4f6f3bbed79a9ce2388cf530460122eed3b9c48efeab7a4e28739c6551fd15bec9245c6b9eeca7a32baa64694d64d9b6faeb74ddb8c4a413d
languageName: node
linkType: hard
"chokidar@npm:^3.5.3":
version: 3.5.3
resolution: "chokidar@npm:3.5.3"
@ -5605,6 +5625,13 @@ __metadata:
languageName: node
linkType: hard
"immutable@npm:^4.0.0":
version: 4.3.5
resolution: "immutable@npm:4.3.5"
checksum: 0e25dd5c314421faede9e1122ab26cdb638cc3edc8678c4a75dee104279b12621a30c80a480fae7f68bc7e81672f1e672e454dc0fdc7e6cf0af10809348387b8
languageName: node
linkType: hard
"import-fresh@npm:^3.2.1":
version: 3.3.0
resolution: "import-fresh@npm:3.3.0"
@ -7967,6 +7994,19 @@ __metadata:
languageName: node
linkType: hard
"sass@npm:^1.71.1":
version: 1.71.1
resolution: "sass@npm:1.71.1"
dependencies:
chokidar: ">=3.0.0 <4.0.0"
immutable: ^4.0.0
source-map-js: ">=0.6.2 <2.0.0"
bin:
sass: sass.js
checksum: 19c4939d3042eb9459d462bbd27b1f576fa18034e23c87ca0005b87effdee431c16503b5a785edcdcde1a76dfb804716d9ad42c85a78968ac3825d515e45cb53
languageName: node
linkType: hard
"sax@npm:^1.2.4":
version: 1.3.0
resolution: "sax@npm:1.3.0"
@ -8193,7 +8233,7 @@ __metadata:
languageName: node
linkType: hard
"source-map-js@npm:^1.0.2":
"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.2":
version: 1.0.2
resolution: "source-map-js@npm:1.0.2"
checksum: c049a7fc4deb9a7e9b481ae3d424cc793cb4845daa690bc5a05d428bf41bf231ced49b4cf0c9e77f9d42fdb3d20d6187619fc586605f5eabe995a316da8d377c