Skip to content

Commit b3da6fe

Browse files
authored
Theming init (#23)
1 parent 4167fad commit b3da6fe

20 files changed

Lines changed: 282 additions & 123 deletions

.changeset/six-glasses-fall.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@scaffold-ui/components": patch
3+
---
4+
5+
setup themeing for components package
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"use client";
2+
3+
import { useEffect, useState } from "react";
4+
import { useTheme } from "next-themes";
5+
import { MoonIcon, SunIcon } from "@heroicons/react/24/outline";
6+
7+
export const SwitchTheme = ({ className }: { className?: string }) => {
8+
const { setTheme, resolvedTheme } = useTheme();
9+
const [mounted, setMounted] = useState(false);
10+
11+
const isDarkMode = resolvedTheme === "dark";
12+
13+
const handleToggle = () => {
14+
if (isDarkMode) {
15+
setTheme("light");
16+
return;
17+
}
18+
setTheme("dark");
19+
};
20+
21+
useEffect(() => {
22+
setMounted(true);
23+
}, []);
24+
25+
if (!mounted) return null;
26+
27+
return (
28+
<div className={`flex items-center space-x-3 ${className}`}>
29+
{/* Custom Toggle Switch */}
30+
<button
31+
onClick={handleToggle}
32+
className="relative inline-flex h-6 w-11 items-center rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-opacity-50 bg-[var(--color-sui-primary-subtle)] dark:bg-[var(--color-sui-primary)] focus:ring-[var(--color-sui-primary)]"
33+
aria-label={`Switch to ${isDarkMode ? "light" : "dark"} theme`}
34+
>
35+
{/* Toggle Circle */}
36+
<span
37+
className={`inline-block h-4 w-4 transform rounded-full transition-transform duration-200 ease-in-out bg-[var(--color-sui-primary)] dark:bg-[var(--color-sui-primary-content)] ${
38+
isDarkMode ? "translate-x-6" : "translate-x-1"
39+
}`}
40+
/>
41+
</button>
42+
43+
{/* Icon Display */}
44+
<div className="flex items-center justify-center w-6 h-6">
45+
{isDarkMode ? (
46+
<MoonIcon className="h-5 w-5 transition-all duration-200 ease-in-out text-[var(--color-sui-primary-content)]" />
47+
) : (
48+
<SunIcon className="h-5 w-5 transition-all duration-200 ease-in-out text-[var(--color-sui-primary)]" />
49+
)}
50+
</div>
51+
</div>
52+
);
53+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import { ThemeProvider as NextThemesProvider } from "next-themes";
5+
import { type ThemeProviderProps } from "next-themes/dist/types";
6+
7+
export const ThemeProvider = ({ children, ...props }: ThemeProviderProps) => {
8+
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
9+
};

example/app/components/UseAddressExample.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const UseAddressExample = () => {
2727
<div className="space-y-8">
2828
{/* Address Component Examples */}
2929
<div className="space-y-4">
30-
<h2 className="text-xl font-semibold text-gray-200">Address Component Examples</h2>
30+
<h2 className="text-xl font-semibold text-[var(--color-sui-primary-content)]">Address Component Examples</h2>
3131

3232
<div className="space-y-4">
3333
<div className="flex flex-col">
@@ -84,7 +84,9 @@ export const UseAddressExample = () => {
8484

8585
{/* Manual Implementation using useAddress hook */}
8686
<div className="space-y-4 border-t border-gray-700 pt-6">
87-
<h2 className="text-xl font-semibold text-gray-200">Manual Implementation (useAddress Hook)</h2>
87+
<h2 className="text-xl font-semibold text-[var(--color-sui-primary-content)]">
88+
Manual Implementation (useAddress Hook)
89+
</h2>
8890

8991
<div className="flex items-center gap-3">
9092
{/* eslint-disable-next-line @next/next/no-img-element */}

example/app/components/UseAddressInputExample.tsx

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import { AddressInput, BaseInput } from "@scaffold-ui/components";
44
import { useAddressInput } from "@scaffold-ui/hooks";
5-
import { useState } from "react";
5+
import { useTheme } from "next-themes";
6+
import { CSSProperties, useState } from "react";
67
import { Address } from "viem";
78

89
export const UseAddressInputExample = () => {
910
const [value, setValue] = useState<string>("");
11+
const { theme } = useTheme();
12+
const isDark = theme === "dark";
1013

1114
const [manualImplementationValue, setManualImplementationValue] = useState<string>("");
1215
const { ensAddress, ensName, ensAvatar, isEnsAddressLoading, isEnsNameLoading, isEnsAvatarLoading } = useAddressInput(
@@ -22,7 +25,9 @@ export const UseAddressInputExample = () => {
2225
<div className="space-y-8">
2326
{/* AddressInput Component Examples */}
2427
<div className="space-y-4">
25-
<h2 className="text-xl font-semibold text-gray-200">AddressInput Component Examples</h2>
28+
<h2 className="text-xl font-semibold text-[var(--color-sui-primary-content)]">
29+
AddressInput Component Examples
30+
</h2>
2631

2732
<div className="space-y-4">
2833
<div className="flex flex-col">
@@ -45,21 +50,41 @@ export const UseAddressInputExample = () => {
4550
<AddressInput
4651
value={value}
4752
onChange={setValue}
48-
colors={{
49-
border: "#22c55e",
50-
background: "#dcfce7",
51-
text: "#166534",
52-
}}
53+
style={
54+
{
55+
"--color-sui-input-border": isDark ? "#4ade80" : "#22c55e",
56+
"--color-sui-input-background": isDark ? "#14532d" : "#dcfce7",
57+
"--color-sui-input-text": isDark ? "#dcfce7" : "#166534",
58+
} as CSSProperties
59+
}
5360
/>
5461
</div>
62+
63+
<div
64+
className="flex flex-col"
65+
style={
66+
{
67+
"--color-sui-input-border": isDark ? "#f87171" : "#ff6b6b",
68+
"--color-sui-input-background": isDark ? "#7f1d1d" : "#fff5f5",
69+
"--color-sui-input-text": isDark ? "#fecaca" : "#d63031",
70+
} as React.CSSProperties
71+
}
72+
>
73+
<span className="text-sm text-gray-500 mb-1">Custom colors using CSS variables</span>
74+
<AddressInput value={value} onChange={setValue} placeholder="Enter an address" />
75+
</div>
5576
</div>
5677
</div>
5778

5879
{/* Manual Implementation using useAddress hook */}
5980
<div className="space-y-4 border-t border-gray-700 pt-6">
60-
<h2 className="text-xl font-semibold text-gray-200">Manual Implementation (useAddressInput Hook)</h2>
81+
<h2 className="text-xl font-semibold text-[var(--color-sui-primary-content)]">
82+
Manual Implementation (useAddressInput Hook)
83+
</h2>
6184

62-
<h3 className="text-lg font-semibold text-gray-200">Shows address when ENS is entered and vice versa</h3>
85+
<h3 className="text-lg font-semibold text-[var(--color-sui-primary-content)]">
86+
Shows address when ENS is entered and vice versa
87+
</h3>
6388

6489
{ensAddress ? <div className="bg-[#dae8ff] items-center">Address: {ensAddress}</div> : null}
6590
{ensName ? (
@@ -73,7 +98,7 @@ export const UseAddressInputExample = () => {
7398
}
7499
</span>
75100
) : null}
76-
<span className="text-accent px-2">{ensName}</span>
101+
<span className="text-sui-primary px-2">{ensName}</span>
77102
</div>
78103
) : (
79104
(isEnsNameLoading || isEnsAddressLoading) && (

example/app/components/UseBalanceExample.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { useBalance } from "@scaffold-ui/hooks";
33
import { Balance } from "@scaffold-ui/components";
44
import { mainnet, polygon } from "viem/chains";
5+
import { CSSProperties } from "react";
56

67
export const UseBalanceExample = () => {
78
// atg.eth
@@ -28,7 +29,7 @@ export const UseBalanceExample = () => {
2829
<div className="space-y-8">
2930
{/* Balance Component Examples */}
3031
<div className="space-y-4">
31-
<h2 className="text-xl font-semibold text-gray-200">Balance Component Examples</h2>
32+
<h2 className="text-xl font-semibold text-[var(--color-sui-primary-content)]">Balance Component Examples</h2>
3233
<div className="space-y-4">
3334
<div className="flex flex-col items-center">
3435
<span className="text-sm text-gray-500 mb-1 self-start">Default (ETH, click to toggle USD)</span>
@@ -39,8 +40,16 @@ export const UseBalanceExample = () => {
3940
<Balance address={address} defaultUsdMode={true} />
4041
</div>
4142
<div className="flex flex-col items-center">
42-
<span className="text-sm text-gray-500 mb-1 self-start">Custom className</span>
43-
<Balance address={address} className="text-lg text-green-400" />
43+
<span className="text-sm text-gray-500 mb-1 self-start">Custom styles</span>
44+
<Balance
45+
address={address}
46+
style={
47+
{
48+
"--color-sui-primary-content": "var(--color-sui-success)",
49+
fontSize: "1.2rem",
50+
} as CSSProperties
51+
}
52+
/>
4453
</div>
4554
</div>
4655
</div>
@@ -53,7 +62,9 @@ export const UseBalanceExample = () => {
5362
</div>
5463
{/* Manual Implementation using useBalance hook */}
5564
<div className="space-y-4 border-t border-gray-700 pt-6">
56-
<h2 className="text-xl font-semibold text-gray-200">Manual Implementation (useBalance Hook)</h2>
65+
<h2 className="text-xl font-semibold text-[var(--color-sui-primary-content)]">
66+
Manual Implementation (useBalance Hook)
67+
</h2>
5768
<div className="flex flex-col gap-2">
5869
<span className="text-sm text-gray-500">Balance</span>
5970
<button

example/app/components/UseEtherInputExample.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"use client";
2-
import React, { useState } from "react";
2+
import React, { CSSProperties, useState } from "react";
33
import { EtherInput } from "@scaffold-ui/components";
44
import { useEtherInput } from "@scaffold-ui/hooks";
55

@@ -14,7 +14,7 @@ export const UseEtherInputExample = () => {
1414
<h1 className="text-2xl font-bold mb-6">useEtherInput Hook & EtherInput Component Examples</h1>
1515
<div className="space-y-4">
1616
{/* EtherInput Component Examples */}
17-
<h2 className="text-xl font-semibold text-gray-200">EtherInput Component Examples</h2>
17+
<h2 className="text-xl font-semibold text-[var(--color-sui-primary-content)]">EtherInput Component Examples</h2>
1818
<div className="space-y-4">
1919
<div className="flex flex-col">
2020
<span className="text-sm text-gray-500 mb-1 self-start">Default (ETH, click to toggle USD)</span>
@@ -51,19 +51,23 @@ export const UseEtherInputExample = () => {
5151
onValueChange={({ valueInEth, valueInUsd, displayUsdMode }) =>
5252
console.log("value changed", valueInEth, valueInUsd, displayUsdMode)
5353
}
54-
colors={{
55-
border: "#eab308",
56-
background: "#fef9c3",
57-
text: "#713f12",
58-
}}
54+
style={
55+
{
56+
"--color-sui-input-border": "#eab308",
57+
"--color-sui-input-background": "#fef9c3",
58+
"--color-sui-input-text": "#713f12",
59+
} as CSSProperties
60+
}
5961
/>
6062
</div>
6163
</div>
6264
</div>
6365

6466
{/* Manual Implementation using useEtherInput Hook */}
6567
<div className="space-y-4 border-t border-gray-700 pt-6 mt-8">
66-
<h2 className="text-xl font-semibold text-gray-200">Manual Implementation (useEtherInput Hook)</h2>
68+
<h2 className="text-xl font-semibold text-[var(--color-sui-primary-content)]">
69+
Manual Implementation (useEtherInput Hook)
70+
</h2>
6771
<div className="flex items-center gap-2 mb-2">
6872
<input
6973
className="input input-bordered w-40"

example/app/globals.css

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
11
@import "tailwindcss";
22

3+
@custom-variant dark (&:is(:root.dark, :root.dark *, :root[data-theme=dark], :root[data-theme=dark] *));
4+
35
:root {
46
--background: #ffffff;
57
--foreground: #171717;
68
}
79

10+
/* Override Scaffold UI theme colors */
11+
/* :root {
12+
--color-sui-primary: #ff4444;
13+
--color-sui-primary-subtle: #ff9999;
14+
--color-sui-primary-neutral: #ffcccc;
15+
--color-sui-success: #00cc66;
16+
--color-sui-warning: #ffaa00;
17+
--color-sui-error: #ff3333;
18+
} */
19+
20+
/* @variant dark {
21+
--color-sui-primary: #654321;
22+
--color-sui-primary-subtle: #987654;
23+
--color-sui-primary-neutral: #ccaa88;
24+
--color-sui-primary-content: #f9fbff;
25+
26+
--color-sui-input-text: var(--color-sui-primary-content);
27+
} */
28+
829
@theme inline {
930
--color-background: var(--background);
1031
--color-foreground: var(--foreground);

example/app/layout.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import type { Metadata } from "next";
22
import { Inter } from "next/font/google";
3-
import "./globals.css";
4-
import "@rainbow-me/rainbowkit/styles.css";
53
import "@scaffold-ui/components/styles.css";
4+
import "@rainbow-me/rainbowkit/styles.css";
5+
import "./globals.css";
66
import { ScaffoldEthAppWithProviders } from "./ScaffoldETHProvider";
7+
import { ThemeProvider } from "./components/ThemeProvider";
78

89
const inter = Inter({ subsets: ["latin"] });
910

@@ -20,7 +21,9 @@ export default function RootLayout({
2021
return (
2122
<html lang="en">
2223
<body className={inter.className}>
23-
<ScaffoldEthAppWithProviders>{children}</ScaffoldEthAppWithProviders>
24+
<ThemeProvider enableSystem>
25+
<ScaffoldEthAppWithProviders>{children}</ScaffoldEthAppWithProviders>
26+
</ThemeProvider>
2427
</body>
2528
</html>
2629
);

example/app/page.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@ import { UseAddressExample } from "./components/UseAddressExample";
33
import { UseBalanceExample } from "./components/UseBalanceExample";
44
import { UseAddressInputExample } from "./components/UseAddressInputExample";
55
import { UseEtherInputExample } from "./components/UseEtherInputExample";
6+
import { SwitchTheme } from "./components/SwitchTheme";
67

78
export default function Home() {
89
return (
9-
<main className="min-h-screen bg-gradient-to-b from-transparent to-gray-900">
10+
<main className="min-h-screen bg-[var(--color-sui-primary-neutral)]">
1011
<div className="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
1112
<div className="flex justify-end mb-8">
1213
<ConnectButton />
1314
</div>
1415

15-
<div className="flex flex-col items-center">
16-
<h1 className="text-4xl font-bold mb-12">Scaffold UI Example</h1>
16+
<div className="flex flex-col items-center text-[var(--color-sui-primary-content)]">
17+
<h1 className="text-4xl font-bold mb-8">Scaffold UI Example</h1>
18+
<SwitchTheme />
19+
1720
<UseAddressExample />
1821
<div className="my-12 border-t border-gray-700 w-full max-w-2xl" />
1922
<UseBalanceExample />

0 commit comments

Comments
 (0)