From 8c4eed2bbc80f53a2fce96a490bf8cbd9f168ce9 Mon Sep 17 00:00:00 2001 From: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Date: Sat, 5 Mar 2022 21:07:46 +0530 Subject: [PATCH] Add "light-brand" and "dark-brand" colors (add a second color picker) (#2028) * init dark brand color addition * added dark mode css vars * added contrast brand colors * minor fixes * added dark branding to loader, button Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/components/AddToHomescreen.tsx | 2 +- apps/web/components/CustomBranding.tsx | 39 ++++++++++++++----- apps/web/components/Loader.tsx | 4 +- apps/web/components/Shell.tsx | 2 +- .../web/components/booking/AvailableTimes.tsx | 2 +- apps/web/components/booking/DatePicker.tsx | 2 +- apps/web/components/booking/TimeOptions.tsx | 4 +- .../booking/pages/AvailabilityPage.tsx | 2 +- .../components/booking/pages/BookingPage.tsx | 2 +- .../security/TwoFactorModalHeader.tsx | 2 +- .../components/team/MemberInvitationModal.tsx | 2 +- apps/web/components/ui/WeekdaySelect.tsx | 2 +- apps/web/components/ui/colorpicker.tsx | 4 +- .../availability/TeamAvailabilityTimes.tsx | 2 +- apps/web/pages/[user]/[type].tsx | 2 + apps/web/pages/[user]/book.tsx | 2 + apps/web/pages/availability/troubleshoot.tsx | 8 ++-- apps/web/pages/cancel/[uid].tsx | 4 +- apps/web/pages/settings/profile.tsx | 22 ++++++++--- apps/web/pages/success.tsx | 5 ++- apps/web/pages/team/[slug]/[type].tsx | 2 + apps/web/pages/team/[slug]/book.tsx | 1 + apps/web/public/static/locales/en/common.json | 2 + apps/web/server/createContext.ts | 1 + apps/web/server/routers/viewer.tsx | 2 + apps/web/styles/globals.css | 2 + apps/web/tailwind.config.js | 2 + .../migration.sql | 2 + packages/prisma/schema.prisma | 1 + packages/ui/Button.tsx | 2 +- 30 files changed, 96 insertions(+), 35 deletions(-) create mode 100644 packages/prisma/migrations/20220302110201_add_dark_mode_brand_color/migration.sql diff --git a/apps/web/components/AddToHomescreen.tsx b/apps/web/components/AddToHomescreen.tsx index fa0f8e60..330863ff 100644 --- a/apps/web/components/AddToHomescreen.tsx +++ b/apps/web/components/AddToHomescreen.tsx @@ -18,7 +18,7 @@ export default function AddToHomescreen() {
- + { +const BrandColor = ({ + lightVal = brandColor, + darkVal = darkBrandColor, +}: { + lightVal: string | undefined | null; + darkVal: string | undefined | null; +}) => { // ensure acceptable hex-code - val = isValidHexCode(val) ? (val?.indexOf("#") === 0 ? val : "#" + val) : fallBackHex(val); + lightVal = isValidHexCode(lightVal) + ? lightVal?.indexOf("#") === 0 + ? lightVal + : "#" + lightVal + : fallBackHex(lightVal, false); + darkVal = isValidHexCode(darkVal) + ? darkVal?.indexOf("#") === 0 + ? darkVal + : "#" + darkVal + : fallBackHex(darkVal, true); useEffect(() => { - document.documentElement.style.setProperty("--brand-color", val); - document.documentElement.style.setProperty("--brand-text-color", getContrastingTextColor(val)); - }, [val]); + document.documentElement.style.setProperty("--brand-color", lightVal); + document.documentElement.style.setProperty("--brand-text-color", getContrastingTextColor(lightVal, true)); + document.documentElement.style.setProperty("--brand-color-dark-mode", darkVal); + document.documentElement.style.setProperty( + "--brand-text-color-dark-mode", + getContrastingTextColor(darkVal, true) + ); + }, [lightVal, darkVal]); return null; }; diff --git a/apps/web/components/Loader.tsx b/apps/web/components/Loader.tsx index 2baf01a8..29ac50d7 100644 --- a/apps/web/components/Loader.tsx +++ b/apps/web/components/Loader.tsx @@ -1,7 +1,7 @@ export default function Loader() { return ( -
- +
+
); } diff --git a/apps/web/components/Shell.tsx b/apps/web/components/Shell.tsx index 75fa88c7..2d239219 100644 --- a/apps/web/components/Shell.tsx +++ b/apps/web/components/Shell.tsx @@ -192,7 +192,7 @@ export default function Shell(props: { } return ( <> - + = ({ diff --git a/apps/web/components/booking/DatePicker.tsx b/apps/web/components/booking/DatePicker.tsx index b558fa3e..bc0ba2c6 100644 --- a/apps/web/components/booking/DatePicker.tsx +++ b/apps/web/components/booking/DatePicker.tsx @@ -278,7 +278,7 @@ function DatePicker({ "hover:border-brand hover:border dark:hover:border-white", day.disabled ? "cursor-default font-light text-gray-400 hover:border-0" : "font-medium", date && date.isSame(browsingDate.date(day.date), "day") - ? "bg-brand text-brandcontrast" + ? "bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast" : !day.disabled ? " bg-gray-100 dark:bg-gray-600 dark:text-white" : "" diff --git a/apps/web/components/booking/TimeOptions.tsx b/apps/web/components/booking/TimeOptions.tsx index d37f2047..14947b04 100644 --- a/apps/web/components/booking/TimeOptions.tsx +++ b/apps/web/components/booking/TimeOptions.tsx @@ -46,7 +46,9 @@ const TimeOptions: FC = ({ onToggle24hClock, onSelectTimeZone }) => { checked={is24hClock} onChange={handle24hClockToggle} className={classNames( - is24hClock ? "bg-brand text-brandcontrast" : "bg-gray-200 dark:bg-gray-600", + is24hClock + ? "bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast" + : "bg-gray-200 dark:bg-gray-600", "relative inline-flex h-5 w-8 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2" )}> {t("use_setting")} diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 27f89fe2..5c58b9b8 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -110,7 +110,7 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => { username={profile.slug || undefined} // avatar={profile.image || undefined} /> - +
{ - +
{isReady && (
diff --git a/apps/web/components/security/TwoFactorModalHeader.tsx b/apps/web/components/security/TwoFactorModalHeader.tsx index c106f508..10963aec 100644 --- a/apps/web/components/security/TwoFactorModalHeader.tsx +++ b/apps/web/components/security/TwoFactorModalHeader.tsx @@ -4,7 +4,7 @@ import React from "react"; const TwoFactorModalHeader = ({ title, description }: { title: string; description: string }) => { return (
-
+
diff --git a/apps/web/components/team/MemberInvitationModal.tsx b/apps/web/components/team/MemberInvitationModal.tsx index 6006daa7..e245e587 100644 --- a/apps/web/components/team/MemberInvitationModal.tsx +++ b/apps/web/components/team/MemberInvitationModal.tsx @@ -64,7 +64,7 @@ export default function MemberInvitationModal(props: { team: TeamWithMembers | n
-
+
diff --git a/apps/web/components/ui/WeekdaySelect.tsx b/apps/web/components/ui/WeekdaySelect.tsx index 6e351f57..95aec8f4 100644 --- a/apps/web/components/ui/WeekdaySelect.tsx +++ b/apps/web/components/ui/WeekdaySelect.tsx @@ -33,7 +33,7 @@ export const WeekdaySelect = (props: WeekdaySelectProps) => { toggleDay(idx); }} className={` - bg-brand text-brandcontrast + bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast h-10 w-10 rounded px-3 py-1 focus:outline-none ${activeDays[idx + 1] ? "rounded-r-none" : ""} ${activeDays[idx - 1] ? "rounded-l-none" : ""} diff --git a/apps/web/components/ui/colorpicker.tsx b/apps/web/components/ui/colorpicker.tsx index b2dd41d5..28bcb694 100644 --- a/apps/web/components/ui/colorpicker.tsx +++ b/apps/web/components/ui/colorpicker.tsx @@ -62,7 +62,9 @@ export type ColorPickerProps = { }; const ColorPicker = (props: ColorPickerProps) => { - const init = !isValidHexCode(props.defaultValue) ? fallBackHex(props.defaultValue) : props.defaultValue; + const init = !isValidHexCode(props.defaultValue) + ? fallBackHex(props.defaultValue, false) + : props.defaultValue; const [color, setColor] = useState(init); const [isOpen, toggle] = useState(false); const popover = useRef() as React.MutableRefObject; diff --git a/apps/web/ee/components/team/availability/TeamAvailabilityTimes.tsx b/apps/web/ee/components/team/availability/TeamAvailabilityTimes.tsx index 2cab13e3..5cd6be67 100644 --- a/apps/web/ee/components/team/availability/TeamAvailabilityTimes.tsx +++ b/apps/web/ee/components/team/availability/TeamAvailabilityTimes.tsx @@ -59,7 +59,7 @@ export default function TeamAvailabilityTimes(props: Props) { {times.map((time) => (
{time.format("HH:mm")} diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 21b1892a..d8682e47 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -79,6 +79,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => availability: true, hideBranding: true, brandColor: true, + darkBrandColor: true, theme: true, plan: true, eventTypes: { @@ -192,6 +193,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => theme: user.theme, weekStart: user.weekStart, brandColor: user.brandColor, + darkBrandColor: user.darkBrandColor, }, date: dateParam, eventType: eventTypeObject, diff --git a/apps/web/pages/[user]/book.tsx b/apps/web/pages/[user]/book.tsx index f98b70af..3c3b2978 100644 --- a/apps/web/pages/[user]/book.tsx +++ b/apps/web/pages/[user]/book.tsx @@ -37,6 +37,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { avatar: true, theme: true, brandColor: true, + darkBrandColor: true, }, }); @@ -140,6 +141,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { image: user.avatar, theme: user.theme, brandColor: user.brandColor, + darkBrandColor: user.darkBrandColor, }, eventType: eventTypeObject, booking, diff --git a/apps/web/pages/availability/troubleshoot.tsx b/apps/web/pages/availability/troubleshoot.tsx index eeb76a1b..2b223073 100644 --- a/apps/web/pages/availability/troubleshoot.tsx +++ b/apps/web/pages/availability/troubleshoot.tsx @@ -64,8 +64,8 @@ const AvailabilityView = ({ user }: { user: User }) => { /> {t("hover_over_bold_times_tip")}
-
-
+
+
{t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)}
@@ -94,8 +94,8 @@ const AvailabilityView = ({ user }: { user: User }) => {
)} -
-
+
+
{t("your_day_ends_at")} {convertMinsToHrsMins(user.endTime)}
diff --git a/apps/web/pages/cancel/[uid].tsx b/apps/web/pages/cancel/[uid].tsx index cdd8aec8..90aba1ac 100644 --- a/apps/web/pages/cancel/[uid].tsx +++ b/apps/web/pages/cancel/[uid].tsx @@ -35,7 +35,7 @@ export default function Type(props: inferSSRProps) { title={`${t("cancel")} ${props.booking && props.booking.title} | ${props.profile?.name}`} description={`${t("cancel")} ${props.booking && props.booking.title} | ${props.profile?.name}`} /> - +
@@ -179,6 +179,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => username: true, name: true, brandColor: true, + darkBrandColor: true, }, }, eventType: { @@ -210,6 +211,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => name: booking.eventType?.team?.name || booking.user?.name || null, slug: booking.eventType?.team?.slug || booking.user?.username || null, brandColor: booking.user?.brandColor || null, + darkBrandColor: booking.user?.darkBrandColor || null, }; return { diff --git a/apps/web/pages/settings/profile.tsx b/apps/web/pages/settings/profile.tsx index 1a9f4b1b..aeda8e0b 100644 --- a/apps/web/pages/settings/profile.tsx +++ b/apps/web/pages/settings/profile.tsx @@ -176,6 +176,7 @@ function SettingsView(props: ComponentProps & { localeProp: str const [hasErrors, setHasErrors] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const [brandColor, setBrandColor] = useState(props.user.brandColor); + const [darkBrandColor, setDarkBrandColor] = useState(props.user.darkBrandColor); useEffect(() => { if (!props.user.theme) return; @@ -194,6 +195,7 @@ function SettingsView(props: ComponentProps & { localeProp: str const enteredDescription = descriptionRef.current.value; const enteredAvatar = avatarRef.current.value; const enteredBrandColor = brandColor; + const enteredDarkBrandColor = darkBrandColor; const enteredTimeZone = typeof selectedTimeZone === "string" ? selectedTimeZone : selectedTimeZone.value; const enteredWeekStartDay = selectedWeekStartDay.value; const enteredHideBranding = hideBrandingRef.current.checked; @@ -213,6 +215,7 @@ function SettingsView(props: ComponentProps & { localeProp: str hideBranding: enteredHideBranding, theme: asStringOrNull(selectedTheme?.value), brandColor: enteredBrandColor, + darkBrandColor: enteredDarkBrandColor, locale: enteredLanguage, timeFormat: enteredTimeFormat, }); @@ -424,11 +427,19 @@ function SettingsView(props: ComponentProps & { localeProp: str
-
- - +
+
+ + +
+
+ + +

@@ -524,6 +535,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => theme: true, plan: true, brandColor: true, + darkBrandColor: true, metadata: true, timeFormat: true, }, diff --git a/apps/web/pages/success.tsx b/apps/web/pages/success.tsx index 3697cd4d..ac8f70fa 100644 --- a/apps/web/pages/success.tsx +++ b/apps/web/pages/success.tsx @@ -96,7 +96,7 @@ export default function Success(props: inferSSRProps) title={needsConfirmation ? t("booking_submitted") : t("booking_confirmed")} description={needsConfirmation ? t("booking_submitted") : t("booking_confirmed")} /> - +
@@ -320,6 +320,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { plan: true, theme: true, brandColor: true, + darkBrandColor: true, }, }, team: { @@ -348,6 +349,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { plan: true, theme: true, brandColor: true, + darkBrandColor: true, }, }); if (user) { @@ -365,6 +367,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { name: eventType.team?.name || eventType.users[0]?.name || null, theme: (!eventType.team?.name && eventType.users[0]?.theme) || null, brandColor: eventType.team ? null : eventType.users[0].brandColor, + darkBrandColor: eventType.team ? null : eventType.users[0].darkBrandColor, }; return { diff --git a/apps/web/pages/team/[slug]/[type].tsx b/apps/web/pages/team/[slug]/[type].tsx index b1abadf8..d50c124d 100644 --- a/apps/web/pages/team/[slug]/[type].tsx +++ b/apps/web/pages/team/[slug]/[type].tsx @@ -48,6 +48,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => hideBranding: true, plan: true, brandColor: true, + darkBrandColor: true, }, }, title: true, @@ -105,6 +106,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => theme: null, weekStart: "Sunday", brandColor: "" /* TODO: Add a way to set a brand color for Teams */, + darkBrandColor: "" /* TODO: Add a way to set a brand color for Teams */, }, date: dateParam, eventType: eventTypeObject, diff --git a/apps/web/pages/team/[slug]/book.tsx b/apps/web/pages/team/[slug]/book.tsx index 1772952e..bb161076 100644 --- a/apps/web/pages/team/[slug]/book.tsx +++ b/apps/web/pages/team/[slug]/book.tsx @@ -102,6 +102,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { image: eventTypeObject.team?.logo || null, theme: null /* Teams don't have a theme, and `BookingPage` uses it */, brandColor: null /* Teams don't have a brandColor, and `BookingPage` uses it */, + darkBrandColor: null /* Teams don't have a darkBrandColor, and `BookingPage` uses it */, }, eventType: eventTypeObject, booking, diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 67fa3055..6814e243 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -513,6 +513,8 @@ "first_day_of_week": "First Day of Week", "single_theme": "Single Theme", "brand_color": "Brand Color", + "light_brand_color": "Brand Color (Light Theme)", + "dark_brand_color": "Brand Color (Dark Theme)", "file_not_named": "File is not named [idOrSlug]/[user]", "create_team": "Create Team", "name": "Name", diff --git a/apps/web/server/createContext.ts b/apps/web/server/createContext.ts index b3cabdfb..358213f3 100644 --- a/apps/web/server/createContext.ts +++ b/apps/web/server/createContext.ts @@ -45,6 +45,7 @@ async function getUserFromSession({ twoFactorEnabled: true, identityProvider: true, brandColor: true, + darkBrandColor: true, plan: true, away: true, credentials: { diff --git a/apps/web/server/routers/viewer.tsx b/apps/web/server/routers/viewer.tsx index 984870e1..85bc9ee3 100644 --- a/apps/web/server/routers/viewer.tsx +++ b/apps/web/server/routers/viewer.tsx @@ -83,6 +83,7 @@ const loggedInViewerRouter = createProtectedRouter() twoFactorEnabled: user.twoFactorEnabled, identityProvider: user.identityProvider, brandColor: user.brandColor, + darkBrandColor: user.darkBrandColor, plan: user.plan, away: user.away, }; @@ -616,6 +617,7 @@ const loggedInViewerRouter = createProtectedRouter() weekStart: z.string().optional(), hideBranding: z.boolean().optional(), brandColor: z.string().optional(), + darkBrandColor: z.string().optional(), theme: z.string().optional().nullable(), completedOnboarding: z.boolean().optional(), locale: z.string().optional(), diff --git a/apps/web/styles/globals.css b/apps/web/styles/globals.css index 3adf00d5..e6119949 100644 --- a/apps/web/styles/globals.css +++ b/apps/web/styles/globals.css @@ -5,6 +5,8 @@ :root { --brand-color: #292929; --brand-text-color: #ffffff; + --brand-color-dark-mode: #fafafa; + --brand-text-color-dark-mode: #292929; } /* PhoneInput dark-mode overrides (it would add a lot of boilerplate to do this in JavaScript) */ diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js index 18de10e5..b6880872 100644 --- a/apps/web/tailwind.config.js +++ b/apps/web/tailwind.config.js @@ -14,6 +14,8 @@ module.exports = { /* your primary brand color */ brand: "var(--brand-color)", brandcontrast: "var(--brand-text-color)", + darkmodebrand: "var(--brand-color-dark-mode)", + darkmodebrandcontrast: "var(--brand-text-color-dark-mode)", black: "#111111", gray: { 50: "#F8F8F8", diff --git a/packages/prisma/migrations/20220302110201_add_dark_mode_brand_color/migration.sql b/packages/prisma/migrations/20220302110201_add_dark_mode_brand_color/migration.sql new file mode 100644 index 00000000..e1d0adf9 --- /dev/null +++ b/packages/prisma/migrations/20220302110201_add_dark_mode_brand_color/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "users" ADD COLUMN "darkBrandColor" TEXT NOT NULL DEFAULT E'#fafafa'; \ No newline at end of file diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 073170ba..98726bbd 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -142,6 +142,7 @@ model User { Schedule Schedule[] webhooks Webhook[] brandColor String @default("#292929") + darkBrandColor String @default("#fafafa") // the location where the events will end up destinationCalendar DestinationCalendar? away Boolean @default(false) diff --git a/packages/ui/Button.tsx b/packages/ui/Button.tsx index 2e996afc..6b63432d 100644 --- a/packages/ui/Button.tsx +++ b/packages/ui/Button.tsx @@ -65,7 +65,7 @@ export const Button = forwardRef