Fix case-study share chrome, hero lockup width, and copy confirmation in the share dialog.

Marketing pages omitted create-flow nav copy, so Share rendered as a translation key; the case-study banner squeezed hero text to the thumbnail width; Copy link had no hover or copied state.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-09-09 22:00:19 -06:00
co-authored by Cursor
parent ddf7b0ef7a
commit ce581a42c8
17 changed files with 225 additions and 52 deletions
@@ -3,8 +3,9 @@
/**
* Figma: Community Rule System — "Modal / Share"
* https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=22073-30884
* Copy-link hover / Copied! : 23769:27494 / 23769:27443
*/
import { memo, useId, useRef } from "react";
import { memo, useCallback, useEffect, useId, useRef, useState } from "react";
import { useTranslation } from "../../../contexts/MessagesContext";
import { useCreateModalA11y } from "../Create/useCreateModalA11y";
import { ShareView } from "./Share.view";
@@ -15,9 +16,27 @@ const ShareContainer = memo<ShareProps>((props) => {
const overlayRef = useRef<HTMLDivElement>(null);
const titleId = useId();
const t = useTranslation("modals.share");
const [linkCopied, setLinkCopied] = useState(false);
useCreateModalA11y(props.isOpen, props.onClose, dialogRef);
useEffect(() => {
if (!props.isOpen) {
setLinkCopied(false);
}
}, [props.isOpen]);
const onCopyLinkClick = useCallback(async () => {
try {
const result = await props.onCopyLink();
if (result !== false) {
setLinkCopied(true);
}
} catch {
setLinkCopied(false);
}
}, [props.onCopyLink]);
return (
<ShareView
{...props}
@@ -27,6 +46,10 @@ const ShareContainer = memo<ShareProps>((props) => {
title={t("title")}
description={t("description")}
copyLinkLabel={t("copyLink")}
copiedLabel={t("copied")}
copiedLive={t("copiedLive")}
linkCopied={linkCopied}
onCopyLinkClick={onCopyLinkClick}
signalLabel={t("signal")}
slackLabel={t("slack")}
discordLabel={t("discord")}
+7 -1
View File
@@ -4,7 +4,8 @@ import type { CreateModalBackdropVariant } from "../Create/CreateModalFrame.view
export type ShareProps = {
isOpen: boolean;
onClose: () => void;
onCopyLink: () => void | Promise<void>;
/** Return `false` when the clipboard write did not succeed. */
onCopyLink: () => void | boolean | Promise<void | boolean>;
onEmailShare: () => void;
onSignalShare: () => void | Promise<void>;
onSlackShare: () => void | Promise<void>;
@@ -20,6 +21,10 @@ export type ShareViewProps = ShareProps & {
title: string;
description: string;
copyLinkLabel: string;
copiedLabel: string;
copiedLive: string;
linkCopied: boolean;
onCopyLinkClick: () => void | Promise<void>;
signalLabel: string;
slackLabel: string;
discordLabel: string;
@@ -34,4 +39,5 @@ export type ShareChannelTileProps = {
onClick: () => void | Promise<void>;
circleClassName: string;
icon: ReactNode;
copied?: boolean;
};
+43 -13
View File
@@ -34,19 +34,32 @@ function ShareAssetIcon(props: {
);
}
function ShareChannelTile({ label, onClick, circleClassName, icon }: ShareChannelTileProps) {
function ShareChannelTile({
label,
onClick,
circleClassName,
icon,
copied = false,
}: ShareChannelTileProps) {
return (
<button
type="button"
onClick={() => void onClick()}
className="flex w-16 shrink-0 flex-col items-center gap-2 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-surface-default-primary)]"
aria-pressed={copied || undefined}
className="group flex w-16 shrink-0 flex-col items-center gap-2 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-surface-default-primary)]"
>
<div
className={`flex h-[60px] w-[60px] items-center justify-center rounded-full border border-solid ${circleClassName}`}
className={`flex h-[60px] w-[60px] items-center justify-center rounded-full border border-solid transition-[transform,background-color,border-color,filter] duration-150 ease-out group-active:scale-90 ${circleClassName}`}
>
{icon}
</div>
<span className="max-w-[4.5rem] text-center text-x-small-label text-[var(--color-content-default-tertiary)]">
<span
className={`max-w-[4.5rem] text-center text-x-small-label ${
copied
? "text-[var(--color-border-default-positive-primary)]"
: "text-[var(--color-content-default-tertiary)]"
}`}
>
{label}
</span>
</button>
@@ -56,7 +69,6 @@ function ShareChannelTile({ label, onClick, circleClassName, icon }: ShareChanne
export const ShareView = memo(function ShareView({
isOpen,
onClose,
onCopyLink,
onEmailShare,
onSignalShare,
onSlackShare,
@@ -69,6 +81,10 @@ export const ShareView = memo(function ShareView({
title,
description,
copyLinkLabel,
copiedLabel,
copiedLive,
linkCopied,
onCopyLinkClick,
signalLabel,
slackLabel,
discordLabel,
@@ -109,36 +125,50 @@ export const ShareView = memo(function ShareView({
{/* Channel circle hexes are third-party brand colors (copy/link, Signal, Slack, Discord), not DS tokens. */}
<div className="flex flex-wrap gap-4">
<ShareChannelTile
label={copyLinkLabel}
onClick={onCopyLink}
circleClassName="border-[#444444] bg-[#333333]"
icon={<ShareAssetIcon name="link" width={24} height={24} />}
label={linkCopied ? copiedLabel : copyLinkLabel}
onClick={onCopyLinkClick}
copied={linkCopied}
circleClassName={
linkCopied
? "border-2 border-[#444444] bg-[#333333]"
: "border-[#444444] bg-[#333333] group-hover:border-2 group-hover:border-[var(--color-border-default-positive-primary)] group-hover:bg-[#444444]"
}
icon={
<ShareAssetIcon
name={linkCopied ? "check" : "link"}
width={24}
height={24}
/>
}
/>
<ShareChannelTile
label={signalLabel}
onClick={onSignalShare}
circleClassName="border-[#3a76f0] bg-[#3a76f0]"
circleClassName="border-[#3a76f0] bg-[#3a76f0] group-hover:brightness-110"
icon={<ShareAssetIcon name="signal" width={26} height={26} />}
/>
<ShareChannelTile
label={slackLabel}
onClick={onSlackShare}
circleClassName="border-[#4a154b] bg-[#4a154b]"
circleClassName="border-[#4a154b] bg-[#4a154b] group-hover:brightness-110"
icon={<ShareAssetIcon name="slack" width={26} height={26} />}
/>
<ShareChannelTile
label={discordLabel}
onClick={onDiscordShare}
circleClassName="border-[#5865f2] bg-[#5865f2]"
circleClassName="border-[#5865f2] bg-[#5865f2] group-hover:brightness-110"
icon={<ShareAssetIcon name="discord" width={30} height={30} />}
/>
<ShareChannelTile
label={emailLabel}
onClick={onEmailShare}
circleClassName="border-[var(--color-surface-default-brand-kiwi)] bg-[var(--color-surface-default-brand-kiwi)]"
circleClassName="border-[var(--color-surface-default-brand-kiwi)] bg-[var(--color-surface-default-brand-kiwi)] group-hover:brightness-110"
icon={<ShareAssetIcon name="mail" width={24} height={24} />}
/>
</div>
<p className="sr-only" aria-live="polite">
{linkCopied ? copiedLive : ""}
</p>
</div>
<ModalFooter