Skip to content

Commit c07bc47

Browse files
add faq to website (vercel#56)
* Scaffold FAQ * Resolves vercel#4 * Update faq.tsx
1 parent 137611a commit c07bc47

File tree

6 files changed

+315
-0
lines changed

6 files changed

+315
-0
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
'use client';
2+
3+
import { CodeBlock } from '@/components/code-block';
4+
import {
5+
Accordion,
6+
AccordionContent,
7+
AccordionItem,
8+
AccordionTrigger,
9+
} from '@/components/ui/accordion';
10+
11+
const faqs = [
12+
{
13+
id: 'streamdown-difference',
14+
question: 'What makes Streamdown different from react-markdown?',
15+
answer:
16+
"Streamdown is specifically designed for AI-powered streaming applications. It includes built-in support for incomplete markdown parsing, which means it can render markdown even while it's being generated by AI models. It also includes security features like URL prefix restrictions and better performance optimizations for streaming contexts.",
17+
},
18+
{
19+
id: 'custom-components',
20+
question: 'Can I use custom components with Streamdown?',
21+
answer:
22+
'Yes! Streamdown fully supports custom components through the `components` prop, just like react-markdown. You can override any markdown element with your own React components to customize the rendering.',
23+
},
24+
{
25+
id: 'incomplete-parsing',
26+
question: 'How does the incomplete markdown parsing work?',
27+
answer:
28+
'When `parseIncompleteMarkdown` is enabled (default), Streamdown automatically detects and fixes common issues in incomplete markdown like unclosed code blocks, incomplete lists, and partial formatting. This ensures smooth rendering even as markdown is being streamed from AI models.',
29+
},
30+
{
31+
id: 'plugin-compatibility',
32+
question: 'Is Streamdown compatible with all react-markdown plugins?',
33+
answer:
34+
'Streamdown supports both remark and rehype plugins, making it compatible with most react-markdown plugins. It includes popular plugins like remarkGfm, remarkMath, and rehypeKatex by default, and you can add your own through the `remarkPlugins` and `rehypePlugins` props.',
35+
},
36+
{
37+
id: 'shiki-warning',
38+
question: (
39+
<span>
40+
Why do I get a{' '}
41+
<code className="rounded-md bg-foreground/5 px-2 py-1 font-mono text-sm tracking-tight">
42+
Package shiki can't be external
43+
</code>{' '}
44+
warning?
45+
</span>
46+
),
47+
answer: (
48+
<div>
49+
<p>
50+
This warning occurs when Next.js tries to treat Shiki as an external
51+
package. To fix this, you need to install Shiki explicitly with{' '}
52+
<code className="rounded-md bg-foreground/5 px-2 py-1 font-mono text-sm tracking-tight">
53+
npm install shiki
54+
</code>{' '}
55+
and add it to your{' '}
56+
<code className="rounded-md bg-foreground/5 px-2 py-1 font-mono text-sm tracking-tight">
57+
transpilePackages
58+
</code>{' '}
59+
array in your{' '}
60+
<code className="rounded-md bg-foreground/5 px-2 py-1 font-mono text-sm tracking-tight">
61+
next.config.ts
62+
</code>
63+
:
64+
</p>
65+
<div className="my-2">
66+
<CodeBlock
67+
className="my-4 rounded-md border p-4"
68+
code={`{
69+
// ... other config
70+
transpilePackages: ["shiki"],
71+
}`}
72+
language="tsx"
73+
/>
74+
</div>
75+
<p>This ensures Shiki is properly bundled with your application.</p>
76+
</div>
77+
),
78+
},
79+
{
80+
id: 'tailwind-config',
81+
question: 'How do I configure Tailwind CSS to work with Streamdown?',
82+
answer: (
83+
<>
84+
<div>
85+
<p>
86+
For <span className="font-medium">Tailwind v4</span>, add a{' '}
87+
<code className="rounded-md bg-foreground/5 px-2 py-1 font-mono text-sm tracking-tight">
88+
@source
89+
</code>{' '}
90+
directive to your{' '}
91+
<code className="rounded-md bg-foreground/5 px-2 py-1 font-mono text-sm tracking-tight">
92+
globals.css
93+
</code>{' '}
94+
file with the path to Streamdown's distribution files:
95+
</p>
96+
<div className="my-2">
97+
<CodeBlock
98+
className="my-4 rounded-md border p-4"
99+
code={`@source "../node_modules/streamdown/dist/index.js";`}
100+
language="css"
101+
/>
102+
</div>
103+
</div>
104+
<div>
105+
<p>
106+
For <span className="font-medium">Tailwind v3</span>, add Streamdown
107+
to your{' '}
108+
<code className="rounded-md bg-foreground/5 px-2 py-1 font-mono text-sm tracking-tight">
109+
content
110+
</code>{' '}
111+
array in{' '}
112+
<code className="rounded-md bg-foreground/5 px-2 py-1 font-mono text-sm tracking-tight">
113+
tailwind.config.js
114+
</code>
115+
:
116+
</p>
117+
<div className="my-2">
118+
<CodeBlock
119+
className="my-4 rounded-md border p-4"
120+
code={`content: [
121+
// ... your other content paths
122+
"./node_modules/streamdown/dist/**/*.js",
123+
]`}
124+
language="js"
125+
/>
126+
</div>
127+
</div>
128+
<p>
129+
Adjust the paths based on your project structure. This ensures
130+
Tailwind scans Streamdown's files for any utility classes used in the
131+
component.
132+
</p>
133+
</>
134+
),
135+
},
136+
];
137+
138+
export const FAQ = () => (
139+
<div className="divide-y sm:grid sm:grid-cols-3 sm:divide-x sm:divide-y-0">
140+
<div className="space-y-2 p-4 sm:p-8">
141+
<h2 className="font-semibold text-2xl tracking-tight">FAQ</h2>
142+
<p className="text-muted-foreground">
143+
Common questions about Streamdown and how it works with AI-powered
144+
streaming applications.
145+
</p>
146+
</div>
147+
<div className="sm:col-span-2">
148+
<Accordion className="w-full" collapsible type="single">
149+
{faqs.map((faq) => (
150+
<AccordionItem key={faq.id} value={faq.id}>
151+
<AccordionTrigger className="p-4 text-left text-base sm:p-8">
152+
{faq.question}
153+
</AccordionTrigger>
154+
<AccordionContent className="p-4 pt-0 text-base leading-relaxed sm:p-8 sm:pt-0">
155+
{faq.answer}
156+
</AccordionContent>
157+
</AccordionItem>
158+
))}
159+
</Accordion>
160+
</div>
161+
</div>
162+
);

apps/website/app/globals.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,26 @@
122122
background-image: linear-gradient(45deg, var(--border) 12.5%, transparent 12.5%, transparent 50%, var(--border) 50%, var(--border) 62.5%, transparent 62.5%, transparent 100%);
123123
background-size: .25rem .25rem;
124124
}
125+
@keyframes accordion-down {
126+
from {
127+
height: 0;
128+
}
129+
to {
130+
height: var(--radix-accordion-content-height);
131+
}
132+
}
133+
@keyframes accordion-up {
134+
from {
135+
height: var(--radix-accordion-content-height);
136+
}
137+
to {
138+
height: 0;
139+
}
140+
}
141+
.animate-accordion-down {
142+
animation: accordion-down 0.2s ease-out;
143+
}
144+
.animate-accordion-up {
145+
animation: accordion-up 0.2s ease-out;
146+
}
125147
}

