From de15f8e2d3264b9ffb18c3a089612ef604eb87b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 12 Dec 2024 01:31:29 +0900 Subject: [PATCH 1/7] feat(embed): Add support for dark mode (#6912) * feat(embed): Support dark mode (wip) * finishing up the implementation * fix tailwind color selector * tweak design * refactor: unify types * fix * fix english grammar * refactor: unify types * tweak design * remove the customization part --- bskyembed/snippet/embed.ts | 4 ++- bskyembed/src/color-mode.ts | 17 +++++++++++++ bskyembed/src/components/container.tsx | 2 +- bskyembed/src/components/embed.tsx | 34 +++++++++++++++----------- bskyembed/src/components/post.tsx | 18 ++++++++------ bskyembed/src/index.css | 4 +++ bskyembed/src/screens/landing.tsx | 31 +++++++++++++---------- bskyembed/src/screens/post.tsx | 9 ++++--- bskyembed/tailwind.config.cjs | 8 ++++++ bskyembed/tsconfig.json | 3 +-- 10 files changed, 88 insertions(+), 42 deletions(-) create mode 100644 bskyembed/src/color-mode.ts diff --git a/bskyembed/snippet/embed.ts b/bskyembed/snippet/embed.ts index 380cda5fb9..3c1b14b955 100644 --- a/bskyembed/snippet/embed.ts +++ b/bskyembed/snippet/embed.ts @@ -20,6 +20,7 @@ window.addEventListener('message', event => { return } + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const id = (event.data as {id: string}).id if (!id) { return @@ -33,6 +34,7 @@ window.addEventListener('message', event => { return } + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const height = (event.data as {height: number}).height if (height) { embed.style.height = `${height}px` @@ -47,7 +49,7 @@ window.addEventListener('message', event => { * @returns */ function scan(node = document) { - const embeds = node.querySelectorAll('[data-bluesky-uri]') + const embeds = node.querySelectorAll('[data-bluesky-uri]') for (let i = 0; i < embeds.length; i++) { const id = String(Math.random()).slice(2) diff --git a/bskyembed/src/color-mode.ts b/bskyembed/src/color-mode.ts new file mode 100644 index 0000000000..2b392c6178 --- /dev/null +++ b/bskyembed/src/color-mode.ts @@ -0,0 +1,17 @@ +export function applyTheme(theme: 'light' | 'dark') { + document.documentElement.classList.remove('light', 'dark') + document.documentElement.classList.add(theme) +} + +export function initColorMode() { + applyTheme( + window.matchMedia('(prefers-color-scheme: dark)').matches + ? 'dark' + : 'light', + ) + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', mql => { + applyTheme(mql.matches ? 'dark' : 'light') + }) +} diff --git a/bskyembed/src/components/container.tsx b/bskyembed/src/components/container.tsx index 5b1b2b7fb4..8e142a25be 100644 --- a/bskyembed/src/components/container.tsx +++ b/bskyembed/src/components/container.tsx @@ -37,7 +37,7 @@ export function Container({ return (
{ if (ref.current && href) { // forwardRef requires preact/compat - let's keep it simple diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx index 74eacf16d4..20ffcb2b29 100644 --- a/bskyembed/src/components/embed.tsx +++ b/bskyembed/src/components/embed.tsx @@ -78,9 +78,9 @@ export function Embed({ return ( + className="transition-colors hover:bg-neutral-100 dark:hover:bg-slate-700 border dark:border-slate-600 rounded-lg p-2 gap-1.5 w-full flex flex-col">
-
+

{record.author.displayName} - + @{record.author.handle}

@@ -209,7 +209,7 @@ function Info({children}: {children: ComponentChildren}) { return (
-

{children}

+

{children}

) } @@ -308,7 +308,7 @@ function ExternalEmbed({ return ( {content.external.thumb && ( )}
-

+

{toNiceDomain(content.external.uri)}

{content.external.title}

-

+

{content.external.description}

@@ -345,23 +345,29 @@ function GenericWithImageEmbed({ return ( + className="w-full rounded-lg border dark:border-slate-600 py-2 px-3 flex flex-col gap-2">
{image ? ( {title} ) : (
)}

{title}

-

{subtitle}

+

+ {subtitle} +

- {description &&

{description}

} + {description && ( +

+ {description} +

+ )} ) } @@ -406,7 +412,7 @@ function StarterPackEmbed({ return ( + className="w-full rounded-lg overflow-hidden border dark:border-slate-600 flex flex-col items-stretch">
@@ -415,7 +421,7 @@ function StarterPackEmbed({

{content.record.name}

-

+

Starter pack by{' '} {content.creator.displayName || `@${content.creator.handle}`}

@@ -425,7 +431,7 @@ function StarterPackEmbed({

{content.record.description}

)} {!!content.joinedAllTimeCount && content.joinedAllTimeCount > 50 && ( -

+

{content.joinedAllTimeCount} users have joined!

)} diff --git a/bskyembed/src/components/post.tsx b/bskyembed/src/components/post.tsx index 4db5eeb45e..26945eb69d 100644 --- a/bskyembed/src/components/post.tsx +++ b/bskyembed/src/components/post.tsx @@ -38,7 +38,7 @@ export function Post({thread}: Props) {
-
+
+ className="text-[15px] text-textLight dark:text-textDimmed hover:underline line-clamp-1">

@{post.author.handle}

@@ -69,15 +69,15 @@ export function Post({thread}: Props) { -
+
{!!post.likeCount && (
-

+

{prettyNumber(post.likeCount)}

@@ -85,17 +85,19 @@ export function Post({thread}: Props) { {!!post.repostCount && (
-

+

{prettyNumber(post.repostCount)}

)}
-

Reply

+

+ Reply +

-

+

{post.replyCount ? `Read ${prettyNumber(post.replyCount)} ${ post.replyCount > 1 ? 'replies' : 'reply' diff --git a/bskyembed/src/index.css b/bskyembed/src/index.css index 22b2b8be5c..289e34cf00 100644 --- a/bskyembed/src/index.css +++ b/bskyembed/src/index.css @@ -5,3 +5,7 @@ .break-word { word-break: break-word; } + +:root { + color-scheme: light dark; +} diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx index a9e08cd3f2..a3448e90ac 100644 --- a/bskyembed/src/screens/landing.tsx +++ b/bskyembed/src/screens/landing.tsx @@ -6,6 +6,7 @@ import {useEffect, useMemo, useRef, useState} from 'preact/hooks' import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg' import logo from '../../assets/logo.svg' +import {initColorMode} from '../color-mode' import {Container} from '../components/container' import {Link} from '../components/link' import {Post} from '../components/post' @@ -21,6 +22,8 @@ export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js` const root = document.getElementById('app') if (!root) throw new Error('No root element') +initColorMode() + const agent = new BskyAgent({ service: 'https://public.api.bsky.app', }) @@ -108,7 +111,7 @@ function LandingPage() { }, [uri]) return ( -

+
@@ -121,20 +124,22 @@ function LandingPage() { type="text" value={uri} onInput={e => setUri(e.currentTarget.value)} - className="border rounded-lg py-3 w-full max-w-[600px] px-4" + className="border rounded-lg py-3 w-full max-w-[600px] px-4 dark:bg-dimmedBg dark:border-slate-500" placeholder={DEFAULT_POST} /> - + {loading ? ( - +
+ +
) : (
{!error && thread && uri && } {!error && thread && } {error && ( -
+

{error}

)} @@ -149,15 +154,15 @@ function Skeleton() {
-
+
-
-
+
+
-
-
-
+
+
+
) @@ -220,7 +225,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { ref={ref} type="text" value={snippet} - className="border rounded-lg py-3 w-full px-4" + className="border rounded-lg py-3 w-full px-4 dark:bg-dimmedBg dark:border-slate-500" readOnly autoFocus onFocus={() => { @@ -228,7 +233,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) { }} />