UI: Improve consistency

This commit is contained in:
Fuma Nama
2024-11-12 20:54:00 +08:00
parent c0646f3089
commit 5fd4e2fa80
11 changed files with 83 additions and 25 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-ui': minor
---
Make TOC collapse to a popover on `lg` screen size instead of `md`
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-ui': minor
---
Support better table styles for Typography plugin
@@ -23,6 +23,8 @@ import { TypeTable } from 'fumadocs-ui/components/type-table';
/>
```
## References
### Type Table
<AutoTypeTable path="./content/docs/ui/props.ts" name="TypeTableProps" />
+17 -7
View File
@@ -10,7 +10,7 @@ import { CpuIcon, PanelsTopLeft, Database, Terminal } from 'lucide-react';
## Introduction
Fumadocs is a **documentation framework** based on Next.js, designed to be fast and flexible.
It composes seamlessly into Next.js App Router, and benefits from the React Server Component model.
It composes seamlessly into Next.js App Router, allowing a high flexibility with code.
Fumadocs has different parts:
@@ -18,7 +18,7 @@ Fumadocs has different parts:
<Card icon={<CpuIcon className="text-purple-300" />} title='Fumadocs Core'>
The core of Fumadocs handles most of the logic, including document search, content source adapters, and Markdown/MDX plugins.
Handles most of the logic, including document search, content source adapters, and Markdown extensions.
</Card>
@@ -42,7 +42,7 @@ A command line tool to configure features automatically, and install UI componen
</Cards>
Thanks to this model, theoretically all format and source can be supported by Fumadocs with a custom adapter.
Thanks to this model, all format and content source can be supported by Fumadocs with a custom adapter.
### Use Cases
@@ -66,9 +66,9 @@ For a better understanding of the docs, there are some common terminologies you
**Page Tree:** A tree of all the pages, separators and folders. It is essential for navigation elements, usually generated by the content source or hardcoded. See [Types definitions](/docs/headless/page-tree).
**MDX File:** A MDX file could be located in a folder, or available remotely. Notice that MDX Page is only referring to `page.mdx`, which is already a Next.js page on its own.
**Markdown (MDX) File:** A Markdown file could be located in a folder, or available remotely.
You might need to be familiar with Next.js App Router to continue.
Some basic knowledge of Next.js App Router would be useful for further customisations.
## Automatic Installation
@@ -125,7 +125,7 @@ npm run dev
In the project, you can see:
- `lib/source.ts`: Where you organize code for the source adapter, we use [`loader`](/docs/headless/source-api) to read from the content source.
- `lib/source.ts`: Where you organize code for content source adapter, we use [`loader`](/docs/headless/source-api) to read from content source.
- `app/layout.config.tsx`: Shared options for layouts, this is optional but preferred to keep.
- `app/(home)`: The route group for your landing page and other pages.
- `app/docs`: The documentation layout and pages.
@@ -148,7 +148,17 @@ they are customisable with exposed props like `style` and `className`.
Layouts are essential elements of docs, providing navigation and basic functionality.
We provided many options to customise them, see [Layouts](/docs/ui/blocks).
Although the docs didn't explicitly mention, you can always style the elements with CSS selectors.
Fumadocs CLI offers a way to install Fumadocs components to your project, allowing you to fully customise them with code.
```package-install
@fumadocs/cli --save-dev
```
```bash
pnpm fumadocs add
```
This is inspired and very similar to Shadcn UI.
</Accordion>
<Accordion id='fix-monorepo-styling' title="How to fix stylings not being applied in Monorepo?">
@@ -112,13 +112,13 @@ export default function ClerkTOCItems({
}
function getItemOffset(depth: number): number {
if (depth <= 2) return 16;
if (depth === 3) return 32;
if (depth <= 2) return 14;
if (depth === 3) return 26;
return 48;
}
function getLineOffset(depth: number): number {
return depth >= 3 ? 12 : 0;
return depth >= 3 ? 10 : 0;
}
function TOCItem({
@@ -140,19 +140,19 @@ function TOCItem({
style={{
paddingInlineStart: getItemOffset(item.depth),
}}
className="prose relative py-2 text-sm text-fd-muted-foreground transition-colors [overflow-wrap:anywhere] first:pt-0 last:pb-0 data-[active=true]:text-fd-primary"
className="prose relative py-1.5 text-sm text-fd-muted-foreground transition-colors [overflow-wrap:anywhere] first:pt-0 last:pb-0 data-[active=true]:text-fd-primary"
>
{offset !== upperOffset ? (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
className="absolute -top-2 start-0 size-4 rtl:-scale-x-100"
className="absolute -top-1.5 start-0 size-4 rtl:-scale-x-100"
>
<line
x1={upperOffset}
y1="0"
x2={offset}
y2="16"
y2="12"
className="stroke-fd-foreground/10"
strokeWidth="1"
/>
@@ -161,8 +161,8 @@ function TOCItem({
<div
className={cn(
'absolute inset-y-0 w-px bg-fd-foreground/10',
offset !== upperOffset && 'top-2',
offset !== lowerOffset && 'bottom-2',
offset !== upperOffset && 'top-1.5',
offset !== lowerOffset && 'bottom-1.5',
)}
style={{
insetInlineStart: offset,
+1 -1
View File
@@ -27,7 +27,7 @@ export function Toc(props: HTMLAttributes<HTMLDivElement>) {
{...props}
data-toc=""
className={cn(
'sticky top-fd-layout-top h-[var(--fd-toc-height)] flex-1 pb-2 pt-12 max-lg:hidden',
'sticky top-fd-layout-top h-[var(--fd-toc-height)] flex-1 pb-2 pt-12',
props.className,
)}
style={
+1 -1
View File
@@ -98,7 +98,7 @@ export function DocsLayout({
id="nd-docs-layout"
{...props.containerProps}
className={cn(
'flex flex-1 flex-row md:[--fd-sidebar-width:260px] lg:[--fd-toc-width:240px] xl:[--fd-toc-width:260px]',
'flex flex-1 flex-row md:[--fd-sidebar-width:260px] xl:[--fd-toc-width:260px]',
!navReplace && navEnabled
? '[--fd-nav-height:3.5rem] md:[--fd-nav-height:0px]'
: null,
+1 -1
View File
@@ -76,7 +76,7 @@ export function DocsLayout({
<LayoutBody
{...props.containerProps}
className={cn(
'[--fd-nav-height:3.5rem] md:[--fd-sidebar-width:260px] lg:[--fd-toc-width:240px] xl:[--fd-toc-width:260px]',
'[--fd-nav-height:3.5rem] md:[--fd-sidebar-width:260px] xl:[--fd-toc-width:260px]',
props.containerProps?.className,
)}
>
+3 -3
View File
@@ -137,7 +137,7 @@ export function DocsPage({
>
{replaceOrDefault(
{ enabled: tocPopoverEnabled, component: tocPopoverReplace },
<PageHeader className="lg:hidden">
<PageHeader className="xl:hidden">
<TocPopover>
<TocPopoverTrigger className="size-full" items={toc} />
<TocPopoverContent>
@@ -158,7 +158,7 @@ export function DocsPage({
)}
<article
className={cn(
'mx-auto flex w-full flex-1 flex-col gap-6 px-4 pt-10 md:px-6 md:pt-12',
'mx-auto flex w-full flex-1 flex-col gap-6 px-4 pt-10 md:px-8 md:pt-12',
tocEnabled ? 'max-w-[860px]' : 'max-w-[1120px]',
)}
>
@@ -181,7 +181,7 @@ export function DocsPage({
</PageContainer>
{replaceOrDefault(
{ enabled: tocEnabled, component: tocReplace },
<Toc>
<Toc className="max-xl:hidden">
<div className="flex h-full w-[var(--fd-toc-width)] max-w-full flex-col gap-3 pe-2">
{tocOptions.header}
<h3 className="-ms-0.5 inline-flex items-center gap-1.5 text-sm text-fd-muted-foreground">
+7 -2
View File
@@ -5,7 +5,7 @@ import { presets } from './theme/colors';
import { animations } from './theme/animations';
import { typography as typographyConfig } from './theme/typography';
interface DocsUIOptions {
export interface DocsUIOptions {
/**
* Prefix to the variable name of colors
*
@@ -38,6 +38,11 @@ interface DocsUIOptions {
* Color preset
*/
preset?: keyof typeof presets | Preset;
/**
* Disable custom table styles
*/
disableRoundedTable?: boolean;
}
type Keys =
@@ -271,7 +276,7 @@ export function createPreset(options: DocsUIOptions = {}): PresetsConfig {
theme: {
extend: {
typography: {
DEFAULT: typographyConfig,
DEFAULT: typographyConfig(options),
},
},
},
+33 -2
View File
@@ -1,4 +1,34 @@
export const typography = {
import type { DocsUIOptions } from '@/tailwind-plugin';
const tableStyles = {
table: {
borderCollapse: 'separate',
borderSpacing: '0',
'@apply bg-fd-card rounded-lg border overflow-hidden': '',
},
thead: {
border: '0',
},
th: {
'@apply p-2.5 border-l bg-muted': '',
},
'th:first-of-type': {
'@apply border-l-0': '',
},
'th, td:not(tr:last-of-type *)': {
'@apply border-b': '',
},
td: {
'@apply border-l p-2.5': '',
},
'td:first-of-type': {
'@apply border-l-0': '',
},
};
export const typography = ({ disableRoundedTable }: DocsUIOptions) => ({
css: {
'--tw-prose-body': `theme('colors.fd-foreground / 90%')`,
'--tw-prose-headings': `theme('colors.fd-foreground')`,
@@ -68,5 +98,6 @@ export const typography = {
'pre code::before': false,
'code::after': false,
'code::before': false,
...(!disableRoundedTable ? tableStyles : undefined),
},
};
});