apps/website/app/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Metadata } from 'next';
22
import { CodeBlocks } from './components/code-blocks';
33
import { CallToAction } from './components/cta';
4+
import { FAQ } from './components/faq';
45
import { Footer } from './components/footer';
56
import { GitHubFlavoredMarkdown } from './components/gfm';
67
import { HardenedMarkdown } from './components/hardened';
@@ -33,6 +34,7 @@ const Home = () => (
3334
<HardenedMarkdown />
3435
<Props />
3536
<CallToAction />
37+
<FAQ />
3638
<Footer />
3739
</div>
3840
);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"use client"
2+
3+
import * as React from "react"
4+
import * as AccordionPrimitive from "@radix-ui/react-accordion"
5+
import { ChevronDownIcon } from "lucide-react"
6+
7+
import { cn } from "@/lib/utils"
8+
9+
function Accordion({
10+
...props
11+
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
12+
return <AccordionPrimitive.Root data-slot="accordion" {...props} />
13+
}
14+
15+
function AccordionItem({
16+
className,
17+
...props
18+
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
19+
return (
20+
<AccordionPrimitive.Item
21+
data-slot="accordion-item"
22+
className={cn("border-b last:border-b-0", className)}
23+
{...props}
24+
/>
25+
)
26+
}
27+
28+
function AccordionTrigger({
29+
className,
30+
children,
31+
...props
32+
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
33+
return (
34+
<AccordionPrimitive.Header className="flex">
35+
<AccordionPrimitive.Trigger
36+
data-slot="accordion-trigger"
37+
className={cn(
38+
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
39+
className
40+
)}
41+
{...props}
42+
>
43+
{children}
44+
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
45+
</AccordionPrimitive.Trigger>
46+
</AccordionPrimitive.Header>
47+
)
48+
}
49+
50+
function AccordionContent({
51+
className,
52+
children,
53+
...props
54+
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
55+
return (
56+
<AccordionPrimitive.Content
57+
data-slot="accordion-content"
58+
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
59+
{...props}
60+
>
61+
<div className={cn("pt-0 pb-4", className)}>{children}</div>
62+
</AccordionPrimitive.Content>
63+
)
64+
}
65+
66+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

apps/website/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"lint": "next lint"
1010
},
1111
"dependencies": {
12+
"@radix-ui/react-accordion": "^1.2.12",
1213
"@radix-ui/react-slot": "^1.2.3",
1314
"@radix-ui/react-tabs": "^1.1.13",
1415
"@vercel/analytics": "^1.5.0",

pnpm-lock.yaml

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)