shade-shift
v0.0.2
Published
Advanced theme transition library for Next.js applications
Downloads
3
Maintainers
Readme
ShadeShift: Advanced Theme Transitions for Next.js
ShadeShift is a powerful, flexible, and performant theme transition library designed specifically for Next.js applications. It provides seamless, animated transitions between themes, enhancing user experience and adding a layer of polish to your web applications.
🌟 Key Features
- 🎨 Smooth, customizable theme transitions
- 🚀 Optimized for performance with requestAnimationFrame
- 🔄 Seamless integration with Next.js and next-themes
- 🧩 Modular architecture for easy extensibility
- 📱 Responsive design support
- 🔧 Fine-grained control over transition parameters
- 🔌 Plugin system for custom transition effects
- 📊 Built-in analytics for transition performance monitoring
- 💡 TypeScript support for enhanced developer experience
- 🛠️ Easy to use API with hooks and components
Table of Contents
- ShadeShift: Advanced Theme Transitions for Next.js
🚀 Quick Start
Get started with ShadeShift in your Next.js project by following these steps:
Installation
Install ShadeShift and its peer dependency:
npm install shade-shift next-themes # or yarn add shade-shift next-themes # or pnpm add shade-shift next-themes
Setup ShadeShiftProvider
You have two options to set up the
ShadeShiftProvider
:// context/theme-provider.tsx "use client"; import * as React from "react"; import { ThemeProvider as NextThemesProvider } from "next-themes"; import { type ThemeProviderProps } from "next-themes/dist/types"; import { ShadeShiftProvider } from "shade-shift"; export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return ( <ShadeShiftProvider> <NextThemesProvider {...props}>{children}</NextThemesProvider> </ShadeShiftProvider> ); }
Then, use this
ThemeProvider
in your_app.tsx
orlayout.tsx
:// pages/_app.tsx or app/layout.tsx import { ThemeProvider } from "../context/theme-provider"; export default function App({ Component, pageProps }) { return ( <ThemeProvider attribute="class" defaultTheme="dark"> <Component {...pageProps} /> </ThemeProvider> ); }
// pages/_app.tsx import { ShadeShiftProvider } from "shade-shift"; import { ThemeProvider } from "next-themes"; function MyApp({ Component, pageProps }) { return ( <ShadeShiftProvider> <ThemeProvider attribute="class" defaultTheme="dark"> <Component {...pageProps} /> </ThemeProvider> </ShadeShiftProvider> ); } export default MyApp;
Implement Theme Toggle
You can now use ShadeShift's hooks to implement theme toggling. Here are two approaches:
import { ThemeToggle } from "shade-shift/components"; function MyComponent() { return ( <div> <h1>My App</h1> <ThemeToggle /> </div> ); }
"use client"; import * as React from "react"; import { AnimatePresence, motion } from "framer-motion"; import { DesktopIcon, MoonIcon, SunIcon } from "@radix-ui/react-icons"; import { Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { useTheme } from "next-themes"; import { shade_shift } from "shade-shift"; export function ThemeToggle() { const { theme, setTheme } = useTheme(); const [currentTheme, setCurrentTheme] = useState<string | undefined>( undefined ); const { transition_theme } = shade_shift.use_theme_transition(); useEffect(() => { setCurrentTheme(theme); }, [theme]); const handleToggle = () => { setCurrentTheme((prevTheme) => { let newTheme; if (prevTheme === "light") { newTheme = "dark"; } else if (prevTheme === "dark") { newTheme = "system"; } else { newTheme = "light"; } transition_theme(newTheme, { type: "vortex", duration: 1000, intensity: 5, }); return newTheme; }); }; if (currentTheme === undefined) { return null; } return ( <TooltipProvider> <Tooltip> <TooltipTrigger asChild> <Button variant="ringHoverOutline" size="icon" onClick={handleToggle} className="relative flex items-center justify-center" > <AnimatePresence initial={false} mode="wait"> {currentTheme === "light" && ( <motion.div key="light" initial={{ opacity: 0, rotate: -90, scale: 0 }} animate={{ opacity: 1, rotate: 0, scale: 1 }} exit={{ opacity: 0, rotate: 90, scale: 0 }} transition={{ duration: 0.15 }} className="absolute" > <SunIcon className="h-[1.2rem] w-[1.2rem]" /> </motion.div> )} {currentTheme === "dark" && ( <motion.div key="dark" initial={{ opacity: 0, rotate: -90, scale: 0 }} animate={{ opacity: 1, rotate: 0, scale: 1 }} exit={{ opacity: 0, rotate: 90, scale: 0 }} transition={{ duration: 0.15 }} className="absolute" > <MoonIcon className="h-[1.2rem] w-[1.2rem]" /> </motion.div> )} {currentTheme === "system" && ( <motion.div key="system" initial={{ opacity: 0, rotate: -90, scale: 0 }} animate={{ opacity: 1, rotate: 0, scale: 1 }} exit={{ opacity: 0, rotate: 90, scale: 0 }} transition={{ duration: 0.15 }} className="absolute" > <DesktopIcon className="h-[1.2rem] w-[1.2rem]" /> </motion.div> )} </AnimatePresence> <span className="sr-only">Toggle theme</span> </Button> </TooltipTrigger> <TooltipContent sideOffset={10}> {currentTheme === "light" && <p>Light Mode</p>} {currentTheme === "dark" && <p>Dark Mode</p>} {currentTheme === "system" && <p>System Mode</p>} <TooltipArrow className="fill-primary" /> </TooltipContent> </Tooltip> </TooltipProvider> ); }
Optional: Set up Route-based Theme Switching
If you want to change themes based on routes, you can use the
useRouteThemeSync
hook:import { shadeShift } from "shade-shift"; function MyApp({ Component, pageProps }) { shadeShift.useRouteThemeSync({ "/": { theme: "light", transition: { type: "fade", duration: 500 } }, "/dark-mode": { theme: "dark", transition: { type: "vortex", duration: 1000 }, }, }); return <Component {...pageProps} />; }
That's it! You now have ShadeShift set up in your Next.js project with smooth theme transitions.
🛠️ Installation
To install ShadeShift, run one of the following commands in your project directory:
npm install shade-shift next-themes
# or
yarn add shade-shift next-themes
# or
pnpm add shade-shift next-themes
🔧 Usage
Setting up ShadeShiftProvider
The ShadeShiftProvider
component is the core of ShadeShift. It manages the theme transition state and provides the necessary context for all ShadeShift hooks and components.
import { ShadeShiftProvider } from "shade-shift";
import { ThemeProvider } from "next-themes";
function MyApp({ Component, pageProps }) {
return (
<ShadeShiftProvider>
<ThemeProvider attribute="class" defaultTheme="dark">
<Component {...pageProps} />
</ThemeProvider>
</ShadeShiftProvider>
);
}
export default MyApp;
Implementing Theme Toggle
You can implement theme toggling using either the pre-built ThemeToggle
component or by creating a custom component using ShadeShift hooks.
import { ThemeToggle } from "shade-shift/components";
function MyComponent() {
return (
<div>
<h1>My App</h1>
<ThemeToggle />
</div>
);
}
import { shadeShift } from "shade-shift";
import { useTheme } from "next-themes";
function CustomThemeToggle() {
const { theme, setTheme } = useTheme();
const { transitionTheme } = shadeShift.useThemeTransition();
const toggleTheme = () => {
const newTheme = theme === "light" ? "dark" : "light";
transitionTheme(newTheme, {
type: "vortex",
duration: 1000,
intensity: 5,
});
};
return <button onClick={toggleTheme}>Toggle Theme</button>;
}
Route-based Theme Switching
ShadeShift allows you to change themes based on routes using the useRouteThemeSync
hook:
import { shadeShift } from "shade-shift";
function MyApp({ Component, pageProps }) {
shadeShift.useRouteThemeSync({
"/": { theme: "light", transition: { type: "fade", duration: 500 } },
"/dark-mode": {
theme: "dark",
transition: { type: "vortex", duration: 1000 },
},
});
return <Component {...pageProps} />;
}
🧠 Core Concepts
ShadeShift is built around several core concepts:
- Transition Engine: Manages the queue and execution of theme transitions.
- Transition Effects: Implements various visual effects for theme transitions.
- Theme Provider: Integrates with next-themes for seamless theme management.
- Route Sync: Allows for automatic theme changes based on routes.
📚 API Reference
ShadeShiftProvider
The main provider component that wraps your application.
<ShadeShiftProvider>{/* Your app components */}</ShadeShiftProvider>
useThemeTransition
A hook that provides theme transition functionality.
const { theme, transitionTheme, transitionState } =
shadeShift.useThemeTransition();
theme
: The current themetransitionTheme(newTheme: string, config: TransitionConfig)
: Function to trigger a theme transitiontransitionState
: Current state of the transition
useRouteThemeSync
A hook that synchronizes themes with routes.
const routeThemeMap = {
"/": { theme: "light", transition: { type: "fade", duration: 500 } },
"/dark-mode": {
theme: "dark",
transition: { type: "vortex", duration: 1000 },
},
};
shadeShift.useRouteThemeSync(routeThemeMap);
TransitionConfig
Configuration object for transitions.
interface TransitionConfig {
type: "splitScreen" | "diagonalWipe" | "morphology" | "pixelate" | "vortex";
duration: number;
easing: string;
direction?:
| "horizontal"
| "vertical"
| "topLeft"
| "topRight"
| "bottomLeft"
| "bottomRight";
intensity?: number;
}
🔬 Advanced Usage
Custom Transition Effects
You can create custom transition effects by implementing the TransitionFunction
type:
import { TransitionFunction } from "shade-shift";
const myCustomTransition: TransitionFunction = async (
element,
fromTheme,
toTheme,
config,
onProgress
) => {
const { duration } = config;
const start = performance.now();
const animate = (now: number) => {
const progress = Math.min((now - start) / duration, 1);
onProgress(progress);
// Implement your custom animation logic here
element.style.backgroundColor = progress < 0.5 ? fromTheme : toTheme;
if (progress < 1) {
requestAnimationFrame(animate);
}
};
requestAnimationFrame(animate);
return new Promise<void>((resolve) => {
setTimeout(resolve, duration);
});
};
// Usage
transitionTheme("dark", {
type: "custom",
duration: 1000,
customTransition: myCustomTransition,
});
Transition Chaining
Chain multiple transitions for complex effects:
const complexTransition = async () => {
await transitionTheme("intermediateTheme", { type: "fade", duration: 500 });
await transitionTheme("finalTheme", { type: "vortex", duration: 1000 });
};
// Usage
<button onClick={complexTransition}>Complex Transition</button>;
Dynamic Transition Parameters
Adjust transition parameters based on device capabilities or user preferences:
const adaptiveTransition = () => {
const { performanceScore } = getDeviceCapabilities();
const duration = performanceScore > 80 ? 1000 : 500;
transitionTheme("newTheme", { type: "morphology", duration });
};
// Usage
<button onClick={adaptiveTransition}>Adaptive Transition</button>;
⚡ Performance Optimization
ShadeShift is designed with performance in mind, but here are some tips to ensure smooth transitions:
- Use the
throttle
anddebounce
utilities for event handlers. - Implement progressive enhancement for low-end devices.
- Optimize your theme stylesheets to minimize CSSOM operations.
- Use the built-in performance monitoring tools to identify bottlenecks.
- Consider using the
will-change
CSS property to optimize animations.
import { shadeShift } from "shade-shift";
// Throttle theme transitions
const throttledTransition = shadeShift.throttle((newTheme, config) => {
shadeShift.transitionTheme(newTheme, config);
}, 200);
// Debounce theme changes based on user input
const debouncedThemeChange = shadeShift.debounce((newTheme) => {
shadeShift.transitionTheme(newTheme, { type: "fade", duration: 300 });
}, 300);
// Progressive enhancement
const performTransition = (newTheme, config) => {
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
// Simplified transition for users who prefer reduced motion
shadeShift.transitionTheme(newTheme, { type: "fade", duration: 100 });
} else {
shadeShift.transitionTheme(newTheme, config);
}
};
// Optimize CSS animations
const optimizeForAnimation = (element) => {
element.style.willChange = "transform, opacity";
// Perform animation
element.addEventListener("transitionend", () => {
element.style.willChange = "auto";
});
};
🎨 Styling and Theming
ShadeShift is designed to work seamlessly with your existing styling solutions. Here are some tips for integrating ShadeShift with popular styling approaches:
CSS Variables
Use CSS variables to define your theme colors and easily switch between them:
:root {
--background: white;
--text: black;
}
[data-theme="dark"] {
--background: black;
--text: white;
}
body {
background-color: var(--background);
color: var(--text);
}
Tailwind CSS
If you're using Tailwind CSS, you can leverage its built-in dark mode support. Here's how to set it up in your globals.css
or main CSS file:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: white;
--text: black;
}
.dark {
--background: black;
--text: white;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
Then, in your Tailwind config file (tailwind.config.js
), ensure you have the dark mode set up:
module.exports = {
darkMode: "class",
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--text)",
},
},
},
// ... other configurations
};
Now you can use these variables in your components:
function MyComponent() {
return (
<div className="bg-background text-foreground">
{/* Your component content */}
</div>
);
}
Styled Components
For Styled Components users, you can access the current theme in your styles:
import styled from "styled-components";
const StyledComponent = styled.div`
background-color: ${(props) => props.theme.background};
color: ${(props) => props.theme.text};
`;
🌐 Browser Compatibility
ShadeShift is compatible with all modern browsers. For older browsers, we provide fallback options that gracefully degrade the transition effects.
| Browser | Minimum Version | | ------- | --------------- | | Chrome | 60 | | Firefox | 55 | | Safari | 11 | | Edge | 79 | | IE | Not supported |
🔍 Troubleshooting
Here are some common issues and their solutions:
Transitions not working
- Ensure
ShadeShiftProvider
is at the root of your app. - Check if
next-themes
is properly configured.
- Ensure
Flickering during transitions
- Check for conflicting CSS animations.
- Ensure your CSS is properly scoped to avoid overrides.
Performance issues
- Use the performance optimization tips mentioned above.
- Consider simplifying complex transitions on low-end devices.
TypeScript errors
- Make sure you're using the latest version of ShadeShift.
- Check if your
tsconfig.json
includes all necessary compiler options.
If you encounter any other issues, please check our GitHub issues page or open a new issue.
🤝 Contributing
We welcome contributions to ShadeShift! Here's how you can help:
- Fork the repository and create your branch from
main
. - If you've added code that should be tested, add tests.
- Ensure the test suite passes.
- Make sure your code lints.
- Issue that pull request!
Please see our Contributing Guide for more details.
📄 License
ShadeShift is MIT licensed. See the LICENSE file for details.
📈 Star History
Built with ❤️ by BankkRoll