Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 24 additions & 41 deletions apps/marketing/src/components/CodeExample.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button } from "@usesend/ui/src/button";
import { CodeBlock } from "@usesend/ui/src/code-block";
import { CodeBlockWithCopy } from "@usesend/ui/src/code-block-with-copy";
import { LangToggle } from "./CodeLangToggle";
import { CodeExampleClient } from "./CodeExampleClient";

const TS_CODE = `import { UseSend } from "usesend-js";

Expand Down Expand Up @@ -82,8 +82,7 @@ if ($response === false) {
}
curl_close($ch);`;

export function CodeExample() {
const containerId = "code-example";
export async function CodeExample() {
const languages = [
{
key: "ts",
Expand Down Expand Up @@ -115,6 +114,19 @@ export function CodeExample() {
},
];

const panels = Object.fromEntries(
await Promise.all(
languages.map(async (l) => [
l.key,
<CodeBlockWithCopy key={l.key} code={l.code}>
<CodeBlock lang={l.shiki as any} className="p-4 rounded-[10px]">
{l.code}
</CodeBlock>
</CodeBlockWithCopy>,
])
)
);

Comment on lines +117 to +129

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd code-block.tsx packages/ui --exec cat -n {} \; 2>/dev/null | sed -n '1,20p'
rg -n "type Props" packages/ui/src/code-block.tsx -A 8

Repository: usesend/useSend

Length of output: 828


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== apps/marketing/src/components/CodeExample.tsx ==\n'
cat -n apps/marketing/src/components/CodeExample.tsx | sed -n '1,180p'

printf '\n== shiki language references in repo ==\n'
rg -n '"typescript"|"python"|"go"|"php"|BundledLanguage' . -g '!**/node_modules/**' -g '!**/.next/**' -g '!**/dist/**' -g '!**/build/**' | sed -n '1,120p'

Repository: usesend/useSend

Length of output: 7623


Remove the as any cast on lang. CodeBlock already accepts BundledLanguage | "text", so this only weakens type safety and can hide future language mismatches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/marketing/src/components/CodeExample.tsx` around lines 117 - 129, Remove
the `as any` cast from the `lang` prop in the `CodeBlock` usage within the
`panels` mapping, passing `l.shiki` directly. Preserve the existing language
values and rendering behavior while relying on `CodeBlock`’s `BundledLanguage |
"text"` type validation.

return (
<section className="py-16 sm:py-20">
<div className="mx-auto max-w-6xl px-6">
Expand All @@ -128,44 +140,15 @@ export function CodeExample() {
</p>
</div>

<div className="mt-8 overflow-hidden" id={containerId}>
<div className="flex items-center gap-2 justify-center py-2 text-xs text-muted-foreground mb-4">
<LangToggle
containerId={containerId}
defaultLang="ts"
languages={languages.map(({ key, label, kind }) => ({
key,
label,
kind,
}))}
/>
</div>
<div className="rounded-[18px] bg-primary/20 p-1">
<div className="rounded-[14px] bg-primary/20 p-0.5 shadow-sm">
<div className="bg-background rounded-xl overflow-hidden">
{languages.map((l, idx) => (
<div
key={l.key}
data-lang-slot={l.key}
className={idx === 0 ? "block" : "hidden"}
>
{/* Cast to any to align with shiki BundledLanguage without importing types here */}
<CodeBlockWithCopy code={l.code}>
<CodeBlock
lang={l.shiki as any}
className="p-4 rounded-[10px]"
>
{l.code}
</CodeBlock>
</CodeBlockWithCopy>
</div>
))}
</div>
</div>
</div>
<div className="sr-only" aria-live="polite">
Language example toggled
</div>
<div className="mt-8 overflow-hidden">
<CodeExampleClient
languages={languages.map(({ key, label, kind }) => ({
key,
label,
kind,
}))}
panels={panels}
/>
</div>

<div className="mt-6 flex items-center justify-center gap-3">
Expand Down
70 changes: 70 additions & 0 deletions apps/marketing/src/components/CodeExampleClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use client";

import { useLayoutEffect, useRef, useState, type ReactNode } from "react";
import { LangToggle } from "./CodeLangToggle";

type LangItem = {
key: string;
label: string;
kind: string;
};

export function CodeExampleClient({
languages,
panels,
}: {
languages: LangItem[];
panels: Record<string, ReactNode>;
}) {
const [activeLang, setActiveLang] = useState(languages[0]?.key ?? "ts");
const contentRef = useRef<HTMLDivElement>(null);
const [height, setHeight] = useState<number | undefined>();

useLayoutEffect(() => {
const el = contentRef.current;
if (!el) return;

const measure = () => setHeight(el.scrollHeight);
measure();

const observer = new ResizeObserver(measure);
observer.observe(el);
return () => observer.disconnect();
}, [activeLang]);

return (
<>
<div className="flex items-center gap-2 justify-center py-2 text-xs text-muted-foreground mb-4">
<LangToggle
active={activeLang}
onActiveChange={setActiveLang}
languages={languages}
/>
</div>
<div className="rounded-[18px] bg-primary/20 p-1">
<div className="rounded-[14px] bg-primary/20 p-0.5 shadow-sm">
<div
className="bg-background rounded-xl overflow-hidden transition-[height] duration-300 ease-in-out"
style={height !== undefined ? { height: `${height}px` } : undefined}
>
<div ref={contentRef}>
{languages.map((l) => (
<div
key={l.key}
className={activeLang === l.key ? "block" : "hidden"}
>
{panels[l.key]}
</div>
))}
</div>
</div>
</div>
</div>
<div className="sr-only" aria-live="polite">
Language example toggled
</div>
Comment on lines +63 to +65

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Static aria-live text never triggers a screen-reader announcement.

Screen readers announce live regions only when their content changes. This text is hardcoded and identical on every render, so switching languages never actually announces anything to assistive tech, despite that being the intent of this region.

🔊 Proposed fix
+  const activeLabel =
+    languages.find((l) => l.key === activeLang)?.label ?? activeLang;
+
   return (
     <>
       ...
       <div className="sr-only" aria-live="polite">
-        Language example toggled
+        Showing {activeLabel} example
       </div>
     </>
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="sr-only" aria-live="polite">
Language example toggled
</div>
const activeLabel =
languages.find((l) => l.key === activeLang)?.label ?? activeLang;
return (
<>
<div className="sr-only" aria-live="polite">
Showing {activeLabel} example
</div>
</>
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/marketing/src/components/CodeExampleClient.tsx` around lines 63 - 65,
Update the language-toggle announcement in CodeExampleClient so the aria-live
region’s text changes whenever the selected language changes, rather than
rendering the static “Language example toggled” text. Reuse the existing
language state or toggle handler to include the current language in the
announcement while preserving the sr-only and polite live-region behavior.

</>
);
}

export default CodeExampleClient;
33 changes: 6 additions & 27 deletions apps/marketing/src/components/CodeLangToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useEffect, useState } from "react";
import { useState } from "react";
import Image from "next/image";
import { Button } from "@usesend/ui/src/button";

Expand All @@ -11,35 +11,14 @@ type LangItem = {
};

export function LangToggle({
containerId,
languages,
defaultLang,
active,
onActiveChange,
}: {
containerId: string;
languages: LangItem[];
defaultLang: string;
active: string;
onActiveChange: (key: string) => void;
}) {
const [active, setActive] = useState(defaultLang);

useEffect(() => {
const container = document.getElementById(containerId);
if (!container) return;

const slots = Array.from(
container.querySelectorAll<HTMLElement>("[data-lang-slot]")
);
for (const el of slots) {
const key = el.getAttribute("data-lang-slot");
if (key === active) {
el.classList.remove("hidden");
el.classList.add("block");
} else {
el.classList.add("hidden");
el.classList.remove("block");
}
}
}, [active, containerId]);

return (
<div className="flex items-center gap-2 justify-center">
{languages.map((l) => (
Expand All @@ -54,7 +33,7 @@ export function LangToggle({
: "border-input")
}
aria-pressed={active === l.key}
onClick={() => setActive(l.key)}
onClick={() => onActiveChange(l.key)}
>
<span className="inline-flex items-center">
<LangIcon kind={l.kind} className="h-4 w-4 mr-1" /> {l.label}
Expand Down