Skip to content

Commit

Permalink
feat: dark mode (#179)
Browse files Browse the repository at this point in the history
* feat: dark mode

* fix: edge case

* fix: calendarium logo in layout

* fix: mismatch in client/server rendering of logo

* fix: status bar color for pwa in ios

* feat: add follow system option to dark mode

* fix: "Dark Mode" -> "Appearance"

* fix: formatting

* fix: formatting
  • Loading branch information
diogogmatos authored Jan 30, 2024
1 parent 4aed193 commit 2ec8417
Show file tree
Hide file tree
Showing 23 changed files with 264 additions and 88 deletions.
14 changes: 7 additions & 7 deletions components/CalendarExportModal/CalendarExportModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const CalendarExportModal = ({
>
<Fade in={isOpen} timeout={400}>
<Box
className="absolute left-1/2 top-1/2 h-fit w-full -translate-x-1/2 -translate-y-1/2 transform overflow-y-auto rounded-2xl border bg-white p-6 text-center shadow-xl sm:w-96"
className="absolute left-1/2 top-1/2 h-fit w-full -translate-x-1/2 -translate-y-1/2 transform overflow-y-auto rounded-2xl border dark:border-neutral-400/20 bg-white dark:bg-neutral-800 p-6 text-center shadow-xl sm:w-96"
style={{
maxHeight: "calc(100% - 4rem)",
maxWidth: "calc(100% - 4rem)",
Expand Down Expand Up @@ -167,7 +167,7 @@ const CalendarExportModal = ({
className="relative flex flex-grow items-stretch focus-within:z-10"
style={{ width: "calc(100% - 38px)" }}
>
<div className="block w-full rounded-none rounded-l-lg border-0 py-1.5 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-cesium-900 sm:text-sm sm:leading-6">
<div className="block w-full rounded-none rounded-l-lg border-0 py-1.5 ring-1 ring-inset ring-neutral-300 dark:ring-neutral-400/30 focus:ring-2 focus:ring-inset focus:ring-cesium-900 sm:text-sm sm:leading-6">
<div className="mx-2 overflow-y-hidden overflow-x-scroll whitespace-nowrap text-start">
{URL}
</div>
Expand All @@ -176,7 +176,7 @@ const CalendarExportModal = ({
<button
type="button"
title={isCopied ? "Copied" : "Copy"}
className="relative -ml-px inline-flex w-[38px] place-content-center items-center gap-x-1.5 rounded-r-lg px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
className="relative -ml-px inline-flex w-[38px] place-content-center items-center gap-x-1.5 rounded-r-lg px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-neutral-300 dark:ring-neutral-400/30 dark:hover:bg-neutral-400/10 hover:bg-neutral-50"
onClick={copyToClipboard}
>
{isCopied ? (
Expand All @@ -188,7 +188,7 @@ const CalendarExportModal = ({
</div>
</div>
</span>
<Collapse className="w-full rounded-lg border-gray-300 bg-white text-left font-display shadow-sm">
<Collapse className="w-full rounded-lg border-neutral-300 bg-white dark:border-neutral-400/30 text-left font-display shadow-sm">
<Collapse.Panel header="How does it work?" key="1">
<div className="text-justify">
<p>
Expand Down Expand Up @@ -234,7 +234,7 @@ const CalendarExportModal = ({
</Collapse>
<Collapse
accordion
className="w-full rounded-lg border-gray-300 bg-white text-left font-display shadow-sm"
className="w-full rounded-lg border-neutral-300 bg-white text-left font-display shadow-sm"
>
<Collapse.Panel header="Google Calendar" key="1">
<div className="">
Expand Down Expand Up @@ -341,7 +341,7 @@ const CalendarExportModal = ({
</div>
</Collapse.Panel>
</Collapse>
<div className="cursor-pointer select-none text-sm text-gray-500">
<div className="cursor-pointer select-none text-sm text-neutral-500 dark:text-neutral-400">
{isHome ? (
<div title="To export your schedule please go to /schedule.">
<i className="bi bi-info-circle-fill"></i> Currently
Expand All @@ -356,7 +356,7 @@ const CalendarExportModal = ({
)}
</div>

<div className="cursor-pointer select-none text-sm text-gray-500">
<div className="cursor-pointer select-none text-sm text-neutral-500 dark:text-neutral-400">
<i className="bi bi-lightbulb"></i> You can also{" "}
<a className="font-medium text-blue-400" href={URL}>
download as .ics file
Expand Down
2 changes: 1 addition & 1 deletion components/ClearScheduleButton/ClearScheduleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const ClearScheduleButton = ({
disabled={isSettings}
>
<button
className={`h-10 w-10 rounded-xl p-2 font-medium leading-3 text-error/50 shadow-md ring-1 ring-zinc-200/50 transition-all duration-300 hover:text-error hover:shadow-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 ${
className={`h-10 w-10 rounded-xl p-2 font-medium leading-3 text-error/50 dark:text-red-400/60 shadow-md ring-1 ring-neutral-200/50 dark:ring-neutral-400/20 transition-all duration-300 hover:text-error hover:shadow-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 dark:bg-neutral-800/70 ${
isSettings && "cursor-not-allowed hover:text-error/50"
}`}
type="button"
Expand Down
31 changes: 31 additions & 0 deletions components/DarkModeToggler/DarkModeToggler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Switch } from '@headlessui/react'
import { useTheme } from 'next-themes';

function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}

const DarkModeToggler = () => {
const { theme, setTheme } = useTheme();

return (
<div>
<label className="block text-sm font-medium leading-6">
Appearance
</label>
<select
id="theme"
name="theme"
className="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 ring-1 ring-inset ring-neutral-300 dark:ring-neutral-400/20 focus:ring-2 focus:ring-cesium-900 dark:bg-neutral-800"
value={theme === "light" ? "Light" : theme === "dark" ? "Dark" : "Follow System"}
onChange={(e) => setTheme(e.target.value === "Light" ? "light" : e.target.value === "Dark" ? "dark" : "system")}
>
<option>Follow System</option>
<option>Dark</option>
<option>Light</option>
</select>
</div>
)
}

export default DarkModeToggler;
1 change: 1 addition & 0 deletions components/DarkModeToggler/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './DarkModeToggler';
4 changes: 2 additions & 2 deletions components/EventModal/EventModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ function EventModal({
>
<Fade in={inspectEvent} timeout={400}>
<Box
className="absolute left-1/2 top-1/2 h-fit w-fit min-w-[18rem] max-w-full -translate-x-1/2 -translate-y-1/2 transform rounded-2xl border border-zinc-200 bg-white p-6 text-center shadow-xl"
className="absolute left-1/2 top-1/2 h-fit w-fit min-w-[18rem] max-w-full -translate-x-1/2 -translate-y-1/2 transform rounded-2xl border border-neutral-200 bg-white dark:bg-neutral-800 dark:border-neutral-400/20 p-6 text-center shadow-xl"
style={{ maxWidth: "calc(100% - 4rem)" }}
>
<div className="space-y-4 font-display">
<div className="m-auto w-fit border-b pb-4">
<div className="m-auto w-fit border-b dark:border-neutral-400/30 pb-4">
<span id="modal-modal-title" className="text-xl font-medium">
{selectedEvent.title}
</span>
Expand Down
6 changes: 3 additions & 3 deletions components/ExportButton/ExportButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ const ExportButton = ({ exportPDF, isHome, filters }: ExportButtonProps) => {
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 z-10 mt-2 w-fit origin-top-right overflow-hidden rounded-xl bg-white font-medium text-cesium-900 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<Menu.Items className="absolute right-0 z-10 mt-2 w-fit origin-top-right overflow-hidden rounded-xl bg-white dark:bg-neutral-800 font-medium text-cesium-900 dark:text-cesium-600 shadow-lg ring-1 ring-black dark:ring-white/20 ring-opacity-5 focus:outline-none">
<div className="grid grid-cols-1 py-1">
<Menu.Item>
{({ active }) => (
<button
className={`${
active && "bg-cesium-100"
active && "bg-cesium-100 dark:bg-neutral-100/10"
} 'block text-sm' px-4 py-2`}
title="Add your events to your favorite calendar app."
onClick={() => setIsModalOpen(true)}
Expand All @@ -54,7 +54,7 @@ const ExportButton = ({ exportPDF, isHome, filters }: ExportButtonProps) => {
{({ active }) => (
<button
className={`${
active && "bg-cesium-100"
active && "bg-cesium-100 dark:bg-neutral-100/10"
} 'block text-sm' px-4 py-2`}
onClick={() => exportPDF()}
>
Expand Down
8 changes: 4 additions & 4 deletions components/FilterBlock/FilterBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,9 @@ const FilterBlock = ({
};

return (
<div className="select-none rounded-xl ring-1 ring-zinc-100/50">
<div className="select-none rounded-xl ring-1 ring-neutral-100/50 dark:ring-neutral-400/20">
<Collapse
className="w-full rounded-xl bg-white font-display font-medium shadow-default"
className="w-full rounded-xl bg-white dark:bg-neutral-800/70 font-display font-medium shadow-default"
bordered={false}
accordion
>
Expand All @@ -292,7 +292,7 @@ const FilterBlock = ({
/>
<Collapse.Panel header={item1} key={item1 + "Panel"}>
<Collapse
className="bg-white font-display font-medium"
className="bg-white dark:bg-inherit font-display font-medium"
bordered={false}
accordion
key={item1 + "Collapse"}
Expand All @@ -310,7 +310,7 @@ const FilterBlock = ({
key={item1 + item2 + "Panel"}
>
<Collapse
className="bg-white font-display font-normal"
className="bg-white dark:bg-inherit font-display font-normal"
bordered={false}
accordion
key={item1 + item2 + "Collapse"}
Expand Down
8 changes: 4 additions & 4 deletions components/Install/Install.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Install = () => {
</label>
<button
onClick={() => setIsModalOpen(true)}
className="mt-2 rounded-lg bg-cesium-100 p-2 font-medium text-cesium-900 shadow-sm transition-colors hover:bg-cesium-200"
className="mt-2 rounded-lg bg-cesium-100 dark:bg-cesium-700/20 dark:hover:bg-cesium-700/30 p-2 font-medium text-cesium-900 shadow-sm transition-colors hover:bg-cesium-200"
>
Install <i className="bi bi-download" />
</button>
Expand All @@ -28,7 +28,7 @@ const Install = () => {
>
<Fade in={isModalOpen} timeout={400}>
<Box
className="absolute left-1/2 top-1/2 h-fit w-full min-w-[18rem] max-w-full -translate-x-1/2 -translate-y-1/2 transform rounded-2xl border border-zinc-200 bg-white p-6 text-center shadow-xl sm:w-80"
className="absolute left-1/2 top-1/2 h-fit w-full min-w-[18rem] max-w-full -translate-x-1/2 -translate-y-1/2 transform rounded-2xl border border-neutral-200 bg-white dark:bg-neutral-800 dark:border-neutral-400/20 p-6 text-center shadow-xl sm:w-80"
style={{
maxHeight: "calc(100% - 4rem)",
maxWidth: "calc(100% - 4rem)",
Expand All @@ -41,7 +41,7 @@ const Install = () => {
>
Install Calendarium <i className="bi bi-download"></i>
</span>
<div className="rounded-lg bg-blue-100 p-2 text-sm ">
<div className="rounded-lg bg-blue-500/20 p-2 text-sm ">
<i className="bi bi-info-circle-fill text-blue-500"></i> You can
install Calendarium as a{" "}
<a
Expand All @@ -55,7 +55,7 @@ const Install = () => {
<i className="bi bi-box-arrow-up-right"></i> on your device.
</div>
<Collapse
className="w-full rounded-lg border-gray-300 bg-white text-left font-display shadow-sm"
className="w-full rounded-lg border-neutral-300 bg-white text-left font-display shadow-sm"
accordion
>
<Collapse.Panel header="Android" key="1">
Expand Down
34 changes: 21 additions & 13 deletions components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useState, ReactNode } from "react";
import { useState, ReactNode, useEffect } from "react";

import Link from "next/link";
import Image from "next/image";

import Sidebar from "../Sidebar";
import Notifications from "../Notifications";

import styles from "./layout.module.scss";

import { useTheme } from "next-themes";

interface ILayoutProps {
children: ReactNode;
isHome: boolean;
Expand All @@ -24,13 +25,26 @@ const Layout = ({
saveTheme,
}: ILayoutProps) => {
const [isOpen, setIsOpen] = useState(false);
const hamburgerLine = `h-1 w-6 my-0.5 rounded-full bg-black transition ease transform duration-300`;
const hamburgerLine = `h-1 w-6 my-0.5 rounded-full bg-black transition ease transform duration-300 dark:bg-neutral-200 bg-neutral-900`;
const { resolvedTheme } = useTheme();
const [logo, setLogo] = useState(null);

// necesary so logo renders only on the client
useEffect(() => setLogo(
<picture>
<img
className="h-[46px] w-auto"
src={resolvedTheme === "dark" ? "/calendarium-dark.svg" : "/calendarium-light.svg"}
alt="Calendarium Logo"
/>
</picture>
), [resolvedTheme]);

return (
<div className="text-gray-900 lg:flex">
<div className="text-neutral-900 lg:flex dark:bg-neutral-900 dark:text-neutral-200">
{/* Open/Close Sidebar Button */}
<button
className="group absolute z-20 ml-8 mt-8 flex h-12 w-12 flex-col items-center justify-center rounded-xl bg-white shadow-md ring-1 ring-zinc-100/50 lg:hidden"
className="group absolute z-20 ml-8 mt-8 flex h-12 w-12 flex-col items-center justify-center rounded-xl bg-white dark:bg-neutral-800/70 shadow-md ring-1 ring-neutral-100/50 dark:ring-neutral-400/20 lg:hidden"
onClick={() => setIsOpen(!isOpen)}
>
<div
Expand Down Expand Up @@ -62,14 +76,8 @@ const Layout = ({
<div
style={{ cursor: "pointer", width: "fit-content", margin: "auto" }}
>
<Link href="https://cesium.link/">
<picture>
<img
className="h-[46px] w-auto"
src={"/calendarium-light.svg"}
alt="CeSIUM Link"
/>
</picture>
<Link href="/">
{logo}
</Link>
</div>
</div>
Expand Down
12 changes: 6 additions & 6 deletions components/NavigationPane/NavigationPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ const NavigationPane = () => {
const { asPath } = useRouter();

return (
<div className="rounded-xl font-display ring-1 ring-zinc-100/50">
<div className="flex w-full place-content-between rounded-xl p-5 font-medium text-gray-300 shadow-default transition-all">
<div className="rounded-xl font-display ring-1 ring-neutral-100/50 dark:ring-neutral-400/20 dark:bg-neutral-800/70">
<div className="flex w-full place-content-between rounded-xl p-5 font-medium text-neutral-300 dark:text-neutral-500 shadow-default transition-all">
<div className="m-auto w-fit space-x-8">
<Link href="/">
<span
className={`${
asPath === "/" &&
"text-gray-900 after:absolute after:ml-4 after:mt-4 after:flex after:h-1 after:w-12 after:rounded-t after:bg-cesium-900"
} transition-all hover:text-gray-900`}
"text-neutral-900 dark:text-neutral-200 after:absolute after:ml-4 after:mt-4 after:flex after:h-1 after:w-12 after:rounded-t after:bg-cesium-900"
} transition-all hover:text-neutral-900 dark:hover:text-neutral-200`}
>
<i className="bi bi-calendar-fill"></i> EVENTS
</span>
Expand All @@ -22,8 +22,8 @@ const NavigationPane = () => {
<span
className={`${
asPath === "/schedule" &&
"text-gray-900 after:absolute after:ml-36 after:mt-4 after:flex after:h-1 after:w-12 after:rounded-t after:bg-cesium-900"
} transition-all hover:text-gray-900`}
"text-neutral-900 dark:text-neutral-200 after:absolute after:ml-36 after:mt-4 after:flex after:h-1 after:w-12 after:rounded-t after:bg-cesium-900"
} transition-all hover:text-neutral-900 dark:hover:text-neutral-200`}
>
<i className="bi bi-clock-fill"></i> SCHEDULE
</span>
Expand Down
6 changes: 4 additions & 2 deletions components/Settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IFilterDTO } from "../../dtos";

import Themes from "../Themes";
import Install from "../Install";
import DarkModeToggler from "../DarkModeToggler";

type SettingsProps = {
saveTheme: () => void;
Expand All @@ -19,10 +20,10 @@ const Settings = ({
isHome,
}: SettingsProps) => {
return (
<div className="h-full w-full select-none space-y-4 overflow-hidden rounded-xl p-4 shadow-default ring-1 ring-zinc-200/30">
<div className="h-full w-full select-none space-y-4 overflow-hidden rounded-xl p-4 shadow-default ring-1 ring-neutral-200/30 dark:ring-neutral-400/20 dark:bg-neutral-800/70">
{/* Title */}
<div className="text-center text-lg font-medium">Settings</div>
<div className="border-b border-zinc-200/80" />
<div className="border-b border-neutral-200/80 dark:border-neutral-400/30" />
{/* Configs */}
<Themes
saveTheme={saveTheme}
Expand All @@ -31,6 +32,7 @@ const Settings = ({
setIsOpen={setIsOpen}
isHome={isHome}
/>
<DarkModeToggler />
<Install />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion components/ShareButton/ShareButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const ShareButton = ({
return (
<div>
<button
className="h-10 w-10 rounded-xl p-2 font-medium leading-3 text-gray-300 shadow-md ring-1 ring-zinc-200/50 transition-all duration-300 hover:text-gray-900 hover:shadow-lg"
className="h-10 w-10 rounded-xl p-2 font-medium leading-3 text-neutral-300 dark:text-neutral-500 shadow-md ring-1 ring-neutral-200/50 dark:ring-neutral-400/20 transition-all duration-300 hover:text-neutral-900 dark:hover:text-neutral-200 hover:shadow-lg dark:bg-neutral-800/70"
title="Share"
onClick={() => setIsModalOpen(true)}
>
Expand Down
Loading

0 comments on commit 2ec8417

Please sign in to comment.