From 56ca09692a308b22455420d11376ff8d41244c47 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Sun, 26 Apr 2026 17:31:28 +0200 Subject: refactor: thinner hooks --- apps/web/src/app/brand/page.tsx | 536 ++++++++++++++++++++-------------------- 1 file changed, 268 insertions(+), 268 deletions(-) (limited to 'apps/web/src/app') diff --git a/apps/web/src/app/brand/page.tsx b/apps/web/src/app/brand/page.tsx index a7085154..94e3f3e1 100644 --- a/apps/web/src/app/brand/page.tsx +++ b/apps/web/src/app/brand/page.tsx @@ -1,268 +1,268 @@ -"use client"; - -import type { CSSProperties } from "react"; -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 BrandPage() { - return ( - - Download OpenCut brand assets for use in your projects.{" "} - - document - .getElementById("guidelines") - ?.scrollIntoView({ behavior: "smooth" }) - } - > - Read the brand guidelines. - - - } - action={ - - } - > -
- {ASSET_SECTIONS.map((section) => ( -
-
-

{section.title}

-

- {section.description} -

-
-
- {section.assets.map((variant) => ( - - ))} -
-
- ))} -
- - - -
-
-

Usage

-

- 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{" "} - - brand@opencut.app - - . -

-
- -
-

What's not allowed

-
    - {[ - "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) => ( -
  • - - - {item} -
  • - ))} -
-
-
-
- ); -} - -const CHECKER_STYLES: Record<"dark" | "light", CSSProperties> = { - light: { - backgroundImage: - "linear-gradient(45deg, #292929 25%, transparent 25%), linear-gradient(-45deg, #292929 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #292929 75%), linear-gradient(-45deg, transparent 75%, #292929 75%)", - backgroundSize: "18px 18px", - backgroundPosition: "0 0, 0 9px, 9px -9px, -9px 0px", - backgroundColor: "#000", - }, - dark: { - backgroundImage: - "linear-gradient(45deg, #e0e0e0 25%, transparent 25%), linear-gradient(-45deg, #e0e0e0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #e0e0e0 75%), linear-gradient(-45deg, transparent 75%, #e0e0e0 75%)", - backgroundSize: "18px 18px", - backgroundPosition: "0 0, 0 9px, 9px -9px, -9px 0px", - backgroundColor: "#f5f5f5", - }, -}; - -function AssetCard({ variant }: { variant: AssetVariant }) { - const [copied, setCopied] = useState(false); - - async function handleCopy() { - await copyAsset(variant.src); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - } - - return ( - -
- {variant.label} -
- - -
- ); -} +"use client"; + +import type { CSSProperties } from "react"; +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 BrandPage() { + return ( + + Download OpenCut brand assets for use in your projects.{" "} + + document + .getElementById("guidelines") + ?.scrollIntoView({ behavior: "smooth" }) + } + > + Read the brand guidelines. + + + } + action={ + + } + > +
+ {ASSET_SECTIONS.map((section) => ( +
+
+

{section.title}

+

+ {section.description} +

+
+
+ {section.assets.map((variant) => ( + + ))} +
+
+ ))} +
+ + + +
+
+

Usage

+

+ 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{" "} + + brand@opencut.app + + . +

+
+ +
+

What's not allowed

+
    + {[ + "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) => ( +
  • + - + {item} +
  • + ))} +
+
+
+
+ ); +} + +const CHECKER_STYLES: Record<"dark" | "light", CSSProperties> = { + light: { + backgroundImage: + "linear-gradient(45deg, #292929 25%, transparent 25%), linear-gradient(-45deg, #292929 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #292929 75%), linear-gradient(-45deg, transparent 75%, #292929 75%)", + backgroundSize: "18px 18px", + backgroundPosition: "0 0, 0 9px, 9px -9px, -9px 0px", + backgroundColor: "#000", + }, + dark: { + backgroundImage: + "linear-gradient(45deg, #e0e0e0 25%, transparent 25%), linear-gradient(-45deg, #e0e0e0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #e0e0e0 75%), linear-gradient(-45deg, transparent 75%, #e0e0e0 75%)", + backgroundSize: "18px 18px", + backgroundPosition: "0 0, 0 9px, 9px -9px, -9px 0px", + backgroundColor: "#f5f5f5", + }, +}; + +function AssetCard({ variant }: { variant: AssetVariant }) { + const [copied, setCopied] = useState(false); + + async function handleCopy() { + await copyAsset(variant.src); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } + + return ( + +
+ {variant.label} +
+ + +
+ ); +} -- cgit v1.3.1