diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-04 02:56:08 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-04 02:56:08 +0100 |
| commit | 24dac1232c5fc438fadcd72889109846d97e48bc (patch) | |
| tree | 6f7972a0e3ed6e1074f1458c2dc2dac1b9ec3849 /apps/web/src/app/branding | |
| parent | 46e55674dee7061973e155375dc4f01fb4f13c00 (diff) | |
| download | opencut-24dac1232c5fc438fadcd72889109846d97e48bc.tar.gz | |
rename branding page to brand
Diffstat (limited to 'apps/web/src/app/branding')
| -rw-r--r-- | apps/web/src/app/branding/page.tsx | 243 |
1 files changed, 0 insertions, 243 deletions
diff --git a/apps/web/src/app/branding/page.tsx b/apps/web/src/app/branding/page.tsx deleted file mode 100644 index 4ebef644..00000000 --- a/apps/web/src/app/branding/page.tsx +++ /dev/null @@ -1,243 +0,0 @@ -"use client"; - -import Image from "next/image"; -import Link from "next/link"; -import { Check, Copy, Download } from "lucide-react"; -import { useState } from "react"; -import { BasePage } from "@/app/base-page"; -import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; -import { Separator } from "@/components/ui/separator"; -import { cn } from "@/utils/ui"; - -function downloadAsset(src: string) { - const filename = src.split("/").pop() ?? "asset.svg"; - const a = document.createElement("a"); - a.href = src; - a.download = filename; - a.click(); -} - -async function copyAsset(src: string) { - const res = await fetch(src); - const text = await res.text(); - await navigator.clipboard.writeText(text); -} - -const ALL_ASSETS = () => - ASSET_SECTIONS.flatMap((s) => s.assets); - -type AssetTheme = "dark" | "light" | "icon"; - -interface AssetVariant { - src: string; - theme: AssetTheme; - label: string; - width: number; - height: number; -} - -interface AssetSection { - title: string; - description: string; - cols: "1" | "2"; - assets: AssetVariant[]; -} - -const ASSET_SECTIONS: AssetSection[] = [ - { - title: "Symbol", - description: - "Use the symbol on its own when the OpenCut name is already present nearby or space is limited.", - cols: "2", - assets: [ - { - src: "/logos/opencut/symbol.svg", - theme: "dark", - label: "Symbol", - width: 400, - height: 400, - }, - { - src: "/logos/opencut/symbol-light.svg", - theme: "light", - label: "Symbol", - width: 400, - height: 400, - }, - ], - }, - { - title: "Lockup", - description: - "The full lockup combines the symbol and wordmark. Prefer this in most contexts where you have enough horizontal space.", - cols: "2", - assets: [ - { - src: "/logos/opencut/logo.svg", - theme: "dark", - label: "Logo", - width: 1809, - height: 400, - }, - { - src: "/logos/opencut/logo-light.svg", - theme: "light", - label: "Logo", - width: 1809, - height: 400, - }, - { - src: "/logos/opencut/text.svg", - theme: "dark", - label: "Text", - width: 1760, - height: 400, - }, - { - src: "/logos/opencut/text-light.svg", - theme: "light", - label: "Text", - width: 1760, - height: 400, - }, - ], - }, -]; - -export default function BrandingPage() { - return ( - <BasePage - maxWidth="6xl" - title="Brand" - description={ - <> - Download OpenCut brand assets for use in your projects.{" "} - <Link href="#guidelines" className="underline underline-offset-4"> - Read the brand guidelines. - </Link> - </> - } - action={ - <Button - variant="outline" - size="lg" - className="mx-auto gap-2" - onClick={() => { - ALL_ASSETS().forEach((asset, i) => { - setTimeout(() => downloadAsset(asset.src), i * 200); - }); - }} - > - <Download /> - Download all - </Button> - } - > - <div className="flex flex-col gap-10"> - {ASSET_SECTIONS.map((section) => ( - <div key={section.title} className="flex flex-col gap-4"> - <div className="flex flex-col gap-1"> - <h2 className="font-semibold text-lg">{section.title}</h2> - <p className="text-muted-foreground text-sm"> - {section.description} - </p> - </div> - <div - className={cn( - "grid gap-3", - section.cols === "2" - ? "grid-cols-1 sm:grid-cols-2" - : "grid-cols-1", - )} - > - {section.assets.map((variant) => ( - <AssetCard key={variant.src} variant={variant} /> - ))} - </div> - </div> - ))} - </div> - - <Separator /> - - <div id="guidelines" className="flex flex-col gap-8 text-sm"> - <div className="flex flex-col gap-3"> - <h2 className="font-semibold text-lg">Usage</h2> - <p className="text-muted-foreground text-base leading-relaxed"> - OpenCut is open source — the code is free to use under its license. - That license does not cover the name or logo. You can say you use - OpenCut, that your project integrates with OpenCut, or that it was - built on top of OpenCut. You cannot name your product OpenCut, imply - we made or endorse your product, or use the marks commercially - without asking first. For anything unclear, reach out at{" "} - <Link - href="mailto:brand@opencut.app" - className="underline underline-offset-4" - > - brand@opencut.app - </Link> - . - </p> - </div> - - <div className="flex flex-col gap-3"> - <h2 className="font-semibold text-lg">What's not allowed</h2> - <ul className="text-muted-foreground text-base flex flex-col gap-2 leading-relaxed"> - {[ - "Using OpenCut in the name of your product, service, or domain.", - "Implying that OpenCut made, sponsors, or endorses your work.", - "Using the logo or name on merchandise or commercial marketing.", - "Modifying the marks.", - ].map((item) => ( - <li key={item} className="flex gap-2"> - <span className="mt-0.5 shrink-0">-</span> - <span>{item}</span> - </li> - ))} - </ul> - </div> - </div> - </BasePage> - ); -} - -function AssetCard({ variant }: { variant: AssetVariant }) { - const [copied, setCopied] = useState(false); - - async function handleCopy() { - await copyAsset(variant.src); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - } - - return ( - <Card - className={cn( - "group relative overflow-hidden", - variant.theme === "light" ? "bg-black" : "bg-white", - )} - > - <div className="flex h-56 items-center justify-center px-12 py-8"> - <Image - src={variant.src} - alt={variant.label} - width={variant.width} - height={variant.height} - className="max-h-16 w-auto select-none object-contain" - draggable={false} - unoptimized - /> - </div> - - <Button - variant="outline" - size="icon" - className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 size-9" - onClick={handleCopy} - > - {copied ? <Check /> : <Copy />} - </Button> - </Card> - ); -} |
