chore: Update responsive margins and styles in Features component
This commit is contained in:
13
src/app/create-theme/page.tsx
Normal file
13
src/app/create-theme/page.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import CreateThemePage from "@/components/create-theme";
|
||||||
|
import Footer from "@/components/footer";
|
||||||
|
import { Navigation } from "@/components/navigation";
|
||||||
|
|
||||||
|
export default function BrandingAssetsPage() {
|
||||||
|
return (
|
||||||
|
<main className="flex min-h-screen flex-col items-center justify-start">
|
||||||
|
<CreateThemePage />
|
||||||
|
<Footer />
|
||||||
|
<Navigation /> {/* At the bottom of the page */}
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
179
src/components/create-theme.tsx
Normal file
179
src/components/create-theme.tsx
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ny } from "@/lib/utils";
|
||||||
|
import { Button } from "./ui/button";
|
||||||
|
import React from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { Dialog, DialogContent, DialogDescription, DialogTitle, DialogTrigger } from "@radix-ui/react-dialog";
|
||||||
|
import { DialogFooter, DialogHeader } from "./ui/dialog";
|
||||||
|
|
||||||
|
const COLORS = [
|
||||||
|
"#ffaa40",
|
||||||
|
"#9c40ff",
|
||||||
|
"#ff40aa",
|
||||||
|
"#40ffaa",
|
||||||
|
"#40aaff",
|
||||||
|
];
|
||||||
|
|
||||||
|
const ThemeFormWrapper = styled.div<{
|
||||||
|
primaryColor: string,
|
||||||
|
accentColor: string,
|
||||||
|
secondaryColor: string,
|
||||||
|
tertiaryColor: string,
|
||||||
|
colorsBorder: string,
|
||||||
|
}>`
|
||||||
|
${({
|
||||||
|
primaryColor,
|
||||||
|
accentColor,
|
||||||
|
secondaryColor,
|
||||||
|
tertiaryColor,
|
||||||
|
colorsBorder,
|
||||||
|
}: {
|
||||||
|
primaryColor: string;
|
||||||
|
accentColor: string;
|
||||||
|
secondaryColor: string;
|
||||||
|
tertiaryColor: string;
|
||||||
|
colorsBorder: string;
|
||||||
|
}) => `
|
||||||
|
--zen-primary-color: ${accentColor};
|
||||||
|
|
||||||
|
--zen-colors-primary: ${primaryColor};
|
||||||
|
--zen-colors-secondary: ${secondaryColor};
|
||||||
|
--zen-colors-tertiary: ${tertiaryColor};
|
||||||
|
|
||||||
|
--zen-colors-border: ${colorsBorder};
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const defaultStyles = {
|
||||||
|
primaryColor: {
|
||||||
|
light: "color-mix(in srgb, var(--zen-primary-color) 50%, black 50%)",
|
||||||
|
dark: "color-mix(in srgb, var(--zen-primary-color) 50%, black 50%)",
|
||||||
|
},
|
||||||
|
secondaryColor: {
|
||||||
|
light: "color-mix(in srgb, var(--zen-primary-color) 40%, white 60%)",
|
||||||
|
dark: "color-mix(in srgb, var(--zen-primary-color) 40%, black 60%)",
|
||||||
|
},
|
||||||
|
tertiaryColor: {
|
||||||
|
light: "color-mix(in srgb, var(--zen-primary-color) 7%, white 93%)",
|
||||||
|
dark: "color-mix(in srgb, var(--zen-primary-color) 15%, black 85%)",
|
||||||
|
},
|
||||||
|
colorsBorder: {
|
||||||
|
light: "color-mix(in srgb, var(--zen-colors-secondary) 90%, black 10%)",
|
||||||
|
dark: "color-mix(in srgb, var(--zen-colors-secondary) 80%, black 20%)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CreateThemePage() {
|
||||||
|
const [selectedColor, setSelectedColor] = React.useState(COLORS[0]);
|
||||||
|
const [isDarkMode, setIsDarkMode] = React.useState(false);
|
||||||
|
|
||||||
|
const [primaryColor, setPrimaryColor] = React.useState(defaultStyles.primaryColor.dark);
|
||||||
|
const [secondaryColor, setSecondaryColor] = React.useState(defaultStyles.secondaryColor.dark);
|
||||||
|
const [tertiaryColor, setTertiaryColor] = React.useState(defaultStyles.tertiaryColor.dark);
|
||||||
|
const [colorsBorder, setColorsBorder] = React.useState(defaultStyles.colorsBorder.dark);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setPrimaryColor(isDarkMode ? defaultStyles.primaryColor.dark : defaultStyles.primaryColor.light);
|
||||||
|
setSecondaryColor(isDarkMode ? defaultStyles.secondaryColor.dark : defaultStyles.secondaryColor.light);
|
||||||
|
setTertiaryColor(isDarkMode ? defaultStyles.tertiaryColor.dark : defaultStyles.tertiaryColor.light);
|
||||||
|
setColorsBorder(isDarkMode ? defaultStyles.colorsBorder.dark : defaultStyles.colorsBorder.light);
|
||||||
|
}, [isDarkMode]);
|
||||||
|
|
||||||
|
const generateThemeData = () => {
|
||||||
|
return JSON.stringify({
|
||||||
|
primaryColor,
|
||||||
|
secondaryColor,
|
||||||
|
tertiaryColor,
|
||||||
|
colorsBorder,
|
||||||
|
}, null, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeFormWrapper
|
||||||
|
primaryColor={primaryColor}
|
||||||
|
accentColor={selectedColor}
|
||||||
|
secondaryColor={secondaryColor}
|
||||||
|
tertiaryColor={tertiaryColor}
|
||||||
|
colorsBorder={colorsBorder}
|
||||||
|
className="flex flex-col mt-52 items-center justify-center w-full min-h-screen">
|
||||||
|
<div className="w-full lg:w-1/2 xl:w-1/2 mx-auto px-2 lg:px-none">
|
||||||
|
<h1 className="text-4xl lg:text-7xl font-bold">Create your theme</h1>
|
||||||
|
<p className="text-lg opacity-40 mt-2">Create your own theme for Zen Browser and share it with the community.</p>
|
||||||
|
<div className="flex items-center mt-8">
|
||||||
|
{COLORS.map((color) => (
|
||||||
|
<div
|
||||||
|
key={color}
|
||||||
|
onClick={() => setSelectedColor(color)}
|
||||||
|
className={ny(`w-6 h-6 mx-2 cursor-pointer rounded-md shadow-sm text-white`, selectedColor === color ? "ring-2 ring-black dark:ring-white" : "")}
|
||||||
|
style={{ backgroundColor: color }}
|
||||||
|
></div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col lg:flex-row">
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="flex items-center mt-10 select-none">
|
||||||
|
<input type="checkbox" className="mr-2" checked={isDarkMode} onChange={(e) => setIsDarkMode(e.target.checked)} id="dark-mode" />
|
||||||
|
<label htmlFor="dark-mode" className="text-md font-bold opacity-60">Dark mode</label>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-lg mt-8 font-bold opacity-70">
|
||||||
|
Primary color
|
||||||
|
</h2>
|
||||||
|
<div className="flex items-center mt-2">
|
||||||
|
<input type="text" className="border text-gray-500 rounded-lg p-2 w-2/3" value={primaryColor} onChange={(e) => setPrimaryColor(e.target.value)} />
|
||||||
|
<div className="w-11 h-11 ml-4 rounded-lg border bg-[var(--zen-colors-primary)]"></div>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-lg mt-8 font-bold opacity-70">
|
||||||
|
Secondary color
|
||||||
|
</h2>
|
||||||
|
<div className="flex items-center mt-2">
|
||||||
|
<input type="text" className="border text-gray-500 rounded-lg p-2 w-2/3" value={secondaryColor} onChange={(e) => setSecondaryColor(e.target.value)} />
|
||||||
|
<div className="w-11 h-11 ml-4 rounded-lg border bg-[var(--zen-colors-secondary)]"></div>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-lg mt-8 font-bold opacity-70">
|
||||||
|
Tertiary color
|
||||||
|
</h2>
|
||||||
|
<div className="flex items-center mt-2">
|
||||||
|
<input type="text" className="border text-gray-500 rounded-lg p-2 w-2/3" value={tertiaryColor} onChange={(e) => setTertiaryColor(e.target.value)} />
|
||||||
|
<div className="w-11 h-11 ml-4 rounded-lg border bg-[var(--zen-colors-tertiary)]"></div>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-lg mt-8 font-bold opacity-70">
|
||||||
|
Border color
|
||||||
|
</h2>
|
||||||
|
<div className="flex items-center mt-2">
|
||||||
|
<input type="text" className="border text-gray-500 rounded-lg p-2 w-2/3" value={colorsBorder} onChange={(e) => setColorsBorder(e.target.value)} />
|
||||||
|
<div className="w-11 h-11 ml-4 rounded-lg border bg-[var(--zen-colors-border)]"></div>
|
||||||
|
</div>
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button className="mt-8">Create theme</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader className="text-xl font-bold">
|
||||||
|
<DialogTitle>Theme data</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Copy the following JSON object and paste it into your Zen Browser theme format.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<pre className="text-sm text-wrap font-mono p-4 bg-gray-100 dark:bg-gray-800 rounded-lg">{generateThemeData()}</pre>
|
||||||
|
<DialogFooter className="mt-4">
|
||||||
|
<Button onClick={() =>
|
||||||
|
navigator.clipboard.writeText(generateThemeData())
|
||||||
|
}>
|
||||||
|
Copy to clipboard
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
{/* Preview */}
|
||||||
|
<div className="p-4 pr-0 pb-0 rounded-xl border-2 relative overflow-hidden w-72 h-48 border-[var(--zen-colors-border)] bg-[var(--zen-colors-tertiary)]">
|
||||||
|
<div className="border-2 flex items-center justify-center border-[var(--zen-colors-border)] h-full w-full bg-[var(--zen-colors-tertiary)] rounded-tl-xl p-4 border-b-0 border-r-0">
|
||||||
|
<Button className={ny("bg-[var(--zen-colors-secondary)]", isDarkMode ? "text-white" : "text-black")}>Button</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ThemeFormWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -14,19 +14,14 @@ export default function MarketplacePage() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-full mx-auto p-5 lg:w-2/3 2xl:w-1/2 items-center justify-center h-full mt-36">
|
<div className="flex flex-col w-full mx-auto items-center justify-center h-full">
|
||||||
<div className="mx-auto w-full text-center">
|
<div className="mx-auto w-full text-center border-b pt-48 pb-24 mb-12 bg-gradient-to-r from-[#f6cfbe] to-[#b9dcf2] dark:from-[#0d1117] dark:to-[#0d1117]">
|
||||||
<h1 className="text-4xl lg:text-7xl font-bold">Themes Store</h1>
|
<div className="w-full lg:w-1/2 xl:w-1/2 mx-auto px-2 lg:px-none">
|
||||||
<ThemesSearch input={searchInput} setInput={setSearchInput} />
|
<h1 className="text-4xl lg:text-7xl font-bold">Themes Store</h1>
|
||||||
<div className="w-full mt-4 flex items-start">
|
<ThemesSearch input={searchInput} setInput={setSearchInput} />
|
||||||
<Button
|
|
||||||
onClick={() => window.open("https://docs.zen-browser.app/themes-store/themes-marketplace-submission-guidelines#themes-store-submission-guidelines", "_blank")}
|
|
||||||
className="text-muted"
|
|
||||||
>Submit a theme</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<hr className="w-full border-muted mt-4" />
|
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-8 mt-10 w-full">
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-8 mt-10 w-full lg:w-1/2 xl:w-2/3 2xl:w-3/4">
|
||||||
{getThemesFromSearch(themes, searchInput).map((theme) => (
|
{getThemesFromSearch(themes, searchInput).map((theme) => (
|
||||||
<ThemeCard key={theme.name} theme={theme} />
|
<ThemeCard key={theme.name} theme={theme} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function ThemeCard({
|
|||||||
<ThemeCardWrapepr onClick={(event) => {
|
<ThemeCardWrapepr onClick={(event) => {
|
||||||
if (event.target instanceof HTMLAnchorElement) return;
|
if (event.target instanceof HTMLAnchorElement) return;
|
||||||
window.open(`/themes/${theme.id}`, "_self");
|
window.open(`/themes/${theme.id}`, "_self");
|
||||||
}} className="flex flex-col justify-start p-5 rounded-lg shadow-md bg-muted/50 border border-muted w-full hover:shadow-lg transition duration-300 ease-in-out hover:bg-muted/100 hover:border-blue-500 cursor-pointer select-none hover:border-blue-500 hover:shadow-lg">
|
}} className="flex flex-col justify-start p-5 rounded-lg shadow-sm bg-muted dark:bg-muted/50 border border-grey-900 dark:border-muted w-full hover:shadow-lg transition duration-300 ease-in-out hover:bg-muted/100 hover:border-blue-500 cursor-pointer select-none hover:border-blue-500 hover:shadow-lg">
|
||||||
<img src={theme.image} alt={theme.name} className="w-full h-32 object-cover rounded-lg border shadow" />
|
<img src={theme.image} alt={theme.name} className="w-full h-32 object-cover rounded-lg border shadow" />
|
||||||
<h2 className="text-xl font-bold mt-4 overflow-ellipsis text-start">{theme.name}</h2>
|
<h2 className="text-xl font-bold mt-4 overflow-ellipsis text-start">{theme.name}</h2>
|
||||||
<div className="flex mt-2">
|
<div className="flex mt-2">
|
||||||
|
|||||||
@@ -8,15 +8,23 @@ export default function ThemesSearch({
|
|||||||
setInput: (input: string) => void;
|
setInput: (input: string) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full p-2 bg-muted/50 rounded-lg mt-10 items-center border border-muted">
|
<div className="flex w-full p-2 bg-black/10 dark:bg-muted/50 rounded-full overflow-hidden mt-10 items-center border border-black dark:border-muted">
|
||||||
<SearchIcon className="w-6 h-6 mx-4 text-muted" />
|
<SearchIcon className="w-6 h-6 mx-4 text-black dark:text-white" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={input}
|
value={input}
|
||||||
onChange={(e) => setInput(e.target.value)}
|
onChange={(e) => setInput(e.target.value)}
|
||||||
placeholder="Search themes"
|
placeholder="Search themes"
|
||||||
className="w-full bg-transparent border-none focus:outline-none focus:border-none focus:ring-0 text-white placeholder-muted"
|
className="w-full bg-transparent border-none focus:outline-none focus:border-none focus:ring-0 dark:text-white text-black"
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={() => window.open("https://docs.zen-browser.app/themes-store/themes-marketplace-submission-guidelines#themes-store-submission-guidelines", "_blank")}
|
||||||
|
className="text-muted rounded-full rounded-r-none hidden md:block"
|
||||||
|
>Submit a theme</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => window.open("/create-theme", "_self")}
|
||||||
|
className="text-muted rounded-full rounded-l-none border-l border-black dark:border-none hidden md:block"
|
||||||
|
>Create your theme</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user