mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
UI: Adjust design
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Adjust design (Accordion, Tabs, border color of themes)
|
||||
@@ -75,10 +75,10 @@ export default async function Page(props: {
|
||||
}}
|
||||
>
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription className="mb-2">
|
||||
<DocsDescription className="mb-0">
|
||||
{page.data.description}
|
||||
</DocsDescription>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<div className="flex flex-row gap-2 items-center mb-4">
|
||||
<LLMCopyButton slug={params.slug} />
|
||||
<EditOnGitHub
|
||||
url={`https://github.com/${owner}/${repo}/blob/dev/${path}`}
|
||||
|
||||
@@ -14,11 +14,11 @@ import { owner, repo } from '@/lib/github';
|
||||
import {
|
||||
Banner,
|
||||
DynamicCodeBlock,
|
||||
InlineTOC,
|
||||
Folder,
|
||||
Files,
|
||||
File,
|
||||
Files,
|
||||
Folder,
|
||||
ImageZoom,
|
||||
InlineTOC,
|
||||
} from '@/components/preview/lazy';
|
||||
import BannerImage from '@/public/banner.png';
|
||||
|
||||
@@ -55,7 +55,7 @@ export function card(): ReactNode {
|
||||
export function tabs(): ReactNode {
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className="space-y-4 rounded-xl bg-fd-background p-4 text-sm">
|
||||
<div className="prose-no-margin">
|
||||
<Tabs
|
||||
groupId="language"
|
||||
persist
|
||||
|
||||
@@ -6,7 +6,7 @@ export function Wrapper(props: HTMLAttributes<HTMLDivElement>) {
|
||||
<div
|
||||
{...props}
|
||||
className={cn(
|
||||
'rounded-lg bg-black/20 p-4 border prose-no-margin',
|
||||
'rounded-lg bg-radial-[at_bottom] from-blue-500/20 p-4 border border-fd-primary/10 prose-no-margin dark:bg-black/20',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"title": "Fumadocs Core",
|
||||
"description": "The headless library",
|
||||
"icon": "Library",
|
||||
"icon": "Cuboid",
|
||||
"root": true,
|
||||
"pages": [
|
||||
"---Guide---",
|
||||
|
||||
@@ -183,11 +183,9 @@ loader({
|
||||
|
||||
### Page Tree
|
||||
|
||||
The page tree is generated from your file system, using the **Page Tree Builder**.
|
||||
It also filters out some unnecessary information (e.g. unused frontmatter properties).
|
||||
The page tree is generated from your file system, some unnecessary information (e.g. unused frontmatter properties) will be filtered.
|
||||
|
||||
To customise the process, use the `pageTree` option.
|
||||
You can attach custom properties to page tree nodes, like customising the display name of pages and folders.
|
||||
You can customise it using the `pageTree` option, such as attaching custom properties to nodes, or customising the display name of pages.
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
"title": "OpenAPI Example",
|
||||
"description": "A demo for Fumadocs OpenAPI",
|
||||
"root": true,
|
||||
"icon": "Terminal",
|
||||
"icon": "Play",
|
||||
"pages": ["index", "...(generated)"]
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ Before continuing, make sure:
|
||||
fumadocs-ui fumadocs-core
|
||||
```
|
||||
|
||||
### 1. MDX Components
|
||||
### MDX Components
|
||||
|
||||
<include cwd meta='title="mdx-components.tsx"'>
|
||||
../../examples/next-mdx/mdx-components.tsx
|
||||
</include>
|
||||
|
||||
### 2. Content Source
|
||||
### Content Source
|
||||
|
||||
Fumadocs supports different content sources, including Fumadocs MDX and [Content Collections](/docs/headless/content-collections).
|
||||
|
||||
@@ -29,7 +29,7 @@ Fumadocs MDX is our official content source, you can configure it with:
|
||||
fumadocs-mdx @types/mdx
|
||||
```
|
||||
|
||||
```js title="next.config.mjs"
|
||||
```js tab="next.config.mjs"
|
||||
import { createMDX } from 'fumadocs-mdx/next';
|
||||
|
||||
const withMDX = createMDX();
|
||||
@@ -42,7 +42,7 @@ const config = {
|
||||
export default withMDX(config);
|
||||
```
|
||||
|
||||
```ts title="source.config.ts"
|
||||
```ts tab="source.config.ts"
|
||||
import { defineDocs } from 'fumadocs-mdx/config';
|
||||
|
||||
export const docs = defineDocs({
|
||||
@@ -50,12 +50,10 @@ export const docs = defineDocs({
|
||||
});
|
||||
```
|
||||
|
||||
Add a `postinstall` script to generate types:
|
||||
|
||||
```json title="package.json"
|
||||
```json tab="package.json"
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "fumadocs-mdx"
|
||||
"postinstall": "fumadocs-mdx" // [!code ++]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -73,7 +71,7 @@ export const source = loader({
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Root Layout
|
||||
### Root Layout
|
||||
|
||||
Wrap the entire application inside [Root Provider](/docs/ui/layouts/root-provider), and add required styles to `body`.
|
||||
|
||||
@@ -99,7 +97,7 @@ export default function Layout({ children }: { children: ReactNode }) {
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Styles
|
||||
### Styles
|
||||
|
||||
Add the following to `global.css`.
|
||||
|
||||
@@ -111,7 +109,7 @@ Add the following to `global.css`.
|
||||
|
||||
> It doesn't come with a default font, you may choose one from `next/font`.
|
||||
|
||||
### 5. Layout
|
||||
### Layout
|
||||
|
||||
Create a `app/layout.config.tsx` file to put the shared options for our layouts.
|
||||
|
||||
@@ -137,7 +135,7 @@ Create a folder `/app/docs` for our docs, and give it a proper layout.
|
||||
|
||||
> `pageTree` refers to Page Tree, it should be provided by your content source.
|
||||
|
||||
### 6. Page
|
||||
### Page
|
||||
|
||||
Create a catch-all route `/app/docs/[[...slug]]` for docs pages.
|
||||
|
||||
@@ -151,7 +149,7 @@ In the page, wrap your content in the [Page](/docs/ui/layouts/page) component.
|
||||
|
||||
</Tabs>
|
||||
|
||||
### 7. Search
|
||||
### Search
|
||||
|
||||
Use the default document search based on Orama.
|
||||
|
||||
@@ -161,7 +159,7 @@ Use the default document search based on Orama.
|
||||
|
||||
Learn more about [Document Search](/docs/headless/search).
|
||||
|
||||
### 8. Done
|
||||
### Done
|
||||
|
||||
You can start the dev server and create MDX files.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"title": "Framework",
|
||||
"description": "The docs framework",
|
||||
"icon": "Building2",
|
||||
"icon": "Building",
|
||||
"root": true,
|
||||
"pages": [
|
||||
"---Introduction---",
|
||||
|
||||
@@ -116,11 +116,7 @@ export function Operation({
|
||||
<>
|
||||
{heading(headingLevel, 'Response Body', ctx)}
|
||||
|
||||
<Tabs
|
||||
items={statuses}
|
||||
groupId="fumadocs_openapi_responses"
|
||||
className="bg-transparent"
|
||||
>
|
||||
<Tabs items={statuses} groupId="fumadocs_openapi_responses">
|
||||
{statuses.map((status) => (
|
||||
<ResponseTab
|
||||
key={status}
|
||||
|
||||
@@ -205,30 +205,31 @@ export function Schema({
|
||||
(s) => isComplexType(s) && !stack.includes(s),
|
||||
);
|
||||
|
||||
footer = (
|
||||
<div className="flex flex-col gap-2">
|
||||
{mentionedObjectTypes.map((s, idx) => {
|
||||
let title = typeof s === 'object' ? s.title : null;
|
||||
title ??=
|
||||
mentionedObjectTypes.length === 1
|
||||
? 'Show Attributes'
|
||||
: `Object ${idx + 1}`;
|
||||
if (mentionedObjectTypes.length > 0)
|
||||
footer = (
|
||||
<div className="flex flex-col gap-2">
|
||||
{mentionedObjectTypes.map((s, idx) => {
|
||||
let title = typeof s === 'object' ? s.title : null;
|
||||
title ??=
|
||||
mentionedObjectTypes.length === 1
|
||||
? 'Show Attributes'
|
||||
: `Object ${idx + 1}`;
|
||||
|
||||
return (
|
||||
<renderer.ObjectCollapsible key={idx} name={title}>
|
||||
<Schema
|
||||
name="element"
|
||||
schema={s}
|
||||
ctx={{
|
||||
...ctx,
|
||||
stack: [schema, ...stack],
|
||||
}}
|
||||
/>
|
||||
</renderer.ObjectCollapsible>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<renderer.ObjectCollapsible key={idx} name={title}>
|
||||
<Schema
|
||||
name="element"
|
||||
schema={s}
|
||||
ctx={{
|
||||
...ctx,
|
||||
stack: [schema, ...stack],
|
||||
}}
|
||||
/>
|
||||
</renderer.ObjectCollapsible>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
--color-fd-popover-foreground: hsl(0, 0%, 15.1%);
|
||||
--color-fd-card: hsl(0, 0%, 99.7%);
|
||||
--color-fd-card-foreground: hsl(0, 0%, 3.9%);
|
||||
--color-fd-border: hsl(0, 0%, 89.8%);
|
||||
--color-fd-border: hsla(0, 0%, 60%, 0.2);
|
||||
--color-fd-primary: hsl(0, 0%, 9%);
|
||||
--color-fd-primary-foreground: hsl(0, 0%, 98%);
|
||||
--color-fd-secondary: hsl(0, 0%, 96.1%);
|
||||
@@ -26,7 +26,7 @@
|
||||
--color-fd-popover-foreground: hsl(0, 0%, 98%);
|
||||
--color-fd-card: hsl(0, 0%, 4%);
|
||||
--color-fd-card-foreground: hsl(0, 0%, 98%);
|
||||
--color-fd-border: hsl(0, 0%, 14%);
|
||||
--color-fd-border: hsl(0, 0%, 50%, 0.2);
|
||||
--color-fd-primary: hsl(0, 0%, 98%);
|
||||
--color-fd-primary-foreground: hsl(0, 0%, 9%);
|
||||
--color-fd-secondary: hsl(0, 0%, 12.9%);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
--color-fd-popover-foreground: hsl(0, 0%, 15.1%);
|
||||
--color-fd-card: hsl(0, 0%, 94.7%);
|
||||
--color-fd-card-foreground: hsl(0, 0%, 3.9%);
|
||||
--color-fd-border: hsl(0, 0%, 89.8%);
|
||||
--color-fd-border: hsla(0, 0%, 62%, 20%);
|
||||
--color-fd-primary: hsl(0, 0%, 9%);
|
||||
--color-fd-primary-foreground: hsl(0, 0%, 98%);
|
||||
--color-fd-secondary: hsl(0, 0%, 93.1%);
|
||||
@@ -26,7 +26,7 @@
|
||||
--color-fd-popover-foreground: hsl(0, 0%, 88%);
|
||||
--color-fd-card: hsl(0, 0%, 9.8%);
|
||||
--color-fd-card-foreground: hsl(0, 0%, 98%);
|
||||
--color-fd-border: hsl(0, 0%, 14%);
|
||||
--color-fd-border: hsla(0, 0%, 40%, 20%);
|
||||
--color-fd-primary: hsl(0, 0%, 98%);
|
||||
--color-fd-primary-foreground: hsl(0, 0%, 9%);
|
||||
--color-fd-secondary: hsl(0, 0%, 12.9%);
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
}
|
||||
|
||||
.dark {
|
||||
--color-fd-background: hsl(220, 60%, 6%);
|
||||
--color-fd-background: hsl(220, 60%, 8%);
|
||||
--color-fd-foreground: hsl(220, 60%, 94.5%);
|
||||
--color-fd-muted: hsl(220, 50%, 10%);
|
||||
--color-fd-muted-foreground: hsl(220, 30%, 65%);
|
||||
--color-fd-popover: hsl(220, 50%, 10%);
|
||||
--color-fd-popover-foreground: hsl(220, 60%, 94.5%);
|
||||
--color-fd-card: hsl(220, 50%, 10%);
|
||||
--color-fd-card: hsla(220, 56%, 15%, 0.4);
|
||||
--color-fd-card-foreground: hsl(220, 60%, 94.5%);
|
||||
--color-fd-border: hsl(220, 50%, 20%);
|
||||
--color-fd-border: hsla(220, 50%, 50%, 0.2);
|
||||
--color-fd-primary: hsl(205, 100%, 85%);
|
||||
--color-fd-primary-foreground: hsl(0, 0%, 9%);
|
||||
--color-fd-secondary: hsl(220, 50%, 20%);
|
||||
|
||||
+10
-10
@@ -1,8 +1,8 @@
|
||||
@theme {
|
||||
--color-fd-background: hsl(256, 100%, 96%);
|
||||
--color-fd-primary: hsl(270, 100%, 52%);
|
||||
--color-fd-border: hsl(270, 40%, 80%);
|
||||
--color-fd-accent: hsl(270, 60%, 86%);
|
||||
--color-fd-border: hsla(270, 40%, 80%, 0.5);
|
||||
--color-fd-accent: hsl(270, 40%, 88%);
|
||||
--color-fd-accent-foreground: hsl(270, 100%, 20%);
|
||||
--color-fd-muted: hsl(256, 60%, 94%);
|
||||
--color-fd-muted-foreground: hsl(256, 50%, 50%);
|
||||
@@ -18,20 +18,20 @@
|
||||
}
|
||||
|
||||
.dark {
|
||||
--color-fd-background: hsl(256, 60%, 6%);
|
||||
--color-fd-background: hsl(256, 20%, 6%);
|
||||
--color-fd-primary: hsl(270, 100%, 86%);
|
||||
--color-fd-border: hsl(270, 100%, 20%);
|
||||
--color-fd-accent: hsl(256, 60%, 26%);
|
||||
--color-fd-accent-foreground: hsl(270, 100%, 86%);
|
||||
--color-fd-muted: hsl(256, 60%, 10%);
|
||||
--color-fd-border: hsla(270, 50%, 40%, 0.3);
|
||||
--color-fd-accent: hsl(256, 23%, 22%);
|
||||
--color-fd-accent-foreground: hsl(270, 40%, 86%);
|
||||
--color-fd-muted: hsl(256, 23%, 10%);
|
||||
--color-fd-foreground: hsl(256, 60%, 90%);
|
||||
--color-fd-muted-foreground: hsl(256, 50%, 75%);
|
||||
--color-fd-secondary: hsl(270, 100%, 20%);
|
||||
--color-fd-secondary: hsl(270, 23%, 20%);
|
||||
--color-fd-secondary-foreground: hsl(256, 60%, 90%);
|
||||
--color-fd-card: hsl(256, 60%, 10%);
|
||||
--color-fd-card: hsl(256, 23%, 10%);
|
||||
--color-fd-card-foreground: hsl(256, 60%, 90%);
|
||||
--color-fd-popover-foreground: hsl(256, 60%, 90%);
|
||||
--color-fd-popover: hsl(256, 60%, 6%);
|
||||
--color-fd-popover: hsl(256, 23%, 6%);
|
||||
--color-fd-primary-foreground: hsl(256, 60%, 6%);
|
||||
--color-fd-ring: hsl(270, 100%, 86%);
|
||||
}
|
||||
|
||||
@@ -69,14 +69,14 @@ export const Accordion = forwardRef<
|
||||
id={id}
|
||||
className="not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent"
|
||||
>
|
||||
<AccordionPrimitive.Trigger className="flex flex-1 items-center gap-2 px-4 py-2.5 text-start focus-visible:outline-none">
|
||||
<ChevronRight className="-ms-1 size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[state=open]/accordion:rotate-90" />
|
||||
<AccordionPrimitive.Trigger className="flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none">
|
||||
<ChevronRight className="size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[state=open]/accordion:rotate-90" />
|
||||
{title}
|
||||
</AccordionPrimitive.Trigger>
|
||||
{id ? <CopyButton id={id} /> : null}
|
||||
</AccordionPrimitive.Header>
|
||||
<AccordionPrimitive.Content className="overflow-hidden data-[state=closed]:animate-fd-accordion-up data-[state=open]:animate-fd-accordion-down">
|
||||
<div className="px-4 py-2 prose-no-margin">{children}</div>
|
||||
<div className="px-4 pb-2 text-[15px] prose-no-margin">{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
</AccordionPrimitive.Item>
|
||||
);
|
||||
|
||||
@@ -93,13 +93,13 @@ export const CodeBlock = forwardRef<HTMLElement, CodeBlockProps>(
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
'not-prose group fd-codeblock relative my-4 overflow-hidden rounded-lg border bg-fd-secondary/50 text-sm',
|
||||
'not-prose group fd-codeblock relative my-4 overflow-hidden rounded-lg border bg-fd-card text-sm',
|
||||
keepBackground && 'bg-(--shiki-light-bg) dark:bg-(--shiki-dark-bg)',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{title ? (
|
||||
<div className="flex items-center gap-2 border-b bg-fd-muted px-4 py-1.5">
|
||||
<div className="flex items-center gap-2 bg-fd-secondary px-4 py-1.5">
|
||||
{icon ? (
|
||||
<div
|
||||
className="text-fd-muted-foreground [&_svg]:size-3.5"
|
||||
|
||||
@@ -366,7 +366,9 @@ export function SidebarFolderContent(props: CollapsibleContentProps) {
|
||||
[ctx],
|
||||
)}
|
||||
>
|
||||
<div className="absolute w-px inset-y-0 bg-fd-border start-3" />
|
||||
{ctx.level === 1 && (
|
||||
<div className="absolute w-px inset-y-0 bg-fd-border start-3" />
|
||||
)}
|
||||
{props.children}
|
||||
</Context.Provider>
|
||||
</CollapsibleContent>
|
||||
|
||||
@@ -5,13 +5,13 @@ import type {
|
||||
TabsProps as BaseProps,
|
||||
} from '@radix-ui/react-tabs';
|
||||
import {
|
||||
useMemo,
|
||||
useState,
|
||||
createContext,
|
||||
useContext,
|
||||
useLayoutEffect,
|
||||
useId,
|
||||
useEffect,
|
||||
useId,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { cn } from '@/utils/cn';
|
||||
import * as Primitive from './ui/tabs';
|
||||
@@ -186,7 +186,7 @@ export function Tab({ value, className, ...props }: TabProps) {
|
||||
<Primitive.TabsContent
|
||||
value={v}
|
||||
className={cn(
|
||||
'prose-no-margin [&>figure:only-child]:-m-4 [&>figure:only-child]:rounded-none [&>figure:only-child]:border-none',
|
||||
'prose-no-margin [&>figure:only-child]:-m-4 [&>figure:only-child]:border-none',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -12,7 +12,7 @@ const Tabs = React.forwardRef<
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-col overflow-hidden rounded-xl border bg-fd-card',
|
||||
'flex flex-col overflow-hidden rounded-xl border bg-fd-secondary',
|
||||
props.className,
|
||||
)}
|
||||
/>
|
||||
@@ -28,10 +28,7 @@ const TabsList = React.forwardRef<
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-row items-end gap-4 overflow-x-auto bg-fd-secondary px-4 text-fd-muted-foreground',
|
||||
props.className,
|
||||
)}
|
||||
className={cn('flex flex-row gap-4 overflow-x-auto px-4', props.className)}
|
||||
/>
|
||||
));
|
||||
TabsList.displayName = 'TabsList';
|
||||
@@ -44,7 +41,7 @@ const TabsTrigger = React.forwardRef<
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
'whitespace-nowrap border-b border-transparent py-2 text-sm font-medium transition-colors hover:text-fd-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=active]:border-fd-primary data-[state=active]:text-fd-primary',
|
||||
'whitespace-nowrap text-fd-muted-foreground border-b border-transparent py-2 text-sm font-medium transition-colors hover:text-fd-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=active]:border-fd-primary data-[state=active]:text-fd-primary',
|
||||
props.className,
|
||||
)}
|
||||
/>
|
||||
@@ -58,7 +55,10 @@ const TabsContent = React.forwardRef<
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn('p-4', props.className)}
|
||||
className={cn(
|
||||
'p-4 text-[15px] bg-fd-background rounded-xl',
|
||||
props.className,
|
||||
)}
|
||||
/>
|
||||
));
|
||||
TabsContent.displayName = 'TabsContent';
|
||||
|
||||
@@ -56,7 +56,7 @@ export function CollapsibleControl() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed flex flex-row animate-fd-fade-in rounded-xl p-0.5 border bg-fd-muted text-fd-muted-foreground z-10 xl:start-4 max-xl:end-4"
|
||||
className="fixed flex shadow-lg animate-fd-fade-in rounded-xl p-0.5 border bg-fd-muted text-fd-muted-foreground z-10 xl:start-4 max-xl:end-4"
|
||||
style={{
|
||||
top: 'calc(var(--fd-banner-height) + var(--fd-tocnav-height) + var(--spacing) * 4)',
|
||||
}}
|
||||
@@ -66,8 +66,8 @@ export function CollapsibleControl() {
|
||||
buttonVariants({
|
||||
color: 'ghost',
|
||||
size: 'icon-sm',
|
||||
className: 'rounded-lg',
|
||||
}),
|
||||
'rounded-lg',
|
||||
)}
|
||||
>
|
||||
<SidebarIcon />
|
||||
|
||||
Reference in New Issue
Block a user