Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@ body,
max-width: 100vw;
overflow-x: hidden;
}

.smooth {
@apply transition-all duration-300 ease-out;
}

input::file-selector-button {
@apply dark:text-zinc-50;
}
5 changes: 4 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google";

import { getMetadata } from "~/lib/metadata";
import Providers from "./providers";

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

Expand All @@ -15,7 +16,9 @@ export default function RootLayout({
}) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
<body className={inter.className}>
<Providers>{children}</Providers>
</body>
</html>
);
}
71 changes: 39 additions & 32 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,53 @@
import Generate from "~/domain/generate";
import clsx from "clsx";
import { Github } from "lucide-react";
import { Plus_Jakarta_Sans } from "next/font/google";
import { Button } from "~/components/ui/button";
import { Github } from "lucide-react";
import Divider from "~/components/ui/divider";
import Generate from "~/domain/generate";
import ThemeSwitcher from "./theme-switcher";

const plusJakartaSans = Plus_Jakarta_Sans({ subsets: ["latin"] });

export default function Home() {
return (
<main className="grid content-start gap-y-4 justify-center min-h-[100svh] items-center p-6 lg:p-16 h-full bg-zinc-100">
<nav className="fixed top-0 left-0 w-full p-4 flex border-b backdrop-filter backdrop-blur-md">
<main className="grid content-start gap-y-4 justify-center min-h-[100svh] items-center p-6 lg:p-16 h-full bg-zinc-100 dark:bg-zinc-950 smooth">
<nav className="fixed top-0 left-0 flex justify-between w-full p-4 border-b backdrop-filter backdrop-blur-md dark:border-zinc-700 smooth">
<div className="flex flex-col gap-y-1">
<h1 className={plusJakartaSans.className}>
<span className="font-extralight text-yellow-500">µ</span>scale
<h1
className={clsx(
plusJakartaSans.className,
" text-zinc-900 dark:text-zinc-50 smooth"
)}
>
<span className="text-yellow-500 font-extralight">µ</span>scale
</h1>
<p className="text-zinc-600 text-xs">
<p className="text-xs text-zinc-600 dark:text-zinc-400 smooth">
Upscale any images with AI up to 4x their original size
</p>
</div>
<div className="flex items-center gap-x-4">
<ThemeSwitcher />

<Divider orientation="vertical" />
<a
href="https://github.com/adevinwild/micro-scale"
target="_blank"
rel="noopener noreferrer"
>
<Button size="sm" className="flex items-center gap-x-2">
<Github size={16} />
<span>Repository</span>
</Button>
</a>
</div>
</nav>
<Generate />
<footer className="fixed bottom-0 left-0 p-4 flex flex-col lg:flex-row gap-y-4 items-center justify-between transition-all bg-zinc-200 border-t border-zinc-300 w-full">
<footer className="fixed bottom-0 left-0 flex flex-col items-center justify-between w-full p-4 transition-all border-t smooth lg:flex-row gap-y-4 bg-zinc-100 dark:bg-zinc-950 dark:border-zinc-700">
<section className="flex flex-col items-center text-center lg:items-start lg:text-left">
<p className="text-xs text-zinc-600">
<p className="text-xs text-zinc-600 dark:text-zinc-400 smooth">
Made by{" "}
<a
className="text-zinc-950"
className="text-zinc-950 dark:text-zinc-50 smooth"
href="https://github.com/adevinwild"
target="_blank"
rel="noopener noreferrer"
Expand All @@ -33,18 +56,18 @@ export default function Home() {
</a>{" "}
for the{" "}
<a
className="text-zinc-950"
className="text-zinc-950 dark:text-zinc-50 smooth"
href="https://thefullstack.network/hackathon/competition"
target="_blank"
rel="noopener noreferrer"
>
Fullstack Network Hackathon #1
</a>
</p>
<p className="text-xs text-zinc-600">
<p className="text-xs text-zinc-600 dark:text-zinc-400 smooth">
Using{" "}
<a
className="text-zinc-950"
className="text-zinc-950 dark:text-zinc-50 smooth "
href="https://nextjs.org"
target="_blank"
rel="noopener noreferrer"
Expand All @@ -53,7 +76,7 @@ export default function Home() {
</a>{" "}
,{" "}
<a
className="text-zinc-950"
className="text-zinc-950 dark:text-zinc-50 smooth"
href="https://vercel.com"
target="_blank"
rel="noopener noreferrer"
Expand All @@ -62,7 +85,7 @@ export default function Home() {
</a>{" "}
,{" "}
<a
className="text-zinc-950"
className="text-zinc-950 dark:text-zinc-50 smooth"
href="https://ui.shadcn.com/"
target="_blank"
rel="noopener noreferrer"
Expand All @@ -71,7 +94,7 @@ export default function Home() {
</a>{" "}
and{" "}
<a
className="text-zinc-950"
className="text-zinc-950 dark:text-zinc-50 smooth"
href="https://replicate.com"
target="_blank"
rel="noopener noreferrer"
Expand All @@ -80,22 +103,6 @@ export default function Home() {
</a>
</p>
</section>
<section>
<a
href="https://github.com/adevinwild/micro-scale"
target="_blank"
rel="noopener noreferrer"
>
<Button
variant="default"
size="sm"
className="flex items-center gap-x-2"
>
<Github size={16} />
<span>GitHub</span>
</Button>
</a>
</section>
</footer>
</main>
);
Expand Down
13 changes: 13 additions & 0 deletions src/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"use client";

import React, { ReactNode } from "react";
import { ThemeProvider } from "~/contexts/theme";

type ProvidersProps = {
children: ReactNode;
};
const Providers = ({ children }: ProvidersProps) => {
return <ThemeProvider>{children}</ThemeProvider>;
};

export default Providers;
23 changes: 23 additions & 0 deletions src/app/theme-switcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import { Moon, Sun } from "lucide-react";
import { Button } from "~/components/ui/button";
import { useThemeContext } from "~/contexts/theme";

const ThemeSwitcher = () => {
const { theme, setTheme } = useThemeContext();

return (
<Button
size="sm"
className="flex items-center gap-x-2 dark:text-zinc-50"
variant="outline"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<span className="sr-only">Toggle dark mode (currently disabled)</span>
{theme === "light" ? <Moon size={16} /> : <Sun size={16} />}
</Button>
);
};

export default ThemeSwitcher;
76 changes: 76 additions & 0 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from "react"

import { cn } from "~/lib/utils"

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border border-zinc-200 bg-white text-zinc-950 shadow dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
className
)}
{...props}
/>
))
Card.displayName = "Card"

const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"

const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"

const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-zinc-500 dark:text-zinc-400", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"

const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"

const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
26 changes: 26 additions & 0 deletions src/components/ui/divider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import clsx from "clsx";
import React from "react";

type Props = {
orientation: "horizontal" | "vertical";
} & React.HTMLAttributes<HTMLDivElement>;

const Divider = ({ orientation, className, ...props }: Props) => {
const orientationStyles = {
horizontal: "w-full h-[1px] bg-zinc-300 dark:bg-zinc-700",
vertical: "w-[1px] h-full bg-zinc-300 dark:bg-zinc-700",
};

return (
<div
className={clsx(orientationStyles[orientation], className)}
{...props}
/>
);
};

Divider.defaultProps = {
orientation: "horizontal",
};

export default Divider;
54 changes: 54 additions & 0 deletions src/contexts/theme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use client";
import {
ReactNode,
createContext,
useContext,
useEffect,
useState,
} from "react";

type Theme = "light" | "dark";

type ThemeContext = {
theme: Theme;
setTheme: (theme: Theme) => void;
};

const ThemeContext = createContext<ThemeContext | null>(null);

type ThemeProviderProps = {
children: ReactNode;
};

export function ThemeProvider({ children }: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>("light");

const contextValue: ThemeContext = {
theme,
setTheme,
};

useEffect(() => {
const htmlElement = document.querySelector("html");
if (htmlElement) {
htmlElement.classList.remove("light", "dark");
htmlElement.classList.add(theme);
}
}, [theme]);

return (
<ThemeContext.Provider value={contextValue}>
{children}
</ThemeContext.Provider>
);
}

export function useThemeContext() {
const context = useContext(ThemeContext);

if (!context) {
throw new Error("useThemeContext must be used within a ThemeProvider");
}

return context;
}
Loading