mirror of
https://github.com/prdlk/leetcode.git
synced 2026-08-02 09:21:40 +00:00
docs(docs): migrate to array based folder structure
This commit is contained in:
+2
-4
@@ -1,4 +1,2 @@
|
||||
node_modules
|
||||
dist
|
||||
.source
|
||||
.vercel
|
||||
.blume/
|
||||
dist/
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
# docs
|
||||
|
||||
A [Fumapress](https://press.fumadocs.dev) (Fumadocs + Waku) docs site with
|
||||
typography by [shadcn/typeset](https://ui.shadcn.com/docs/typeset) — one CSS
|
||||
file you own.
|
||||
|
||||
## Getting started
|
||||
|
||||
Requires **Node.js 22+**.
|
||||
|
||||
```bash
|
||||
bun install
|
||||
bun dev # http://localhost:3000
|
||||
```
|
||||
|
||||
## Your typeset
|
||||
|
||||
| Control | Choice |
|
||||
| ------- | ------ |
|
||||
| Heading | DM Sans |
|
||||
| Body | Instrument Sans |
|
||||
| Mono | JetBrains Mono |
|
||||
| Size | 16px |
|
||||
| Leading | Loose (1.9) |
|
||||
| Flow | Compact (1em) |
|
||||
| Measure | 80ch |
|
||||
|
||||
- `src/typeset.css` is the stylesheet from the
|
||||
[typeset builder](https://ui.shadcn.com/typeset). It lives in your project —
|
||||
edit it directly, or regenerate it from the builder any time.
|
||||
- `src/app.css` holds your presets. `.typeset-docs` carries the choices
|
||||
above; `.typeset-compact` is a tighter preset for chat-like UI. Rhythm is
|
||||
three variables: `--typeset-size`, `--typeset-leading`, `--typeset-flow`.
|
||||
- `typeset-prompt.md` is an agent-ready prompt with the choices above baked
|
||||
in (like the builder's Prompt tab). Paste it into your coding agent to wire
|
||||
typeset into the surfaces that render markdown.
|
||||
|
||||
Wrap any rendered markdown/HTML:
|
||||
|
||||
```tsx
|
||||
<article className="typeset typeset-docs typeset-measure">{page}</article>
|
||||
<div className="typeset typeset-compact">{message}</div>
|
||||
```
|
||||
|
||||
Opt components out with `not-typeset`:
|
||||
|
||||
```tsx
|
||||
<div className="typeset typeset-docs">
|
||||
<p>Styled prose.</p>
|
||||
<Card className="not-typeset">Untouched component.</Card>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Fumadocs prose seam
|
||||
|
||||
Fumadocs UI ships its own `prose` styles for docs page bodies. If you apply
|
||||
`typeset` to the same container, both systems will add spacing. This
|
||||
template zeroes out `margin-block-end` inside `.typeset.prose` (see
|
||||
`src/app.css`) so flow comes only from typeset's `margin-block-start`.
|
||||
|
||||
To apply the typeset classes to docs pages, customize the page layout in
|
||||
`press.config.tsx` — see the
|
||||
[Fumapress layouts docs](https://press.fumadocs.dev/docs/layouts).
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
├── waku.config.ts # Waku/Vite plugins: press(), mdx(), tailwindcss()
|
||||
├── source.config.ts # Fumadocs MDX content source (content/docs)
|
||||
├── press.config.tsx # Site config, adapters, plugins
|
||||
├── typeset-prompt.md # Agent prompt with your typeset picks baked in
|
||||
├── content/docs/ # Your MDX pages
|
||||
└── src/
|
||||
├── app.css # Tailwind + presets + your typeset presets
|
||||
└── typeset.css # shadcn/typeset — yours to edit
|
||||
```
|
||||
|
||||
## Deploying static
|
||||
|
||||
Fumapress builds static + dynamic routes by default. For a pure CDN deploy,
|
||||
force static mode in `press.config.tsx`:
|
||||
|
||||
```tsx
|
||||
export default defineConfig({ mode: "static", /* ... */ });
|
||||
```
|
||||
-1739
File diff suppressed because it is too large
Load Diff
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"$schema": "node_modules/@fumadocs/cli/dist/schema.json",
|
||||
"aliases": {
|
||||
"uiDir": "./components/ui",
|
||||
"componentsDir": "./components",
|
||||
"layoutDir": "./layouts",
|
||||
"cssDir": "./styles",
|
||||
"libDir": "./lib"
|
||||
},
|
||||
"baseDir": "src",
|
||||
"uiLibrary": "base-ui",
|
||||
"framework": "waku",
|
||||
"commands": {}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
title: Introduction
|
||||
description: Prad's Random Leetcode Notes
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This is a collection of random CS notes that I've made over the years. I've been learning CS for a while now and I thought it would be a good idea to make a public repository of my notes. This is the first iteration of the repository and I plan to add more content to it over time.
|
||||
|
||||
## The Golden Rule (Memorize This)
|
||||
|
||||
**Only sort if the problem asks for ORDER or RANKING.**
|
||||
|
||||
If the problem asks for:
|
||||
- "first"
|
||||
- "index"
|
||||
- "position"
|
||||
- "does it exist"
|
||||
- "true/false"
|
||||
- "count how many"
|
||||
|
||||
→ **DO NOT SORT**. Just use the frequency map and loop.
|
||||
|
||||
---
|
||||
|
||||
## Decision Cheat Sheet
|
||||
|
||||
| What the problem wants | Do you need to sort? | What to do instead | Example Problem |
|
||||
|-----------------------------------------|----------------------|-------------------------------------|---------------------|
|
||||
| Sort characters by how often they appear | YES | Freq + Sort | LC 451 |
|
||||
| Top K most frequent | YES | Freq + Sort + slice | LC 347 |
|
||||
| Sort array by frequency | YES | Freq + Sort | LC 1636 |
|
||||
| First unique character (index) | NO | Freq + loop original string | LC 387 |
|
||||
| Is it an anagram? | NO | Freq + check counts | LC 242 |
|
||||
| How many numbers smaller than current | Kind of | Freq + prefix count | LC 1365 |
|
||||
| Most frequent element | NO | Freq + find max | LC 169 |
|
||||
|
||||
---
|
||||
|
||||
## Mental Checklist (Say this out loud every time)
|
||||
|
||||
1. "Did I count the frequencies?" → Yes
|
||||
2. "Does the problem care about the **original order**?"
|
||||
- Yes → Loop the original string/array and check freq
|
||||
- No → You can sort
|
||||
3. "Does it want an **index** or a **boolean**?"
|
||||
- Yes → Almost never sort
|
||||
4. "Does it want the elements **re-ordered**?"
|
||||
- Yes → Sort by frequency
|
||||
|
||||
---
|
||||
|
||||
## Super Short Version to Tattoo in Your Brain
|
||||
|
||||
**"If it wants the first / index / true-false → loop original.
|
||||
If it wants sorted / top k / reordered → sort."**
|
||||
|
||||
---
|
||||
|
||||
## Practice Trigger Words
|
||||
|
||||
- "first non-repeating" → Loop original
|
||||
- "return its index" → Loop original
|
||||
- "sort by frequency" → Sort
|
||||
- "top k frequent" → Sort + slice
|
||||
- "valid anagram" → Just check counts
|
||||
|
||||
|
||||
+62
-4
@@ -1,10 +1,68 @@
|
||||
---
|
||||
title: Introduction
|
||||
description: Welcome to your new Blume docs.
|
||||
description: Prad's Random Leetcode Notes
|
||||
---
|
||||
|
||||
# Introduction
|
||||
## Overview
|
||||
|
||||
This is a collection of random CS notes that I've made over the years. I've been learning CS for a while now and I thought it would be a good idea to make a public repository of my notes. This is the first iteration of the repository and I plan to add more content to it over time.
|
||||
|
||||
## The Golden Rule (Memorize This)
|
||||
|
||||
**Only sort if the problem asks for ORDER or RANKING.**
|
||||
|
||||
If the problem asks for:
|
||||
- "first"
|
||||
- "index"
|
||||
- "position"
|
||||
- "does it exist"
|
||||
- "true/false"
|
||||
- "count how many"
|
||||
|
||||
→ **DO NOT SORT**. Just use the frequency map and loop.
|
||||
|
||||
---
|
||||
|
||||
## Decision Cheat Sheet
|
||||
|
||||
| What the problem wants | Do you need to sort? | What to do instead | Example Problem |
|
||||
|-----------------------------------------|----------------------|-------------------------------------|---------------------|
|
||||
| Sort characters by how often they appear | YES | Freq + Sort | LC 451 |
|
||||
| Top K most frequent | YES | Freq + Sort + slice | LC 347 |
|
||||
| Sort array by frequency | YES | Freq + Sort | LC 1636 |
|
||||
| First unique character (index) | NO | Freq + loop original string | LC 387 |
|
||||
| Is it an anagram? | NO | Freq + check counts | LC 242 |
|
||||
| How many numbers smaller than current | Kind of | Freq + prefix count | LC 1365 |
|
||||
| Most frequent element | NO | Freq + find max | LC 169 |
|
||||
|
||||
---
|
||||
|
||||
## Mental Checklist (Say this out loud every time)
|
||||
|
||||
1. "Did I count the frequencies?" → Yes
|
||||
2. "Does the problem care about the **original order**?"
|
||||
- Yes → Loop the original string/array and check freq
|
||||
- No → You can sort
|
||||
3. "Does it want an **index** or a **boolean**?"
|
||||
- Yes → Almost never sort
|
||||
4. "Does it want the elements **re-ordered**?"
|
||||
- Yes → Sort by frequency
|
||||
|
||||
---
|
||||
|
||||
## Super Short Version to Tattoo in Your Brain
|
||||
|
||||
**"If it wants the first / index / true-false → loop original.
|
||||
If it wants sorted / top k / reordered → sort."**
|
||||
|
||||
---
|
||||
|
||||
## Practice Trigger Words
|
||||
|
||||
- "first non-repeating" → Loop original
|
||||
- "return its index" → Loop original
|
||||
- "sort by frequency" → Sort
|
||||
- "top k frequent" → Sort + slice
|
||||
- "valid anagram" → Just check counts
|
||||
|
||||
Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
|
||||
|
||||
Edit `docs/index.mdx` to get started, then run `blume dev`.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"name": "docs",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "waku dev",
|
||||
"build": "waku build",
|
||||
"start": "waku start",
|
||||
"types:check": "fumadocs-mdx && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui/react": "latest",
|
||||
"@fontsource-variable/dm-sans": "latest",
|
||||
"@fontsource-variable/instrument-sans": "latest",
|
||||
"@fontsource-variable/jetbrains-mono": "latest",
|
||||
"@fuma-translate/react": "^1.0.2",
|
||||
"@fumadocs/base-ui": "^16.11.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"cnfast": "^0.0.8",
|
||||
"fumadocs-core": "latest",
|
||||
"fumadocs-mdx": "latest",
|
||||
"fumadocs-ui": "npm:@fumadocs/base-ui@latest",
|
||||
"fumapress": "latest",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-server-dom-webpack": "^19.2.0",
|
||||
"vercel": "^55.0.0",
|
||||
"waku": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { defineConfig } from "fumapress";
|
||||
import { fumadocsMdx } from "fumapress/adapters/mdx";
|
||||
import defaultMdxComponents, { createRelativeLink } from "fumadocs-ui/mdx";
|
||||
import { TypeTable } from "fumadocs-ui/components/type-table";
|
||||
import { Step, Steps } from "fumadocs-ui/components/steps";
|
||||
import { Tabs, Tab } from "fumadocs-ui/components/tabs";
|
||||
import { Accordion, Accordions } from "fumadocs-ui/components/accordion";
|
||||
import { flexsearchPlugin } from "fumapress/plugins/flexsearch";
|
||||
import { llmsPlugin } from "fumapress/plugins/llms.txt";
|
||||
import { docs } from "./.source/server";
|
||||
|
||||
export default defineConfig({
|
||||
site: {
|
||||
name: "Docs",
|
||||
baseUrl: import.meta.env.DEV
|
||||
? "http://localhost:3000"
|
||||
: "https://cs.prad.nu",
|
||||
git: {
|
||||
user: "prdlk",
|
||||
branch: "main",
|
||||
repo: "leetcode",
|
||||
},
|
||||
},
|
||||
content: {
|
||||
docs: docs.toFumadocsSource(),
|
||||
},
|
||||
meta: {
|
||||
root() {
|
||||
return <meta property="og:type" content="website" />;
|
||||
},
|
||||
},
|
||||
})
|
||||
.plugins(flexsearchPlugin(), llmsPlugin())
|
||||
.adapters(
|
||||
fumadocsMdx({
|
||||
async getMdxComponents(page) {
|
||||
const source = await this.getLoader();
|
||||
return {
|
||||
...defaultMdxComponents,
|
||||
TypeTable,
|
||||
Steps,
|
||||
Step,
|
||||
Tabs,
|
||||
Tab,
|
||||
Accordion,
|
||||
Accordions,
|
||||
a: createRelativeLink(source, page),
|
||||
};
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -1,11 +0,0 @@
|
||||
import { defineConfig, defineDocs } from "fumadocs-mdx/config";
|
||||
|
||||
export const docs = defineDocs({
|
||||
dir: "content/docs",
|
||||
docs: {
|
||||
// Required by fumapress' llms.txt plugin (getText("processed")).
|
||||
postprocess: { includeProcessedMarkdown: true },
|
||||
},
|
||||
});
|
||||
|
||||
export default defineConfig();
|
||||
@@ -1,65 +0,0 @@
|
||||
@import "tailwindcss";
|
||||
@import "fumadocs-ui/css/neutral.css";
|
||||
@import "fumadocs-ui/css/preset.css";
|
||||
@import "fumapress/css/preset.css";
|
||||
|
||||
/* Fonts — self-hosted via Fontsource, referenced by the typeset presets. */
|
||||
@import "@fontsource-variable/dm-sans";
|
||||
@import "@fontsource-variable/instrument-sans";
|
||||
@import "@fontsource-variable/jetbrains-mono";
|
||||
|
||||
/* shadcn/typeset — one CSS file you own.
|
||||
Regenerate it any time at https://ui.shadcn.com/typeset */
|
||||
@import "./typeset.css";
|
||||
|
||||
:root {
|
||||
--font-dm-sans: "DM Sans Variable", sans-serif;
|
||||
--font-instrument-sans: "Instrument Sans Variable", sans-serif;
|
||||
--font-jetbrains-mono: "JetBrains Mono Variable", monospace;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--default-font-family: var(--font-instrument-sans);
|
||||
--font-heading: var(--font-dm-sans);
|
||||
--font-mono: var(--font-jetbrains-mono);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Typeset presets — tune rhythm per context.
|
||||
Three controls: size, leading, flow.
|
||||
Built with https://ui.shadcn.com/typeset
|
||||
--------------------------------------------------------- */
|
||||
|
||||
.typeset-docs {
|
||||
--typeset-font-body: var(--font-instrument-sans);
|
||||
--typeset-font-heading: var(--font-dm-sans);
|
||||
--typeset-font-mono: var(--font-jetbrains-mono);
|
||||
--typeset-size: 16px;
|
||||
--typeset-leading: 1.9;
|
||||
--typeset-flow: 1em;
|
||||
}
|
||||
|
||||
/* Tighter rhythm, e.g. for changelog callouts or chat-like UI */
|
||||
.typeset-compact {
|
||||
--typeset-leading: 1.6;
|
||||
--typeset-flow: 1em;
|
||||
}
|
||||
|
||||
/* Measure (80ch) — the builder puts max-width on the wrapper,
|
||||
not in typeset.css. Add this class next to `typeset` when your
|
||||
layout doesn't already constrain line length. */
|
||||
.typeset-measure {
|
||||
max-width: 37em;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
Fumadocs interop:
|
||||
Fumadocs UI ships its own `prose` styling for page bodies.
|
||||
When a container is both, let Typeset own the rhythm by
|
||||
disabling the prose margins that would double up.
|
||||
Keep interactive Fumadocs components (tabs, callouts,
|
||||
accordions) out of Typeset with `not-typeset`.
|
||||
--------------------------------------------------------- */
|
||||
.typeset.prose :where(*) {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { Check, LinkIcon } from 'lucide-react';
|
||||
import { type ComponentProps, type ReactNode, useEffect, useRef, useState } from 'react';
|
||||
import { cn } from '../lib/cn';
|
||||
import { useCopyButton } from '@fumadocs/base-ui/utils/use-copy-button';
|
||||
import { buttonVariants } from './ui/button';
|
||||
import { mergeRefs } from '../lib/merge-refs';
|
||||
import { useTranslations } from '@fuma-translate/react';
|
||||
import {
|
||||
Accordion as Root,
|
||||
AccordionContent,
|
||||
AccordionHeader,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from './ui/accordion';
|
||||
|
||||
export function Accordions({
|
||||
ref,
|
||||
className,
|
||||
defaultValue,
|
||||
...props
|
||||
}: ComponentProps<typeof Root>) {
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const composedRef = mergeRefs(ref, rootRef);
|
||||
const [value, setValue] = useState<unknown[]>(defaultValue ?? []);
|
||||
|
||||
useEffect(() => {
|
||||
const id = window.location.hash.substring(1);
|
||||
const element = rootRef.current;
|
||||
if (!element || id.length === 0) return;
|
||||
|
||||
const selected = document.getElementById(id);
|
||||
if (!selected || !element.contains(selected)) return;
|
||||
const value = selected.getAttribute('data-accordion-value');
|
||||
|
||||
if (value) setValue((prev) => [value, ...prev]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Root
|
||||
ref={composedRef}
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
className={(s) =>
|
||||
cn(
|
||||
'divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card',
|
||||
typeof className === 'function' ? className(s) : className,
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Accordion({
|
||||
title,
|
||||
id,
|
||||
value = String(title),
|
||||
children,
|
||||
...props
|
||||
}: Omit<ComponentProps<typeof AccordionItem>, 'value' | 'title'> & {
|
||||
title: string | ReactNode;
|
||||
value?: string;
|
||||
}) {
|
||||
return (
|
||||
<AccordionItem value={value} {...props}>
|
||||
<AccordionHeader id={id} data-accordion-value={value}>
|
||||
<AccordionTrigger>{title}</AccordionTrigger>
|
||||
{id ? <CopyButton id={id} /> : null}
|
||||
</AccordionHeader>
|
||||
<AccordionContent hiddenUntilFound>
|
||||
<div className="px-4 pb-2 text-[0.9375rem] prose-no-margin [&[hidden]:not([hidden='until-found'])]:hidden">
|
||||
{children}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
);
|
||||
}
|
||||
|
||||
function CopyButton({ id }: { id: string }) {
|
||||
const t = useTranslations({ note: 'accordion' });
|
||||
const [checked, onClick] = useCopyButton(() => {
|
||||
const url = new URL(window.location.href);
|
||||
url.hash = id;
|
||||
|
||||
return navigator.clipboard.writeText(url.toString());
|
||||
});
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t('Copy Link', { note: 'aria-label' })}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
color: 'ghost',
|
||||
className: 'text-fd-muted-foreground me-2',
|
||||
}),
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{checked ? <Check className="size-3.5" /> : <LinkIcon className="size-3.5" />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export function Steps({ children }: { children: ReactNode }) {
|
||||
return <div className="fd-steps">{children}</div>;
|
||||
}
|
||||
|
||||
export function Step({ children }: { children: ReactNode }) {
|
||||
return <div className="fd-step">{children}</div>;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { Accordion as Primitive } from '@base-ui/react/accordion';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { type ComponentProps } from 'react';
|
||||
import { cn } from '../../lib/cn';
|
||||
|
||||
export function Accordion({ className, ...props }: ComponentProps<typeof Primitive.Root>) {
|
||||
return (
|
||||
<Primitive.Root
|
||||
className={(s) =>
|
||||
cn(
|
||||
'divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card',
|
||||
typeof className === 'function' ? className(s) : className,
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function AccordionItem({ children, ...props }: ComponentProps<typeof Primitive.Item>) {
|
||||
return <Primitive.Item {...props}>{children}</Primitive.Item>;
|
||||
}
|
||||
|
||||
export function AccordionHeader({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<typeof Primitive.Header>) {
|
||||
return (
|
||||
<Primitive.Header
|
||||
className={(s) =>
|
||||
cn(
|
||||
'scroll-m-24 not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent',
|
||||
typeof className === 'function' ? className(s) : className,
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Primitive.Header>
|
||||
);
|
||||
}
|
||||
|
||||
export function AccordionTrigger({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<typeof Primitive.Trigger>) {
|
||||
return (
|
||||
<Primitive.Trigger
|
||||
className={(s) =>
|
||||
cn(
|
||||
'group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none',
|
||||
typeof className === 'function' ? className(s) : className,
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
<ChevronRight className="size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[panel-open]:rotate-90" />
|
||||
{children}
|
||||
</Primitive.Trigger>
|
||||
);
|
||||
}
|
||||
|
||||
export function AccordionContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<typeof Primitive.Panel>) {
|
||||
return (
|
||||
<Primitive.Panel
|
||||
className={(s) =>
|
||||
cn(
|
||||
'h-(--accordion-panel-height) overflow-hidden transition-[height] ease-out data-[ending-style]:h-0 data-[starting-style]:h-0',
|
||||
typeof className === 'function' ? className(s) : className,
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Primitive.Panel>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
|
||||
const variants = {
|
||||
primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
|
||||
outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground',
|
||||
ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
|
||||
secondary:
|
||||
'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground',
|
||||
} as const;
|
||||
|
||||
export const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fd-ring',
|
||||
{
|
||||
variants: {
|
||||
variant: variants,
|
||||
// fumadocs use `color` instead of `variant`
|
||||
color: variants,
|
||||
size: {
|
||||
sm: 'gap-1 px-2 py-1.5 text-xs',
|
||||
icon: 'p-1.5 [&_svg]:size-5',
|
||||
'icon-sm': 'p-1.5 [&_svg]:size-4.5',
|
||||
'icon-xs': 'p-1 [&_svg]:size-4',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export type ButtonProps = VariantProps<typeof buttonVariants>;
|
||||
@@ -1 +0,0 @@
|
||||
export { cn } from 'cnfast';
|
||||
@@ -1,13 +0,0 @@
|
||||
import type * as React from 'react';
|
||||
|
||||
export function mergeRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> {
|
||||
return (value) => {
|
||||
refs.forEach((ref) => {
|
||||
if (typeof ref === 'function') {
|
||||
ref(value);
|
||||
} else if (ref) {
|
||||
ref.current = value;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,490 +0,0 @@
|
||||
/*
|
||||
* shadcn/typeset
|
||||
* https://ui.shadcn.com/docs/typeset.
|
||||
*/
|
||||
|
||||
@layer components {
|
||||
.typeset {
|
||||
--typeset-font-body: inherit;
|
||||
--typeset-font-heading: var(--font-heading);
|
||||
--typeset-font-mono: var(--font-mono);
|
||||
--typeset-size: 1em;
|
||||
--typeset-leading: 1.75;
|
||||
--typeset-flow: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.typeset {
|
||||
font-family: var(--typeset-font-body);
|
||||
font-size: calc(var(--typeset-size) * 1.125);
|
||||
line-height: var(--typeset-leading);
|
||||
color: var(--color-foreground, currentColor);
|
||||
overflow-wrap: break-word;
|
||||
margin-trim: block-start;
|
||||
|
||||
@media (min-width: 48rem), print {
|
||||
font-size: var(--typeset-size);
|
||||
}
|
||||
|
||||
--typeset-muted: var(
|
||||
--color-muted-foreground,
|
||||
color-mix(in oklab, currentColor 60%, transparent)
|
||||
);
|
||||
--typeset-rule: var(
|
||||
--color-border,
|
||||
color-mix(in oklab, currentColor 20%, transparent)
|
||||
);
|
||||
}
|
||||
|
||||
.typeset
|
||||
*:not(
|
||||
:where(
|
||||
.not-typeset,
|
||||
[data-not-typeset],
|
||||
.not-typeset *,
|
||||
[data-not-typeset] *
|
||||
)
|
||||
) {
|
||||
/* Paragraphs. */
|
||||
&:where(p) {
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
/* Headings. */
|
||||
&:where(h1, h2, h3, h4, h5, h6) {
|
||||
color: var(--color-foreground, currentColor);
|
||||
font-family: var(--typeset-font-heading);
|
||||
font-weight: 600;
|
||||
margin-block-end: 0;
|
||||
}
|
||||
&:where(h1) {
|
||||
font-size: 1.75em;
|
||||
line-height: 1.3;
|
||||
margin-block-start: var(--typeset-flow);
|
||||
}
|
||||
&:where(h2) {
|
||||
font-size: 1.25em;
|
||||
line-height: 1.4;
|
||||
margin-block-start: calc(var(--typeset-flow) * 1.4);
|
||||
}
|
||||
&:where(h3) {
|
||||
font-size: 1.125em;
|
||||
line-height: 1.45;
|
||||
margin-block-start: var(--typeset-flow);
|
||||
}
|
||||
&:where(h4) {
|
||||
font-size: 1em;
|
||||
line-height: 1.5;
|
||||
margin-block-start: var(--typeset-flow);
|
||||
}
|
||||
&:where(h5) {
|
||||
font-size: 0.875em;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
color: var(--typeset-muted);
|
||||
margin-block-start: calc(var(--typeset-flow) / 0.875);
|
||||
}
|
||||
&:where(h6) {
|
||||
font-size: 0.8125em;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--typeset-muted);
|
||||
margin-block-start: calc(var(--typeset-flow) / 0.8125);
|
||||
}
|
||||
/* Anchors. */
|
||||
&:where([id]) {
|
||||
scroll-margin-block-start: var(--typeset-flow);
|
||||
}
|
||||
|
||||
/* Headings own the space below them. */
|
||||
&:where(h1 + *, h2 + *, h3 + *, h4 + *, h5 + *, h6 + *) {
|
||||
margin-block-start: 1em;
|
||||
}
|
||||
&:where(:is(h1, h2, h3, h4) :is(code)) {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* Links. */
|
||||
&:where(a) {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-color: color-mix(in oklab, currentColor 30%, transparent);
|
||||
}
|
||||
&:where(a:hover) {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
&:where(a:focus-visible) {
|
||||
outline: 2px solid var(--color-ring, currentColor);
|
||||
outline-offset: 2px;
|
||||
border-radius: 0.125em;
|
||||
}
|
||||
&:where(:is(h1, h2, h3, h4, h5, h6) :is(a)) {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/* Inline semantics. */
|
||||
&:where(strong, b) {
|
||||
font-weight: 600;
|
||||
}
|
||||
&:where(:is(h1, h2, h3, h4) :is(strong, b)) {
|
||||
font-weight: 600;
|
||||
}
|
||||
&:where(mark) {
|
||||
background-color: color-mix(
|
||||
in oklab,
|
||||
oklch(0.86 0.17 95) 40%,
|
||||
transparent
|
||||
);
|
||||
color: inherit;
|
||||
padding: 0.05em 0.15em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
&:where(abbr[title]) {
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: dotted;
|
||||
text-underline-offset: 0.2em;
|
||||
cursor: help;
|
||||
}
|
||||
&:where(del, s) {
|
||||
color: var(--typeset-muted);
|
||||
text-decoration-line: line-through;
|
||||
}
|
||||
&:where(sup, sub) {
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
&:where(sup) {
|
||||
top: -0.5em;
|
||||
}
|
||||
&:where(sub) {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
&:where(sup a) {
|
||||
text-decoration-line: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Lists. */
|
||||
&:where(ul) {
|
||||
list-style-type: disc;
|
||||
}
|
||||
&:where(ol) {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
&:where(ul ul) {
|
||||
list-style-type: circle;
|
||||
}
|
||||
&:where(ul ul ul) {
|
||||
list-style-type: square;
|
||||
}
|
||||
&:where(ol[type="a" i]) {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
&:where(ol[type="i" i]) {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
&:where(ol[type="A" s]) {
|
||||
list-style-type: upper-alpha;
|
||||
}
|
||||
&:where(ol[type="I" s]) {
|
||||
list-style-type: upper-roman;
|
||||
}
|
||||
&:where(ul, ol) {
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
padding-inline-start: 1.5em;
|
||||
}
|
||||
&:where(li) {
|
||||
margin-block-start: 0.5em;
|
||||
padding-inline-start: 0.4em;
|
||||
}
|
||||
&:where(li > p, li > ul, li > ol) {
|
||||
margin-block-start: 0.5em;
|
||||
}
|
||||
&:where(ul > li)::marker {
|
||||
color: var(--typeset-muted);
|
||||
}
|
||||
&:where(ol > li)::marker {
|
||||
color: var(--typeset-muted);
|
||||
}
|
||||
|
||||
/* GFM task lists. */
|
||||
&:where(ul.contains-task-list) {
|
||||
list-style-type: none;
|
||||
padding-inline-start: 0.25em;
|
||||
}
|
||||
&:where(li.task-list-item > input[type="checkbox"]) {
|
||||
margin-inline-end: 0.5em;
|
||||
vertical-align: -0.1em;
|
||||
accent-color: var(--color-primary, currentColor);
|
||||
}
|
||||
|
||||
/* Disclosures. */
|
||||
&:where(details) {
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
}
|
||||
&:where(summary) {
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
&:where(summary)::marker {
|
||||
color: var(--typeset-muted);
|
||||
}
|
||||
|
||||
/* Keyboard keys. */
|
||||
&:where(kbd) {
|
||||
font-family: inherit;
|
||||
font-size: 0.85em;
|
||||
font-weight: 500;
|
||||
border: 1px solid var(--typeset-rule);
|
||||
border-block-end-width: 2px;
|
||||
border-radius: min(calc(var(--radius, 0.5em) * 0.6), 0.35em);
|
||||
padding: 0.0625em 0.35em;
|
||||
}
|
||||
|
||||
/* Definition lists. */
|
||||
&:where(dl) {
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
}
|
||||
&:where(dt) {
|
||||
font-weight: 500;
|
||||
margin-block-start: 1em;
|
||||
}
|
||||
&:where(dt + dt) {
|
||||
margin-block-start: 0.25em;
|
||||
}
|
||||
&:where(dd) {
|
||||
margin-block-start: 0.25em;
|
||||
margin-inline-start: 0;
|
||||
padding-inline-start: 1em;
|
||||
color: var(--typeset-muted);
|
||||
}
|
||||
|
||||
/* Inline code. */
|
||||
&:where(:not(pre) > code) {
|
||||
background-color: var(
|
||||
--color-muted,
|
||||
color-mix(in oklab, currentColor 8%, transparent)
|
||||
);
|
||||
font-family: var(--typeset-font-mono);
|
||||
font-size: 0.85em;
|
||||
border-radius: min(calc(var(--radius, 0.5em) * 0.6), 0.35em);
|
||||
padding: 0.125em 0.3em;
|
||||
}
|
||||
|
||||
/* Code blocks. */
|
||||
&:where(pre) {
|
||||
background-color: var(
|
||||
--color-muted,
|
||||
color-mix(in oklab, currentColor 8%, transparent)
|
||||
);
|
||||
font-family: var(--typeset-font-mono);
|
||||
font-size: 0.875em;
|
||||
line-height: 1.5;
|
||||
tab-size: 2;
|
||||
direction: ltr;
|
||||
border-radius: var(--radius, 0.5em);
|
||||
padding: 0.75em 1em;
|
||||
overflow-x: auto;
|
||||
margin-block-start: calc(var(--typeset-flow) / 0.875);
|
||||
margin-block-end: 0;
|
||||
}
|
||||
&:where(pre code) {
|
||||
background-color: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Blockquote. */
|
||||
&:where(blockquote) {
|
||||
border-inline-start: 2px solid var(--typeset-rule);
|
||||
padding-inline-start: 1em;
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
||||
/* Dividers. */
|
||||
&:where(hr) {
|
||||
border: 0;
|
||||
border-block-start: 1px solid var(--typeset-rule);
|
||||
margin-block-start: calc(var(--typeset-flow) * 2.4);
|
||||
margin-block-end: 0;
|
||||
}
|
||||
&:where(hr + h1, hr + h2, hr + h3, hr + h4) {
|
||||
margin-block-start: var(--typeset-flow);
|
||||
}
|
||||
|
||||
/* GFM footnotes. */
|
||||
&:where(.footnotes, [data-footnotes]) {
|
||||
margin-block-start: calc(var(--typeset-flow) * 2);
|
||||
border-block-start: 1px solid var(--typeset-rule);
|
||||
padding-block-start: var(--typeset-flow);
|
||||
font-size: 0.875em;
|
||||
color: var(--typeset-muted);
|
||||
}
|
||||
|
||||
/* Math. */
|
||||
&:where(math[display="block"]) {
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding-block: 0.25em;
|
||||
}
|
||||
|
||||
/* Media. */
|
||||
&:where(img, video) {
|
||||
border-radius: var(--radius, 0.5em);
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
}
|
||||
&:where(p img) {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
&:where(figure) {
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
margin-inline: 0;
|
||||
}
|
||||
&:where(figcaption, caption) {
|
||||
color: var(--typeset-muted);
|
||||
font-size: 0.875em;
|
||||
text-align: center;
|
||||
margin-block-start: calc(0.75em / 0.875);
|
||||
}
|
||||
&:where(caption) {
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
/* Tables. Separators sit on cells, not tr:last-child: append-safe.
|
||||
Bare tables stay real tables (keep their semantics) and wrap to fit. */
|
||||
&:where(table) {
|
||||
max-width: 100%;
|
||||
font-size: 1em;
|
||||
line-height: 1.5;
|
||||
font-variant-numeric: tabular-nums;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border-block-end: 1px solid var(--typeset-rule);
|
||||
margin-block-start: var(--typeset-flow);
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
/* Horizontal scroll for any wide block. Wrap it and the wrapper owns the
|
||||
flow margin. Tables shrink to fit, so widen the table to make it scroll
|
||||
instead of compress. */
|
||||
&:where(.typeset-scroll) {
|
||||
overflow-x: auto;
|
||||
margin-block-start: var(--typeset-flow);
|
||||
}
|
||||
&:where(.typeset-scroll > *) {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
&:where(.typeset-scroll table) {
|
||||
width: max-content;
|
||||
max-width: none;
|
||||
}
|
||||
&:where(thead th) {
|
||||
font-weight: 500;
|
||||
text-align: start;
|
||||
white-space: nowrap;
|
||||
padding: 0.65em 1em;
|
||||
}
|
||||
&:where(:is(tbody, tfoot) :is(td, th)) {
|
||||
padding: 0.75em 1em;
|
||||
text-align: start;
|
||||
vertical-align: top;
|
||||
border-block-start: 1px solid var(--typeset-rule);
|
||||
}
|
||||
&:where(tbody th, tfoot th) {
|
||||
font-weight: 500;
|
||||
}
|
||||
&:where(th:first-child, td:first-child) {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
&:where(th[align="center"], td[align="center"]) {
|
||||
text-align: center;
|
||||
}
|
||||
&:where(th[align="right"], td[align="right"]) {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
/* Blocks inside list items keep the list's tighter rhythm. */
|
||||
&:where(li > blockquote, li > table, li > figure) {
|
||||
margin-block-start: 0.5em;
|
||||
}
|
||||
&:where(li > pre) {
|
||||
margin-block-start: calc(0.5em / 0.875);
|
||||
}
|
||||
|
||||
@media print {
|
||||
&:where(pre, table, blockquote, figure) {
|
||||
break-inside: avoid;
|
||||
}
|
||||
&:where(h1, h2, h3, h4) {
|
||||
break-after: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
/* Forced colors strips the code background, so give it a border. */
|
||||
@media (forced-colors: active) {
|
||||
&:where(:not(pre) > code) {
|
||||
border: 1px solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* First child adds no space above. Last so it wins ties. */
|
||||
.typeset
|
||||
> :where(:first-child):not(
|
||||
:where(
|
||||
.not-typeset,
|
||||
[data-not-typeset],
|
||||
.not-typeset *,
|
||||
[data-not-typeset] *
|
||||
)
|
||||
),
|
||||
.typeset
|
||||
> :where(:first-child)
|
||||
> :where(:first-child):not(
|
||||
:where(
|
||||
.not-typeset,
|
||||
[data-not-typeset],
|
||||
.not-typeset *,
|
||||
[data-not-typeset] *
|
||||
)
|
||||
),
|
||||
.typeset
|
||||
:where(
|
||||
li > :first-child,
|
||||
blockquote > :first-child,
|
||||
td > :first-child,
|
||||
th > :first-child,
|
||||
dd > :first-child,
|
||||
figure > :first-child,
|
||||
figure > picture > img
|
||||
):not(
|
||||
:where(
|
||||
.not-typeset,
|
||||
[data-not-typeset],
|
||||
.not-typeset *,
|
||||
[data-not-typeset] *
|
||||
)
|
||||
) {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"types": ["react/experimental"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src", "press.config.tsx", "source.config.ts", ".source"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
Install shadcn/typeset in this Fumapress (Fumadocs + Waku) project.
|
||||
|
||||
Typeset is a single stylesheet that styles rendered markdown: wrap the output
|
||||
in a `typeset` container and everything inside (headings, lists, tables,
|
||||
code, blockquotes, math) is styled. Everything outside is untouched.
|
||||
|
||||
If this project was scaffolded with create-fumapress-typeset, steps 1-4 are
|
||||
already done — verify each one and move on; every step below is safe to
|
||||
re-run.
|
||||
|
||||
1. Download https://ui.shadcn.com/typeset.css and save it as
|
||||
`src/typeset.css` (next to the main CSS file, `src/app.css`). If the
|
||||
file already exists, replace it with the downloaded copy.
|
||||
|
||||
2. Import it in `src/app.css`, after the Tailwind and Fumadocs imports:
|
||||
|
||||
@import "./typeset.css";
|
||||
|
||||
3. Install the fonts:
|
||||
|
||||
bun add @fontsource-variable/dm-sans @fontsource-variable/instrument-sans @fontsource-variable/jetbrains-mono
|
||||
|
||||
Then import them in `src/app.css`:
|
||||
|
||||
@import "@fontsource-variable/dm-sans";
|
||||
@import "@fontsource-variable/instrument-sans";
|
||||
@import "@fontsource-variable/jetbrains-mono";
|
||||
|
||||
:root {
|
||||
--font-dm-sans: "DM Sans Variable", sans-serif;
|
||||
--font-instrument-sans: "Instrument Sans Variable", sans-serif;
|
||||
--font-jetbrains-mono: "JetBrains Mono Variable", monospace;
|
||||
}
|
||||
|
||||
4. Add this preset to `src/app.css`, after the typeset import. If a class
|
||||
named `.typeset-docs` already exists, update its values in place. Leave
|
||||
any other `typeset-*` presets untouched: they are separate surfaces:
|
||||
|
||||
.typeset-docs {
|
||||
--typeset-font-body: var(--font-instrument-sans);
|
||||
--typeset-font-heading: var(--font-dm-sans);
|
||||
--typeset-font-mono: var(--font-jetbrains-mono);
|
||||
--typeset-size: 16px;
|
||||
--typeset-leading: 1.9;
|
||||
--typeset-flow: 1em;
|
||||
}
|
||||
|
||||
5. Do not apply the class anywhere yet. Search the project for surfaces that
|
||||
render markdown or rich content:
|
||||
|
||||
- The Fumadocs docs page body. Fumapress owns that layout: customize it in
|
||||
`press.config.tsx` with `createDocsLayoutPage` from
|
||||
"fumapress/layouts/docs" (see https://press.fumadocs.dev/docs/layouts)
|
||||
and wrap the page body there.
|
||||
- MDX components, react-markdown/Streamdown renderers,
|
||||
dangerouslySetInnerHTML with parsed markdown, `prose` classes, CMS
|
||||
content renderers.
|
||||
|
||||
Present the candidates you find as a short list and ask the user which
|
||||
surface should use typeset. Then wrap only the surface they pick:
|
||||
|
||||
<div className="typeset typeset-docs max-w-[37em]">
|
||||
{content}
|
||||
</div>
|
||||
|
||||
(`src/app.css` also defines `.typeset-measure` with the same
|
||||
max-width, if a plain class is preferred over a Tailwind utility.)
|
||||
|
||||
If the picked surface already has its own typography (Fumadocs' `prose`
|
||||
class, styled markdown components), list those styles and let the user
|
||||
decide what to remove before wrapping. Note: `src/app.css` already zeroes
|
||||
`margin-block-end` inside `.typeset.prose` so typeset owns the rhythm
|
||||
when both classes share a container.
|
||||
|
||||
Notes:
|
||||
|
||||
- To exclude an embedded component from typeset styles, add the
|
||||
`not-typeset` class or the `data-not-typeset` attribute to it. Keep
|
||||
interactive Fumadocs components (tabs, callouts, accordions) out of typeset
|
||||
this way.
|
||||
- Verify on the surface the user picked: headings, lists, tables, and code
|
||||
inside the container should be styled with no classes on the content
|
||||
itself.
|
||||
- Docs: https://ui.shadcn.com/docs/typeset
|
||||
Reference in New Issue
Block a user