Skip to content

Commit

Permalink
Dark/light mode
Browse files Browse the repository at this point in the history
  • Loading branch information
pomber committed Jan 4, 2023
1 parent 0cfc961 commit a797277
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 954 deletions.
5 changes: 5 additions & 0 deletions .changeset/clever-guests-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"bright": minor
---

Dual themes for dark/light mode
2 changes: 1 addition & 1 deletion lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
],
"scripts": {
"build": "tsup src/index.tsx --format esm,cjs --dts",
"dev": "tsup src/index.tsx --format esm,cjs --watch --dts"
"watch": "tsup src/index.tsx --format esm,cjs --watch --dts"
},
"dependencies": {
"@code-hike/lighter": "0.1.2"
Expand Down
112 changes: 112 additions & 0 deletions lib/src/code.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { highlight, Lang, Theme } from "@code-hike/lighter"

export async function Code({
code,
lang,
style,
className,
lineNumbers,
unstyled,
theme,
scheme,
}: {
code: string
lang: Lang
style?: React.CSSProperties
className?: string
lineNumbers?: boolean
unstyled?: boolean
theme?: Theme
scheme?: "dark" | "light"
}) {
const {
lines,
foreground,
background,
colorScheme,
selectionBackground,
lineNumberForeground,
} = await highlight(code, lang, theme)

const lineCount = lines.length
const digits = lineCount.toString().length

const kids = lines.map((tokens, i) => {
return (
<span key={i}>
{lineNumbers && (
<span className="bright-ln" style={{ width: `${digits}ch` }}>
{i + 1}
</span>
)}
{tokens.map((t, j) => (
<span key={j} style={t.style}>
{t.content}
</span>
))}
<br />
</span>
)
})

const themeName = getThemeName(theme)
const codeSelector = `pre[data-bright-theme="${themeName}"]`
const css = `
${displayStyle(scheme, codeSelector)}
${codeSelector} ::selection {
background-color: ${selectionBackground};
}
${codeSelector} .bright-ln {
color: ${lineNumberForeground};
padding-right: 2ch;
display: inline-block;
text-align: right;
user-select: none;
}`

return (
<pre
className={className}
data-bright-theme={themeName}
style={{
color: foreground,
background,
padding: "1em",
borderRadius: "4px",
colorScheme,
...style,
}}
>
<style dangerouslySetInnerHTML={{ __html: css }} />
<code>{kids}</code>
</pre>
)
}

function displayStyle(
scheme: "dark" | "light" | undefined,
codeSelector: string
) {
if (scheme === "dark") {
return `${codeSelector} {
display: block;
}
[data-theme="light"] ${codeSelector} {
display: none;
}`
}
if (scheme === "light") {
return `${codeSelector} {
display: none;
}
[data-theme="light"] ${codeSelector} {
display: block;
}`
}
return ""
}
function getThemeName(theme?: Theme) {
if (!theme) return "default"
if (typeof theme === "string") return theme
return (theme as any).name
}
106 changes: 51 additions & 55 deletions lib/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import React from "react"
import { highlight, Theme, Lang } from "@code-hike/lighter"
import { Theme, Lang } from "@code-hike/lighter"
import { Code as InnerCode } from "./code"

type DoubleTheme = {
dark: Theme
light: Theme
selector?: (scheme: "dark" | "light") => string
}

type BrightTheme = Theme | DoubleTheme

type CodeProps = {
lang: Lang
Expand All @@ -8,11 +17,11 @@ type CodeProps = {
className?: string
lineNumbers?: boolean
unstyled?: boolean
theme?: Theme
theme?: BrightTheme
}

type CodeComponent = ((props: CodeProps) => Promise<JSX.Element>) & {
theme?: Theme
theme?: BrightTheme
}

export const Code: CodeComponent = async ({
Expand All @@ -24,61 +33,48 @@ export const Code: CodeComponent = async ({
unstyled,
theme,
}) => {
const {
lines,
foreground,
background,
colorScheme,
selectionBackground,
lineNumberForeground,
} = await highlight(children, lang || "js", theme || Code.theme)

const lineCount = lines.length
const digits = lineCount.toString().length

const kids = lines.map((tokens, i) => {
const finalTheme = theme || Code.theme
if (finalTheme && (finalTheme as any).dark && (finalTheme as any).light) {
const doubleTheme = finalTheme as DoubleTheme
const darkTheme = doubleTheme.dark
const lightTheme = doubleTheme.light
return (
<span key={i}>
{lineNumbers && (
<span className="bright-ln" style={{ width: `${digits}ch` }}>
{i + 1}
</span>
)}
{tokens.map((t, j) => (
<span key={j} style={t.style}>
{t.content}
</span>
))}
<br />
</span>
<>
{/* @ts-expect-error Server Component */}
<InnerCode
code={children}
lang={lang || "js"}
style={style}
className={className}
lineNumbers={lineNumbers}
unstyled={unstyled}
theme={darkTheme}
scheme="dark"
/>
{/* @ts-expect-error Server Component */}
<InnerCode
code={children}
lang={lang || "js"}
style={style}
className={className}
lineNumbers={lineNumbers}
unstyled={unstyled}
theme={lightTheme}
scheme="light"
/>
</>
)
})

}
return (
<pre
/* @ts-expect-error Server Component */
<InnerCode
code={children}
lang={lang || "js"}
style={style}
className={className}
style={{
color: foreground,
background,
// border: "1px solid " + background,
padding: "1em",
borderRadius: "4px",
colorScheme,
...style,
}}
>
<style>{`
code ::selection {
background-color: ${selectionBackground}
}
.bright-ln {
color: ${lineNumberForeground};
padding-right: 2ch;
display: inline-block;
text-align: right;
user-select: none;
}`}</style>
<code>{kids}</code>
</pre>
lineNumbers={lineNumbers}
unstyled={unstyled}
theme={finalTheme}
/>
)
}
Loading

0 comments on commit a797277

Please sign in to comment.