@gv-tech/design-system 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +24 -0
- package/dist/components/ui/theme-provider.d.ts +4 -0
- package/dist/components/ui/theme-provider.d.ts.map +1 -0
- package/dist/components/ui/theme-provider.test.d.ts +2 -0
- package/dist/components/ui/theme-provider.test.d.ts.map +1 -0
- package/dist/components/ui/theme-toggle.test.d.ts +2 -0
- package/dist/components/ui/theme-toggle.test.d.ts.map +1 -0
- package/dist/design-system.css +1 -1
- package/dist/hooks/use-theme.d.ts +52 -0
- package/dist/hooks/use-theme.d.ts.map +1 -0
- package/dist/hooks/use-theme.test.d.ts +2 -0
- package/dist/hooks/use-theme.test.d.ts.map +1 -0
- package/dist/index.cjs.js +2 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +736 -723
- package/dist/index.es.js.map +1 -1
- package/dist/pages/GettingStarted.d.ts.map +1 -1
- package/dist/pages/components/ThemeToggleDocs.d.ts.map +1 -1
- package/dist/registry/alert-dialog.test.json +1 -1
- package/dist/registry/index.json +21 -0
- package/dist/registry/theme-provider.json +13 -0
- package/dist/registry/theme-provider.test.json +13 -0
- package/dist/registry/theme-toggle.json +1 -1
- package/dist/registry/theme-toggle.test.json +13 -0
- package/dist/{vendor-BLvpSabH.mjs → vendor-Bcg_ARLM.mjs} +1111 -1140
- package/dist/vendor-Bcg_ARLM.mjs.map +1 -0
- package/dist/{vendor-n4WFhtJT.js → vendor-BrqPND3G.js} +12 -12
- package/dist/vendor-BrqPND3G.js.map +1 -0
- package/package.json +3 -2
- package/scripts/validate.js +1 -0
- package/src/App.tsx +2 -2
- package/src/components/ui/alert-dialog.test.tsx +2 -0
- package/src/components/ui/theme-provider.test.tsx +47 -0
- package/src/components/ui/theme-provider.tsx +12 -0
- package/src/components/ui/theme-toggle.test.tsx +49 -0
- package/src/components/ui/theme-toggle.tsx +1 -1
- package/src/hooks/use-theme.test.tsx +27 -0
- package/src/hooks/use-theme.ts +15 -0
- package/src/index.ts +2 -0
- package/src/pages/GettingStarted.tsx +37 -6
- package/src/pages/components/ThemeToggleDocs.tsx +187 -13
- package/vite.config.ts +2 -1
- package/dist/vendor-BLvpSabH.mjs.map +0 -1
- package/dist/vendor-n4WFhtJT.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gv-tech/design-system",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Garcia Ventures react design system",
|
|
5
5
|
"repository": "git@github.com:Garcia-Ventures/gvtech-design.git",
|
|
6
6
|
"license": "MIT",
|
|
@@ -99,7 +99,6 @@
|
|
|
99
99
|
"date-fns": "^4.1.0",
|
|
100
100
|
"embla-carousel-react": "^8.6.0",
|
|
101
101
|
"lucide-react": "^0.563.0",
|
|
102
|
-
"next-themes": "^0.4.6",
|
|
103
102
|
"react-day-picker": "^9.13.2",
|
|
104
103
|
"react-hook-form": "^7.71.1",
|
|
105
104
|
"react-icons": "^5.5.0",
|
|
@@ -131,6 +130,7 @@
|
|
|
131
130
|
"husky": "^9.1.7",
|
|
132
131
|
"jsdom": "^27.4.0",
|
|
133
132
|
"lint-staged": "^16.2.7",
|
|
133
|
+
"next-themes": "^0.4.6",
|
|
134
134
|
"postcss": "^8.5.6",
|
|
135
135
|
"prettier": "^3.8.1",
|
|
136
136
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
@@ -145,6 +145,7 @@
|
|
|
145
145
|
"vitest": "^4.0.18"
|
|
146
146
|
},
|
|
147
147
|
"peerDependencies": {
|
|
148
|
+
"next-themes": "^0.4.0",
|
|
148
149
|
"prop-types": "^15.8.0",
|
|
149
150
|
"react": "^18 || ^19",
|
|
150
151
|
"react-dom": "^18 || ^19",
|
package/scripts/validate.js
CHANGED
package/src/App.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ThemeProvider } from 'next-themes';
|
|
2
1
|
import * as React from 'react';
|
|
3
2
|
import { Footer, Sidebar } from './components/docs';
|
|
4
3
|
import { navItems } from './components/docs/Sidebar';
|
|
@@ -21,6 +20,7 @@ import {
|
|
|
21
20
|
SearchTrigger,
|
|
22
21
|
} from './components/ui/search';
|
|
23
22
|
import { Toaster as SonnerToaster } from './components/ui/sonner';
|
|
23
|
+
import { ThemeProvider } from './components/ui/theme-provider';
|
|
24
24
|
import { ThemeToggle } from './components/ui/theme-toggle';
|
|
25
25
|
import { Toaster } from './components/ui/toaster';
|
|
26
26
|
import { TooltipProvider } from './components/ui/tooltip';
|
|
@@ -205,7 +205,7 @@ function App() {
|
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
return (
|
|
208
|
-
<ThemeProvider
|
|
208
|
+
<ThemeProvider>
|
|
209
209
|
<TooltipProvider>
|
|
210
210
|
<div className="flex h-screen bg-background">
|
|
211
211
|
{/* Sidebar */}
|
|
@@ -50,6 +50,7 @@ describe('AlertDialog', () => {
|
|
|
50
50
|
<AlertDialogContent>
|
|
51
51
|
<AlertDialogHeader>
|
|
52
52
|
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
53
|
+
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
53
54
|
</AlertDialogHeader>
|
|
54
55
|
<AlertDialogFooter>
|
|
55
56
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
@@ -72,6 +73,7 @@ describe('AlertDialog', () => {
|
|
|
72
73
|
<AlertDialogContent>
|
|
73
74
|
<AlertDialogHeader>
|
|
74
75
|
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
76
|
+
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
75
77
|
</AlertDialogHeader>
|
|
76
78
|
<AlertDialogFooter>
|
|
77
79
|
<AlertDialogAction>Continue</AlertDialogAction>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ThemeProvider } from '@/components/ui/theme-provider';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
describe('ThemeProvider', () => {
|
|
6
|
+
it('renders children correctly', () => {
|
|
7
|
+
render(
|
|
8
|
+
<ThemeProvider>
|
|
9
|
+
<div data-testid="child">Hello</div>
|
|
10
|
+
</ThemeProvider>,
|
|
11
|
+
);
|
|
12
|
+
expect(screen.getByTestId('child')).toBeInTheDocument();
|
|
13
|
+
expect(screen.getByTestId('child')).toHaveTextContent('Hello');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('applies class attribute to html element by default', () => {
|
|
17
|
+
render(
|
|
18
|
+
<ThemeProvider>
|
|
19
|
+
<div>Content</div>
|
|
20
|
+
</ThemeProvider>,
|
|
21
|
+
);
|
|
22
|
+
// next-themes applies the attribute to the html element;
|
|
23
|
+
// in jsdom the attribute should be set to 'class' by default
|
|
24
|
+
const html = document.documentElement;
|
|
25
|
+
// The attribute prop controls how the theme class is toggled.
|
|
26
|
+
// We verify the provider renders without error, which confirms the defaults are valid.
|
|
27
|
+
expect(html).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('allows overriding default props', () => {
|
|
31
|
+
render(
|
|
32
|
+
<ThemeProvider defaultTheme="dark" enableSystem={false}>
|
|
33
|
+
<div data-testid="child">Dark Mode</div>
|
|
34
|
+
</ThemeProvider>,
|
|
35
|
+
);
|
|
36
|
+
expect(screen.getByTestId('child')).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('passes additional next-themes props through', () => {
|
|
40
|
+
render(
|
|
41
|
+
<ThemeProvider storageKey="my-app-theme" themes={['light', 'dark', 'ocean']}>
|
|
42
|
+
<div data-testid="child">Custom themes</div>
|
|
43
|
+
</ThemeProvider>,
|
|
44
|
+
);
|
|
45
|
+
expect(screen.getByTestId('child')).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ThemeProviderProps as NextThemesProviderProps } from 'next-themes';
|
|
2
|
+
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
|
3
|
+
|
|
4
|
+
export type ThemeProviderProps = NextThemesProviderProps;
|
|
5
|
+
|
|
6
|
+
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
|
7
|
+
return (
|
|
8
|
+
<NextThemesProvider attribute="class" defaultTheme="system" enableSystem {...props}>
|
|
9
|
+
{children}
|
|
10
|
+
</NextThemesProvider>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { ThemeProvider } from 'next-themes';
|
|
5
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
6
|
+
|
|
7
|
+
// Mock the useTheme hook to control its return values
|
|
8
|
+
vi.mock('@/hooks/use-theme', async () => {
|
|
9
|
+
const actual = await vi.importActual('@/hooks/use-theme');
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
useTheme: () => ({
|
|
13
|
+
theme: 'light',
|
|
14
|
+
setTheme: vi.fn(),
|
|
15
|
+
resolvedTheme: 'light',
|
|
16
|
+
tokens: {},
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('ThemeToggle', () => {
|
|
22
|
+
it('renders binary toggle by default', () => {
|
|
23
|
+
render(
|
|
24
|
+
<ThemeProvider>
|
|
25
|
+
<ThemeToggle />
|
|
26
|
+
</ThemeProvider>,
|
|
27
|
+
);
|
|
28
|
+
// Use role button which is accessible
|
|
29
|
+
const button = screen.getByRole('button', { name: /toggle theme/i });
|
|
30
|
+
expect(button).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('renders ternary toggle with dropdown', async () => {
|
|
34
|
+
const user = userEvent.setup();
|
|
35
|
+
render(
|
|
36
|
+
<ThemeProvider>
|
|
37
|
+
<ThemeToggle variant="ternary" />
|
|
38
|
+
</ThemeProvider>,
|
|
39
|
+
);
|
|
40
|
+
const button = screen.getByRole('button'); // Dropdown trigger
|
|
41
|
+
expect(button).toBeInTheDocument();
|
|
42
|
+
|
|
43
|
+
// Open dropdown
|
|
44
|
+
await user.click(button);
|
|
45
|
+
expect(await screen.findByText('Light')).toBeInTheDocument();
|
|
46
|
+
expect(screen.getByText('Dark')).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByText('System')).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
DropdownMenuItem,
|
|
6
6
|
DropdownMenuTrigger,
|
|
7
7
|
} from '@/components/ui/dropdown-menu';
|
|
8
|
+
import { useTheme } from '@/hooks/use-theme';
|
|
8
9
|
import { cn } from '@/lib/utils';
|
|
9
10
|
import { Moon, Sun, SunMoon } from 'lucide-react';
|
|
10
|
-
import { useTheme } from 'next-themes';
|
|
11
11
|
|
|
12
12
|
export interface ThemeToggleProps {
|
|
13
13
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useTheme } from '@/hooks/use-theme';
|
|
2
|
+
import { theme } from '@/theme/tokens';
|
|
3
|
+
import { renderHook } from '@testing-library/react';
|
|
4
|
+
import { ThemeProvider } from 'next-themes';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
describe('useTheme', () => {
|
|
8
|
+
it('returns default light tokens when no theme is set', () => {
|
|
9
|
+
const { result } = renderHook(() => useTheme(), {
|
|
10
|
+
wrapper: ({ children }) => <ThemeProvider>{children}</ThemeProvider>,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
expect(result.current.tokens).toEqual(theme.light);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('returns dark tokens when theme is dark', () => {
|
|
17
|
+
const { result } = renderHook(() => useTheme(), {
|
|
18
|
+
wrapper: ({ children }) => (
|
|
19
|
+
<ThemeProvider defaultTheme="dark" enableSystem={false}>
|
|
20
|
+
{children}
|
|
21
|
+
</ThemeProvider>
|
|
22
|
+
),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(result.current.tokens).toEqual(theme.dark);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { theme } from '@/theme/tokens';
|
|
2
|
+
import { useTheme as useNextTheme } from 'next-themes';
|
|
3
|
+
|
|
4
|
+
export function useTheme() {
|
|
5
|
+
const context = useNextTheme();
|
|
6
|
+
const { resolvedTheme } = context;
|
|
7
|
+
|
|
8
|
+
// Default to light theme tokens if resolvedTheme is undefined or invalid
|
|
9
|
+
const activeTokens = resolvedTheme === 'dark' ? theme.dark : theme.light;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
...context,
|
|
13
|
+
tokens: activeTokens,
|
|
14
|
+
};
|
|
15
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -45,6 +45,7 @@ export * from './components/ui/switch';
|
|
|
45
45
|
export * from './components/ui/table';
|
|
46
46
|
export * from './components/ui/tabs';
|
|
47
47
|
export * from './components/ui/textarea';
|
|
48
|
+
export * from './components/ui/theme-provider';
|
|
48
49
|
export * from './components/ui/theme-toggle';
|
|
49
50
|
export * from './components/ui/toast';
|
|
50
51
|
export { Toaster as ToasterToast } from './components/ui/toaster';
|
|
@@ -53,4 +54,5 @@ export * from './components/ui/toggle-group';
|
|
|
53
54
|
export * from './components/ui/tooltip';
|
|
54
55
|
|
|
55
56
|
// Hooks
|
|
57
|
+
export * from './hooks/use-theme';
|
|
56
58
|
export * from './hooks/use-toast';
|
|
@@ -133,10 +133,34 @@ export function InstallationPage() {
|
|
|
133
133
|
<CodeBlock
|
|
134
134
|
code={`{
|
|
135
135
|
"react": "^18 || ^19",
|
|
136
|
-
"react-dom": "^18 || ^19"
|
|
136
|
+
"react-dom": "^18 || ^19",
|
|
137
|
+
"next-themes": "^0.4.0"
|
|
137
138
|
}`}
|
|
138
139
|
language="json"
|
|
139
140
|
/>
|
|
141
|
+
<p className="text-sm text-muted-foreground">
|
|
142
|
+
<code className="text-sm bg-muted px-1.5 py-0.5 rounded">next-themes</code> is required for the design
|
|
143
|
+
system's <code className="text-sm bg-muted px-1.5 py-0.5 rounded">ThemeProvider</code>,{' '}
|
|
144
|
+
<code className="text-sm bg-muted px-1.5 py-0.5 rounded">ThemeToggle</code>, and{' '}
|
|
145
|
+
<code className="text-sm bg-muted px-1.5 py-0.5 rounded">useTheme</code> to work. Install it alongside the
|
|
146
|
+
design system:
|
|
147
|
+
</p>
|
|
148
|
+
<Tabs defaultValue="npm" className="w-full">
|
|
149
|
+
<TabsList>
|
|
150
|
+
<TabsTrigger value="npm">npm</TabsTrigger>
|
|
151
|
+
<TabsTrigger value="yarn">yarn</TabsTrigger>
|
|
152
|
+
<TabsTrigger value="pnpm">pnpm</TabsTrigger>
|
|
153
|
+
</TabsList>
|
|
154
|
+
<TabsContent value="npm" className="mt-4">
|
|
155
|
+
<CodeBlock code="npm install next-themes" language="bash" />
|
|
156
|
+
</TabsContent>
|
|
157
|
+
<TabsContent value="yarn" className="mt-4">
|
|
158
|
+
<CodeBlock code="yarn add next-themes" language="bash" />
|
|
159
|
+
</TabsContent>
|
|
160
|
+
<TabsContent value="pnpm" className="mt-4">
|
|
161
|
+
<CodeBlock code="pnpm add next-themes" language="bash" />
|
|
162
|
+
</TabsContent>
|
|
163
|
+
</Tabs>
|
|
140
164
|
</section>
|
|
141
165
|
|
|
142
166
|
<section className="space-y-4">
|
|
@@ -253,15 +277,22 @@ module.exports = {
|
|
|
253
277
|
|
|
254
278
|
<section className="space-y-4">
|
|
255
279
|
<h2 className="text-2xl font-semibold tracking-tight">Start Using Components</h2>
|
|
256
|
-
<p className="text-muted-foreground">
|
|
280
|
+
<p className="text-muted-foreground">
|
|
281
|
+
Wrap your app with <code className="text-sm bg-muted px-1.5 py-0.5 rounded">ThemeProvider</code> and start
|
|
282
|
+
importing components:
|
|
283
|
+
</p>
|
|
257
284
|
<CodeBlock
|
|
258
|
-
code={`import { Button } from '@gv-tech/design-system';
|
|
285
|
+
code={`import { ThemeProvider, ThemeToggle, Button } from '@gv-tech/design-system';
|
|
286
|
+
import '@gv-tech/design-system/style.css';
|
|
259
287
|
|
|
260
288
|
export default function App() {
|
|
261
289
|
return (
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
290
|
+
<ThemeProvider>
|
|
291
|
+
<div>
|
|
292
|
+
<ThemeToggle variant="ternary" />
|
|
293
|
+
<Button variant="default">Click me</Button>
|
|
294
|
+
</div>
|
|
295
|
+
</ThemeProvider>
|
|
265
296
|
);
|
|
266
297
|
}`}
|
|
267
298
|
/>
|
|
@@ -30,23 +30,178 @@ export function ThemeToggleDocs() {
|
|
|
30
30
|
</ComponentShowcase>
|
|
31
31
|
|
|
32
32
|
<ComponentShowcase
|
|
33
|
-
title="
|
|
34
|
-
description="You can control the theme externally by passing customTheme and onThemeChange props."
|
|
33
|
+
title="Controlled Mode"
|
|
34
|
+
description="You can control the theme externally by passing customTheme and onThemeChange props. This is useful for testing or when using a different theme provider."
|
|
35
35
|
code={`const [theme, setTheme] = useState('light');
|
|
36
36
|
|
|
37
37
|
<ThemeToggle
|
|
38
38
|
customTheme={theme}
|
|
39
|
-
onThemeChange={
|
|
39
|
+
onThemeChange={setTheme}
|
|
40
40
|
/>
|
|
41
41
|
|
|
42
42
|
<p>Current Theme: {theme}</p>`}
|
|
43
43
|
>
|
|
44
44
|
<div className="flex flex-col items-center gap-4">
|
|
45
|
-
<ThemeToggle customTheme={customTheme} onThemeChange={
|
|
45
|
+
<ThemeToggle customTheme={customTheme} onThemeChange={setCustomTheme} />
|
|
46
46
|
<p className="text-sm font-medium">Current Selection: {customTheme}</p>
|
|
47
47
|
</div>
|
|
48
48
|
</ComponentShowcase>
|
|
49
49
|
|
|
50
|
+
{/* ThemeProvider Section */}
|
|
51
|
+
<div className="mt-12 space-y-6">
|
|
52
|
+
<div>
|
|
53
|
+
<h3 className="text-xl font-semibold">ThemeProvider</h3>
|
|
54
|
+
<p className="mt-2 text-muted-foreground">
|
|
55
|
+
The design system exports a pre-configured{' '}
|
|
56
|
+
<code className="text-sm bg-muted px-1.5 py-0.5 rounded">ThemeProvider</code> that wraps{' '}
|
|
57
|
+
<code className="text-sm bg-muted px-1.5 py-0.5 rounded">next-themes</code> with sensible defaults. Wrap
|
|
58
|
+
your application with it to enable theme switching for all design system components.
|
|
59
|
+
</p>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div className="rounded-md border bg-muted p-4">
|
|
63
|
+
<pre className="text-xs">
|
|
64
|
+
<code>
|
|
65
|
+
{`import { ThemeProvider, ThemeToggle } from '@gv-tech/design-system';
|
|
66
|
+
import '@gv-tech/design-system/style.css';
|
|
67
|
+
|
|
68
|
+
function App() {
|
|
69
|
+
return (
|
|
70
|
+
<ThemeProvider>
|
|
71
|
+
<ThemeToggle variant="ternary" />
|
|
72
|
+
{/* Your app content */}
|
|
73
|
+
</ThemeProvider>
|
|
74
|
+
);
|
|
75
|
+
}`}
|
|
76
|
+
</code>
|
|
77
|
+
</pre>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div className="space-y-4">
|
|
81
|
+
<h4 className="font-medium text-foreground">Defaults</h4>
|
|
82
|
+
<p className="text-sm text-muted-foreground">
|
|
83
|
+
Out of the box, <code className="text-sm bg-muted px-1.5 py-0.5 rounded">ThemeProvider</code> applies these
|
|
84
|
+
defaults. You can override any of them by passing the corresponding prop.
|
|
85
|
+
</p>
|
|
86
|
+
<div className="rounded-md border">
|
|
87
|
+
<table className="w-full text-sm">
|
|
88
|
+
<thead>
|
|
89
|
+
<tr className="border-b bg-muted/50">
|
|
90
|
+
<th className="p-3 text-left font-medium">Prop</th>
|
|
91
|
+
<th className="p-3 text-left font-medium">Default</th>
|
|
92
|
+
<th className="p-3 text-left font-medium">Description</th>
|
|
93
|
+
</tr>
|
|
94
|
+
</thead>
|
|
95
|
+
<tbody>
|
|
96
|
+
<tr className="border-b">
|
|
97
|
+
<td className="p-3">
|
|
98
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">attribute</code>
|
|
99
|
+
</td>
|
|
100
|
+
<td className="p-3">
|
|
101
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">"class"</code>
|
|
102
|
+
</td>
|
|
103
|
+
<td className="p-3 text-muted-foreground">Applies the theme as a CSS class on the HTML element</td>
|
|
104
|
+
</tr>
|
|
105
|
+
<tr className="border-b">
|
|
106
|
+
<td className="p-3">
|
|
107
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">defaultTheme</code>
|
|
108
|
+
</td>
|
|
109
|
+
<td className="p-3">
|
|
110
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">"system"</code>
|
|
111
|
+
</td>
|
|
112
|
+
<td className="p-3 text-muted-foreground">Respects the user's operating system preference</td>
|
|
113
|
+
</tr>
|
|
114
|
+
<tr>
|
|
115
|
+
<td className="p-3">
|
|
116
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">enableSystem</code>
|
|
117
|
+
</td>
|
|
118
|
+
<td className="p-3">
|
|
119
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">true</code>
|
|
120
|
+
</td>
|
|
121
|
+
<td className="p-3 text-muted-foreground">Enables automatic detection of the OS color scheme</td>
|
|
122
|
+
</tr>
|
|
123
|
+
</tbody>
|
|
124
|
+
</table>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div className="rounded-md border bg-muted p-4">
|
|
129
|
+
<p className="text-xs font-medium text-muted-foreground mb-2">Overriding defaults:</p>
|
|
130
|
+
<pre className="text-xs">
|
|
131
|
+
<code>
|
|
132
|
+
{`<ThemeProvider
|
|
133
|
+
defaultTheme="dark"
|
|
134
|
+
storageKey="my-app-theme"
|
|
135
|
+
themes={['light', 'dark', 'ocean']}
|
|
136
|
+
>
|
|
137
|
+
<App />
|
|
138
|
+
</ThemeProvider>`}
|
|
139
|
+
</code>
|
|
140
|
+
</pre>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
{/* useTheme Section */}
|
|
145
|
+
<div className="mt-12 space-y-4">
|
|
146
|
+
<h3 className="text-xl font-semibold">useTheme Hook</h3>
|
|
147
|
+
<p className="text-sm text-muted-foreground">
|
|
148
|
+
The <code className="text-sm bg-muted px-1.5 py-0.5 rounded">useTheme</code> hook provides access to the
|
|
149
|
+
current theme, theme controls, and the active design tokens. It must be used within a{' '}
|
|
150
|
+
<code className="text-sm bg-muted px-1.5 py-0.5 rounded">ThemeProvider</code>.
|
|
151
|
+
</p>
|
|
152
|
+
<div className="rounded-md border bg-muted p-4">
|
|
153
|
+
<pre className="text-xs">
|
|
154
|
+
<code>
|
|
155
|
+
{`import { useTheme } from '@gv-tech/design-system';
|
|
156
|
+
|
|
157
|
+
export function MyComponent() {
|
|
158
|
+
const { theme, setTheme, resolvedTheme, tokens } = useTheme();
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div style={{ backgroundColor: tokens.background }}>
|
|
162
|
+
<p>Current theme: {theme}</p>
|
|
163
|
+
<p>Resolved theme: {resolvedTheme}</p>
|
|
164
|
+
<button onClick={() => setTheme('dark')}>Dark Mode</button>
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}`}
|
|
168
|
+
</code>
|
|
169
|
+
</pre>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<PropsTable
|
|
173
|
+
props={[
|
|
174
|
+
{
|
|
175
|
+
name: 'theme',
|
|
176
|
+
type: 'string',
|
|
177
|
+
required: false,
|
|
178
|
+
description: 'The current theme name ("light", "dark", or "system").',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'setTheme',
|
|
182
|
+
type: '(theme: string) => void',
|
|
183
|
+
required: false,
|
|
184
|
+
description: 'Function to programmatically change the theme.',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'resolvedTheme',
|
|
188
|
+
type: 'string',
|
|
189
|
+
required: false,
|
|
190
|
+
description:
|
|
191
|
+
'The resolved theme ("light" or "dark"). Useful when theme is "system" and you need the actual value.',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'tokens',
|
|
195
|
+
type: 'ThemeTokens',
|
|
196
|
+
required: false,
|
|
197
|
+
description:
|
|
198
|
+
'The active color tokens for the current resolved theme (light or dark). Contains background, foreground, primary, and other design token values.',
|
|
199
|
+
},
|
|
200
|
+
]}
|
|
201
|
+
/>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* ThemeToggle Props Section */}
|
|
50
205
|
<div className="space-y-4">
|
|
51
206
|
<h3 className="text-xl font-semibold">ThemeToggle Props</h3>
|
|
52
207
|
<PropsTable
|
|
@@ -81,41 +236,60 @@ export function ThemeToggleDocs() {
|
|
|
81
236
|
/>
|
|
82
237
|
</div>
|
|
83
238
|
|
|
239
|
+
{/* Integration Section */}
|
|
84
240
|
<div className="mt-12 space-y-6">
|
|
85
241
|
<div>
|
|
86
242
|
<h3 className="text-xl font-semibold">Integration</h3>
|
|
87
243
|
<p className="mt-2 text-muted-foreground">
|
|
88
|
-
The
|
|
89
|
-
|
|
244
|
+
The <code className="text-sm bg-muted px-1.5 py-0.5 rounded">ThemeToggle</code> component works seamlessly
|
|
245
|
+
with the design system's <code className="text-sm bg-muted px-1.5 py-0.5 rounded">ThemeProvider</code>. It
|
|
246
|
+
can also be used in a fully controlled manner with any theme provider or custom state.
|
|
90
247
|
</p>
|
|
91
248
|
</div>
|
|
92
249
|
|
|
93
250
|
<div className="grid gap-6 md:grid-cols-2">
|
|
94
251
|
<div className="rounded-lg border bg-muted/50 p-6">
|
|
95
|
-
<h4 className="font-medium text-foreground">With
|
|
252
|
+
<h4 className="font-medium text-foreground">With ThemeProvider (Recommended)</h4>
|
|
96
253
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
97
|
-
|
|
254
|
+
Wrap your app with the design system's{' '}
|
|
255
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">ThemeProvider</code> and drop in the toggle.
|
|
256
|
+
Everything connects automatically.
|
|
98
257
|
</p>
|
|
99
258
|
<pre className="mt-4 overflow-x-auto rounded-md bg-background p-4 text-xs">
|
|
100
|
-
<code>{
|
|
259
|
+
<code>{`import { ThemeProvider, ThemeToggle } from '@gv-tech/design-system';
|
|
260
|
+
|
|
261
|
+
<ThemeProvider>
|
|
101
262
|
<ThemeToggle />
|
|
102
263
|
</ThemeProvider>`}</code>
|
|
103
264
|
</pre>
|
|
104
265
|
</div>
|
|
105
266
|
|
|
106
267
|
<div className="rounded-lg border bg-muted/50 p-6">
|
|
107
|
-
<h4 className="font-medium text-foreground">Custom
|
|
268
|
+
<h4 className="font-medium text-foreground">Controlled / Custom State</h4>
|
|
108
269
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
109
270
|
Pass your own theme state and change handler to integrate with custom logic or external storage.
|
|
110
271
|
</p>
|
|
111
272
|
<pre className="mt-4 overflow-x-auto rounded-md bg-background p-4 text-xs">
|
|
112
|
-
<code>{
|
|
113
|
-
|
|
114
|
-
|
|
273
|
+
<code>{`const [theme, setTheme] = useState("light")
|
|
274
|
+
|
|
275
|
+
<ThemeToggle
|
|
276
|
+
customTheme={theme}
|
|
277
|
+
onThemeChange={setTheme}
|
|
115
278
|
/>`}</code>
|
|
116
279
|
</pre>
|
|
117
280
|
</div>
|
|
118
281
|
</div>
|
|
282
|
+
|
|
283
|
+
<div className="rounded-lg border border-amber-500/30 bg-amber-500/5 p-6">
|
|
284
|
+
<h4 className="font-medium text-amber-600 dark:text-amber-400">⚠️ Important: Shared Context</h4>
|
|
285
|
+
<p className="mt-1 text-sm text-muted-foreground">
|
|
286
|
+
The design system marks <code className="text-xs bg-muted px-1 py-0.5 rounded">next-themes</code> as a{' '}
|
|
287
|
+
<strong>peer dependency</strong>, meaning it uses the same instance as your project. This ensures that{' '}
|
|
288
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">useTheme</code>,{' '}
|
|
289
|
+
<code className="text-xs bg-muted px-1 py-0.5 rounded">ThemeToggle</code>, and your own components all share
|
|
290
|
+
the same theme context — no duplicate providers needed.
|
|
291
|
+
</p>
|
|
292
|
+
</div>
|
|
119
293
|
</div>
|
|
120
294
|
</ComponentSection>
|
|
121
295
|
);
|
package/vite.config.ts
CHANGED
|
@@ -27,13 +27,14 @@ export default defineConfig({
|
|
|
27
27
|
}
|
|
28
28
|
: undefined,
|
|
29
29
|
rollupOptions: {
|
|
30
|
-
external: isLibrary ? ['react', 'react-dom', 'prop-types'] : [],
|
|
30
|
+
external: isLibrary ? ['react', 'react-dom', 'prop-types', 'next-themes'] : [],
|
|
31
31
|
output: {
|
|
32
32
|
globals: isLibrary
|
|
33
33
|
? {
|
|
34
34
|
react: 'React',
|
|
35
35
|
'react-dom': 'ReactDOM',
|
|
36
36
|
'prop-types': 'PropTypes',
|
|
37
|
+
'next-themes': 'NextThemes',
|
|
37
38
|
}
|
|
38
39
|
: {},
|
|
39
40
|
manualChunks(id) {
|