
How to use Framer AI to make a right click to download logo menu
Jun 5, 2025
Realized that we hadn’t added back the right click to download menu to the Basedash website since our rebrand and relaunch earlier this year.
Saw this post by Felix from Lovable and decided to spend about 10 minutes in Framer seeing if I could make it happen.
Worked out pretty well, not that hard to do TBH.
Here’s the video of me making over some smooth smooth jazz:
Not too hard, pretty happy with the results.
Here’s the component that I ended up with if you want to use it yourself:
// Right-click to download header with dark mode menu and color props for menu background, text, and hover import { useState, useRef, useEffect, startTransition, type CSSProperties } from "react" import { addPropertyControls, ControlType, RenderTarget } from "framer" const defaultSVG = "https://framerusercontent.com/images/GfGkADagM4KEibNcIiRUWlfrR0.svg" const defaultPNG = "https://framerusercontent.com/images/GfGkADagM4KEibNcIiRUWlfrR0.png" const defaultFull = "https://framerusercontent.com/images/aNsAT3jCvt4zglbWCUoFe33Q.png" interface DownloadHeaderProps { darkBrandFile: string lightBrandFile: string iconFile: string fullBrandFile: string brandPackFile: string menuOptions: Array<{ label: string; type: string; icon?: string }> backgroundColor: string menuBgColor: string menuTextColor: string menuHoverBgColor: string style?: CSSProperties } /** * Right-click Download Header (Dark Menu) * * @framerIntrinsicWidth 600 * @framerIntrinsicHeight 120 * * @framerSupportedLayoutWidth any-prefer-fixed * @framerSupportedLayoutHeight fixed */ export default function DownloadHeader(props: DownloadHeaderProps) { const { darkBrandFile = defaultSVG, lightBrandFile = defaultPNG, iconFile = defaultFull, fullBrandFile = defaultFull, brandPackFile = "https://framerusercontent.com/assets/brand-pack.zip", menuOptions = [ { label: "Download dark brand mark", type: "dark" }, { label: "Download light brand mark", type: "light" }, { label: "Download icon", type: "icon" }, { label: "Download full brand mark", type: "full" }, { label: "Download brand pack (zip)", type: "pack" }, ], backgroundColor, menuBgColor, menuTextColor, menuHoverBgColor, style, } = props const [menuOpen, setMenuOpen] = useState(false) const [menuPos, setMenuPos] = useState({ x: 0, y: 0 }) const containerRef = useRef<htmldivelement>(null) useEffect(() => { function handleClick(e: MouseEvent) { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { startTransition(() => setMenuOpen(false)) } } if (menuOpen) { window.addEventListener("mousedown", handleClick) } return () => window.removeEventListener("mousedown", handleClick) }, [menuOpen]) function handleContextMenu(e) { e.preventDefault() startTransition(() => { setMenuOpen(true) setMenuPos({ x: e.clientX, y: e.clientY }) }) } function handleMenuClick(type: string) { let url = "" let filename = "" if (type === "dark") { url = darkBrandFile filename = "brand-dark.svg" } else if (type === "light") { url = lightBrandFile filename = "brand-light.svg" } else if (type === "icon") { url = iconFile filename = "brand-icon.svg" } else if (type === "full") { url = fullBrandFile filename = "brand-full.svg" } else if (type === "pack") { url = brandPackFile filename = "brand-pack.zip" } if (url) { const link = document.createElement("a") link.href = url link.download = filename document.body.appendChild(link) link.click() document.body.removeChild(link) } startTransition(() => setMenuOpen(false)) } // Show fullBrandFile as background image const headerStyle: CSSProperties = { ...style, width: "100%", height: "100%", backgroundColor: backgroundColor, backgroundImage: `url(${fullBrandFile})`, backgroundRepeat: "no-repeat", backgroundPosition: "center center", backgroundSize: "auto 80%", borderRadius: 8, boxShadow: "0 2px 8px rgba(0,0,0,0.06)", display: "flex", alignItems: "center", position: "relative", userSelect: "none", padding: "0 8%", fontSize: 28, fontWeight: 700, color: "#222", overflow: "visible", } return ( <div ref="{containerRef}" style="{headerStyle}" oncontextmenu="{handleContextMenu}" tabindex="{0}" aria-label="Header with download menu"> {menuOpen && RenderTarget.current() !== RenderTarget.thumbnail && ( <div style="{{" position:="" "fixed",="" top:="" menupos.y,="" left:="" menupos.x,="" background:="" menubgcolor,="" borderradius:="" 8,="" boxshadow:="" "0="" 4px="" 16px="" rgba(0,0,0,0.32)",="" minwidth:="" 220,="" zindex:="" 9999,="" padding:="" display:="" "flex",="" flexdirection:="" "column",="" gap:="" 2,="" }}="" role="menu"> {menuOptions.map((opt, i) => ( <menubutton key="{opt.type" +="" i}="" label="{opt.label}" icon="{opt.icon}" onclick="{()" ==""> handleMenuClick(opt.type)} textColor={menuTextColor} hoverBg={menuHoverBgColor} /> ))} </menubutton></div> )} </div> ) } function MenuButton({ label, icon, onClick, textColor, hoverBg }) { const [hover, setHover] = useState(false) return ( <button style="{{" background:="" hover="" ?="" hoverbg="" :="" "none",="" border:="" textalign:="" "left",="" padding:="" "10px="" 16px",="" borderradius:="" 6,="" fontsize:="" 16,="" color:="" textcolor,="" cursor:="" "pointer",="" transition:="" "background="" 0.05s",="" outline:="" }}="" onmouseenter="{()" ==""> setHover(true)} onMouseLeave={() => setHover(false)} onClick={onClick} role="menuitem" > {icon && ( <img src="{icon}" alt="" style="{{" width:="" 20,="" height:="" marginright:="" 10,="" verticalalign:="" "middle"="" }}=""> )} {label} </button> ) } addPropertyControls(DownloadHeader, { darkBrandFile: { type: ControlType.File, allowedFileTypes: ["svg", "png"], title: "Dark Brand Mark", }, lightBrandFile: { type: ControlType.File, allowedFileTypes: ["svg", "png"], title: "Light Brand Mark", }, iconFile: { type: ControlType.File, allowedFileTypes: ["svg", "png"], title: "Icon", }, fullBrandFile: { type: ControlType.File, allowedFileTypes: ["svg", "png"], title: "Full Brand Mark", }, brandPackFile: { type: ControlType.File, allowedFileTypes: ["zip"], title: "Brand Pack (zip)", }, menuOptions: { type: ControlType.Array, title: "Menu Options", control: { type: ControlType.Object, controls: { label: { type: ControlType.String, defaultValue: "Download dark brand mark" }, type: { type: ControlType.Enum, options: ["dark", "light", "icon", "full", "pack"], optionTitles: ["Dark Brand", "Light Brand", "Icon", "Full Brand", "Brand Pack (zip)"], defaultValue: "dark", }, icon: { type: ControlType.File, allowedFileTypes: ["svg", "png", "jpg", "jpeg", "webp"], title: "Icon", }, }, }, defaultValue: [ { label: "Download dark brand mark", type: "dark" }, { label: "Download light brand mark", type: "light" }, { label: "Download icon", type: "icon" }, { label: "Download full brand mark", type: "full" }, { label: "Download brand pack (zip)", type: "pack" }, ], maxCount: 5, }, backgroundColor: { type: ControlType.Color, title: "Background", defaultValue: "#FFFFFF", }, menuBgColor: { type: ControlType.Color, title: "Menu BG", defaultValue: "#181A1B", }, menuTextColor: { type: ControlType.Color, title: "Menu Text", defaultValue: "#F5F5F5", }, menuHoverBgColor: { type: ControlType.Color, title: "Menu Hover BG", defaultValue: "#23272A", }, })</htmldivelement>
© 2025 BaseDash Inc.
© 2025 BaseDash Inc.

© 2025 BaseDash Inc.