diff --git a/.changeset/docs-markdown-and-name.md b/.changeset/docs-markdown-and-name.md new file mode 100644 index 0000000..6872b78 --- /dev/null +++ b/.changeset/docs-markdown-and-name.md @@ -0,0 +1,4 @@ +--- +--- + +Serve the documentation website's pages as Markdown, name the site Sitex throughout, let browsers keep its build files, and move the component documentation to Fulldev UI's React components. diff --git a/apps/docs/src/globals/index.yaml b/apps/docs/src/globals/index.yaml index d705c97..87535d9 100644 --- a/apps/docs/src/globals/index.yaml +++ b/apps/docs/src/globals/index.yaml @@ -1,4 +1,4 @@ -name: Sitex Docs +name: Sitex logo: label: Sitex href: / @@ -22,33 +22,5 @@ sidebar: label: Pages - href: /docs/layouts label: Layouts - - href: /docs/components - label: Components - - label: Components - links: - - href: /docs/components/banner - label: Banner - - href: /docs/components/code - label: Code - - href: /docs/components/gradient - label: Gradient - - href: /docs/components/header - label: Header - - href: /docs/components/layout - label: Layout - - href: /docs/components/marquee - label: Marquee - - href: /docs/components/price - label: Price - - href: /docs/components/rating - label: Rating - - href: /docs/components/section - label: Section - - href: /docs/components/theme-toggle - label: Theme Toggle - - href: /docs/components/tile - label: Tile - - href: /docs/components/toc - label: Toc - - href: /docs/components/typography - label: Typography + - href: /docs/rendering + label: Rendering diff --git a/apps/docs/src/layouts/base.tsx b/apps/docs/src/layouts/base.tsx index c59e9fa..42599f6 100644 --- a/apps/docs/src/layouts/base.tsx +++ b/apps/docs/src/layouts/base.tsx @@ -15,6 +15,7 @@ import { Posthog } from "@/components/posthog" import { Sidebar1 } from "@/components/blocks/sidebar-1" import { ThemeScript } from "@/components/ui/theme-toggle" import { globals } from "@/lib/globals" +import { markdownPath } from "@/lib/markdown" import { theme } from "@/lib/theme" export type BaseLayoutProps = LayoutHeadProps & { @@ -41,7 +42,7 @@ export default function BaseLayout({ image={ head.image || { src: "https://sitex.full.dev/social.png", - alt: "SiteX", + alt: globals.name, width: 1200, height: 630, type: "image/png", @@ -49,6 +50,7 @@ export default function BaseLayout({ } > + ) { + // Fulldev made Sitex; as creator rather than publisher it does not read + // as a second name for the site itself. const website: WithContext = { "@context": "https://schema.org", "@type": "WebSite", - name: title, + name: globals.name, description, url, - publisher: { + creator: { "@type": "Organization", name: "Fulldev", url: "https://full.dev", diff --git a/apps/docs/src/lib/markdown.ts b/apps/docs/src/lib/markdown.ts new file mode 100644 index 0000000..d18d316 --- /dev/null +++ b/apps/docs/src/lib/markdown.ts @@ -0,0 +1,3 @@ +/** A page's Markdown version: /docs/pages at /docs/pages.md, the homepage at /index.md. */ +export const markdownPath = (path: string) => + `${path.replace(/\/$/, "") || "/index"}.md` diff --git a/apps/docs/src/pages/docs/components.mdx b/apps/docs/src/pages/docs/components.mdx deleted file mode 100644 index aa6a4df..0000000 --- a/apps/docs/src/pages/docs/components.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -layout: "doc" -title: "Components" -description: "Static rendering by default, client directives, and the pre-built component registry." ---- - -Components are plain React. Everything renders to static HTML at build time, and Sitex follows each page's module graph to include exactly the CSS that page imports. Pages ship zero JavaScript unless a component opts in. - -## Client Directives - -Imported React components opt into browser rendering with a `client:*` directive. Directives live in TSX (layouts, blocks, components), never in MDX bodies, and never on HTML elements or same-file components. - -```tsx -import Search from "@/components/search" - -export default function Header() { - return -} -``` - -1. `client:load` — hydrates immediately. For controls that must work right away. -2. `client:idle` — hydrates when the browser is idle. For page chrome like sidebars. -3. `client:visible` — hydrates when scrolled into view. For below-the-fold widgets. -4. `client:media="(min-width: 64rem)"` — hydrates when the media query matches. -5. `client:only` — skips static rendering, renders in the browser only. - -Island props must be JSON-serializable — functions, symbols, React elements, and cyclic values throw at build time. Children pass through as static HTML; a nested `client:*` component inside them becomes its own independent island. Components that share client state belong inside one island root. - -## Pre-Built Components - -Sitex ships components for the pieces content sites keep needing — sections, typography, code blocks, a table of contents, a price display. They follow the [shadcn/ui](https://ui.shadcn.com) conventions (Tailwind, `cn` from `@/lib/utils`, your theme tokens) and install from the Sitex registry: the source lands in `src/components/ui`, where you own and edit it. No npm package, no version to track, and they compose with regular shadcn/ui components. - -Initialize shadcn once (creates `components.json` and `src/lib/utils.ts`), then add what you need: - -```bash -pnpm dlx shadcn@latest init -pnpm dlx shadcn@latest add https://sitex.full.dev/r/section.json -``` - -| Component | Description | -| ----------------------------------------------- | -------------------------------------------------------------------------------------------- | -| [`banner`](/docs/components/banner) | Dismissable site-wide announcement banner. | -| [`code`](/docs/components/code) | Code block with shiki highlighting and a copy button; drop-in MDX `pre` replacement. | -| [`gradient`](/docs/components/gradient) | Decorative gradient surface for hero and section backgrounds. | -| [`header`](/docs/components/header) | Site header with container and grouping primitives. | -| [`layout`](/docs/components/layout) | Document primitives for Sitex layouts: html, head with SEO meta and JSON-LD, body, and main. | -| [`marquee`](/docs/components/marquee) | Continuously scrolling strip for logos and quotes. | -| [`price`](/docs/components/price) | Locale-aware price display with sale and discount formatting. | -| [`rating`](/docs/components/rating) | Star rating display. | -| [`section`](/docs/components/section) | Page section with consistent vertical rhythm and container. | -| [`theme-toggle`](/docs/components/theme-toggle) | Light/dark toggle button with a head script that prevents theme flash. | -| [`tile`](/docs/components/tile) | Card-like tile and tile group for feature grids. | -| [`toc`](/docs/components/toc) | Table of contents navigation for page headings. | -| [`typography`](/docs/components/typography) | Prose typography primitives for MDX content. | - -## Prefetch - -Navigation is prefetched by default: Sitex injects speculation rules so supporting browsers prerender links on hover (instant navigation), plus a small hover-prefetch script for the rest. Disable it with `sitex({ prefetch: false })`, or per link tree with a `data-no-prefetch` attribute. diff --git a/apps/docs/src/pages/docs/components/banner.mdx b/apps/docs/src/pages/docs/components/banner.mdx deleted file mode 100644 index c8a5a80..0000000 --- a/apps/docs/src/pages/docs/components/banner.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -layout: "doc" -title: "Banner" -description: "Dismissable site-wide announcement banner." ---- - -A site-wide announcement bar with an optional close button and session-based dismissal. Use it for launch notices, promos, or maintenance messages at the top of a layout. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/banner.json -``` - -## Usage - -```tsx -import { Banner, BannerContainer } from "@/components/ui/banner" - -export function SiteBanner() { - return ( - - -

SiteX 0.3 is out — read the release notes.

-
-
- ) -} -``` - -Key parts: - -1. `Banner` renders an `aside` with two variants: `default` (full-width, inverted colors) and `floating` (rounded, bordered, centered). -2. `storageKey` makes dismissal persist for the session: closing stores a flag in `sessionStorage` and the banner stays hidden on later renders. -3. `BannerContainer` centers the content and renders the close button; pass `showClose={false}` to hide it, or `onClose` to intercept the click (call `event.preventDefault()` to keep the banner open). -4. `bannerVariants` is exported for reusing the variant classes elsewhere. - -The dismiss behavior and `sessionStorage` check are client-side, so hydrate the banner as an island with a Sitex client directive where you use it in a layout or block, e.g. ``. diff --git a/apps/docs/src/pages/docs/components/code.mdx b/apps/docs/src/pages/docs/components/code.mdx deleted file mode 100644 index 852520e..0000000 --- a/apps/docs/src/pages/docs/components/code.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -layout: "doc" -title: "Code" -description: "Code block with shiki syntax highlighting and a copy button; drop-in MDX pre replacement." ---- - -A syntax-highlighted code block powered by shiki, with light/dark themes and a copy button. It is designed as the `pre` replacement for MDX code fences, and also works standalone. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/code.json -``` - -## Usage - -Wire it up as the MDX `pre` component in `vite.config.ts`: - -```ts -import { defineConfig } from "vite-plus" -import { sitex } from "@fulldotdev/sitex/plugin" -import react from "@vitejs/plugin-react" - -export default defineConfig({ - plugins: [ - react(), - sitex({ mdx: { components: { pre: "@/components/ui/code" } } }), - ], -}) -``` - -Every fenced code block in your MDX pages now renders through this component. For direct use, import the named `Code` export: - -```tsx -import { Code } from "@/components/ui/code" - -export function InstallSnippet() { - return -} -``` - -Key details: - -1. The default export is a drop-in `pre` replacement that reads the code text and `language-*` class from the MDX-generated `pre > code` element. -2. The named `Code` export takes `code`, an optional `lang`, and `className`. -3. Supported languages: `bash`, `css`, `html`, `json`, `markdown`, `mdx`, `ts`, `tsx`, `yaml`; anything else falls back to plain `text`. -4. Highlighting uses the github-light and github-dark themes and switches with your site's dark mode. -5. The copy button (`code-copy-button.tsx`, installed with this item) carries its own `client:load` directive, so it hydrates as its own island automatically — no directive needed where you use `Code`. diff --git a/apps/docs/src/pages/docs/components/gradient.mdx b/apps/docs/src/pages/docs/components/gradient.mdx deleted file mode 100644 index caee776..0000000 --- a/apps/docs/src/pages/docs/components/gradient.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: "doc" -title: "Gradient" -description: "Decorative gradient surface for hero and section backgrounds." ---- - -An absolutely positioned, mask-based gradient overlay that fades the `primary` color out from an edge or the center. Use it inside a `relative` container to give heroes and sections a soft background glow. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/gradient.json -``` - -## Usage - -```tsx -import { Gradient } from "@/components/ui/gradient" - -export function Hero() { - return ( -
- -
-

Build sites fast

-
-
- ) -} -``` - -Key details: - -1. `variant` is `radial` (default) or `linear`, implemented with CSS mask images. -2. `direction` sets the gradient origin: `left`, `right`, `top`, `bottom` (default), or `center`. -3. The element fills its nearest positioned ancestor (`absolute inset-0`), is `pointer-events-none`, and is hidden from assistive tech with `aria-hidden`. -4. It renders at 50% opacity with the `primary` background color; override both via `className`. -5. `gradientVariants` is exported for reusing the variant classes elsewhere. - -The component is purely decorative and static, so it needs no client directive. diff --git a/apps/docs/src/pages/docs/components/header.mdx b/apps/docs/src/pages/docs/components/header.mdx deleted file mode 100644 index 87b6b00..0000000 --- a/apps/docs/src/pages/docs/components/header.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -layout: "doc" -title: "Header" -description: "Site header with container and grouping primitives." ---- - -Structural primitives for a site header: the header bar itself, a centered container, and groups for clustering navigation, logos, and actions. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/header.json -``` - -## Usage - -```tsx -import { Header, HeaderContainer, HeaderGroup } from "@/components/ui/header" - -export function SiteHeader() { - return ( -
- - - Acme - - - Docs - Pricing - - -
- ) -} -``` - -Key parts: - -1. `Header` renders a `header` element with two variants: `default` (plain full-width bar) and `floating` (rounded, bordered, centered card that respects the `--container` CSS variable). -2. `HeaderContainer` centers content with a `max-w-7xl` container and horizontal padding. -3. `HeaderGroup` is a flex row for clustering items; use `className="ml-auto"` to push a group to the far side. -4. `headerVariants` is exported for reusing the variant classes elsewhere. - -All parts are static and render without a client directive; add one only if you place interactive islands inside. diff --git a/apps/docs/src/pages/docs/components/layout.mdx b/apps/docs/src/pages/docs/components/layout.mdx deleted file mode 100644 index f4fee52..0000000 --- a/apps/docs/src/pages/docs/components/layout.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -layout: "doc" -title: "Layout" -description: "Document primitives for Sitex layouts: html, head with SEO meta and JSON-LD, body, and main." ---- - -Document primitives for building Sitex layouts: `Layout` renders the `html` element, `LayoutHead` the `head` with SEO meta tags and JSON-LD, plus `LayoutBody` and `LayoutMain` for the page shell. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/layout.json -``` - -## Usage - -```tsx -import { - Layout, - LayoutBody, - LayoutHead, - LayoutMain, -} from "@/components/ui/layout" - -export default function BaseLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - - - {children} - - - ) -} -``` - -Key details: - -1. `Layout`, `LayoutBody`, and `LayoutMain` are thin wrappers over `html`, `body`, and `main` with base styling; all standard element props pass through. -2. `LayoutHead` turns props into meta tags: `title`, `description`, `name` (as `og:site_name`), `image` (string or object with `src`, `alt`, `width`, `height`, `type`), `openGraphType` (alias `type`), and `twitterCard` (`summary` or `summary_large_image`). -3. For `openGraphType="article"`, `publishedAt` and `updatedAt` emit `article:published_time` and `article:modified_time`. -4. `jsonLd` accepts a single `schema-dts` object (`Graph` or `WithContext`) or an array, serialized into a `script type="application/ld+json"` tag. -5. Children of `LayoutHead` render after the generated tags, so you can add extra `link` or `meta` elements. `LayoutHeadProps` and `LayoutJsonLd` types are exported. diff --git a/apps/docs/src/pages/docs/components/marquee.mdx b/apps/docs/src/pages/docs/components/marquee.mdx deleted file mode 100644 index 6f8d121..0000000 --- a/apps/docs/src/pages/docs/components/marquee.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -layout: "doc" -title: "Marquee" -description: "Continuously scrolling strip for logos and quotes." ---- - -A CSS-animated strip that scrolls its children in a continuous loop. Use it for logo walls, testimonial tickers, or any row of items you want to drift across the page. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/marquee.json -``` - -## Usage - -```tsx -import { Marquee } from "@/components/ui/marquee" - -export function LogoMarquee() { - return ( - - Acme - Globex - Initech - - ) -} -``` - -Key props: - -1. `direction` sets the scroll axis and heading: `"left"` (default), `"right"`, `"top"`, or `"bottom"`. Vertical directions stack the children in a column. -2. `duration` (or the legacy `time` prop, default `30`) controls one loop's length; a number is treated as seconds, a string is passed through as a CSS duration. -3. `gap` (default `"1rem"`) spaces the items and is also used to offset the loop seam. -4. `infinite` (default `true`) duplicates the children in an `aria-hidden` copy so the loop is seamless; set it to `false` to animate a single pass without duplication. -5. `pauseOnHover` pauses the animation while the pointer is over the marquee, and `variant` picks the edge treatment: `"gradient"` (default, fades the edges with a mask) or `"solid"`. - -The animation is pure CSS (it also respects `prefers-reduced-motion`), so no client directive is needed. `marqueeVariants` is exported for reusing the variant classes elsewhere. diff --git a/apps/docs/src/pages/docs/components/price.mdx b/apps/docs/src/pages/docs/components/price.mdx deleted file mode 100644 index d5f82ad..0000000 --- a/apps/docs/src/pages/docs/components/price.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -layout: "doc" -title: "Price" -description: "Locale-aware price display with sale and discount formatting." ---- - -A price display that formats amounts with `Intl.NumberFormat` and handles compare-at pricing with a strikethrough and a discount badge. Use it on product cards, pricing tables, and checkout summaries. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/price.json -``` - -## Usage - -```tsx -import { Price, PriceUnit, PriceValue } from "@/components/ui/price" - -export function ProductPrice() { - return ( - - - per month - - ) -} -``` - -Key parts: - -1. `Price` is a flex wrapper that lays out its children; `PriceUnit` renders a muted suffix such as a billing period. -2. `PriceValue` formats `price` and optional `compareAt`. Both accept a `PriceValueType`: a number, a numeric string, a range string like `"10 - 20"`, or a `[min, max]` tuple (ranges render as `"$10.00 - $20.00"`). It renders nothing when `price` is empty. -3. `currency` (default `"USD"`) and `locale` (default `"en-US"`) drive the currency formatting. -4. When `compareAt` is higher than `price`, the original price is shown struck through with a "Save …" badge; `discountFormat` switches the badge between `"percentage"` (default) and `"amount"`, and `showDiscount={false}` hides it. The `variant` prop styles the main value: `"default"` or `"sale"` (muted with line-through). -5. The helpers `formatPriceValue`, `formatPriceDiscount`, and `parseSinglePriceValue` are exported for formatting prices outside the component, along with `priceValueVariants`. diff --git a/apps/docs/src/pages/docs/components/rating.mdx b/apps/docs/src/pages/docs/components/rating.mdx deleted file mode 100644 index f34e4c1..0000000 --- a/apps/docs/src/pages/docs/components/rating.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -layout: "doc" -title: "Rating" -description: "Star rating display." ---- - -A read-only row of five stars that fills to match a numeric rating, including fractional values. Use it for review scores on product cards and testimonials. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/rating.json -``` - -## Usage - -```tsx -import { Rating } from "@/components/ui/rating" - -export function ReviewScore() { - return ( -

- - 4.5 out of 5 -

- ) -} -``` - -Key points: - -1. `Rating` is the only export; it renders a `div` and accepts all standard `div` props. -2. `value` (default `5`) sets how many of the five stars are filled; fractions like `4.5` render a partially filled star. -3. The stars scale with the surrounding font size (they are sized in `em`), so set `className="text-sm"` or similar to resize them; filled stars use the `primary` color and empty stars a muted track. - -It is purely presentational with no state or handlers, so no client directive is needed. diff --git a/apps/docs/src/pages/docs/components/section.mdx b/apps/docs/src/pages/docs/components/section.mdx deleted file mode 100644 index 4e773be..0000000 --- a/apps/docs/src/pages/docs/components/section.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -layout: "doc" -title: "Section" -description: "Page section with consistent vertical rhythm and container." ---- - -A `section` element with consistent vertical padding plus a centered container for its content. Use it as the outer shell of every page block so sections share the same rhythm and max width. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/section.json -``` - -## Usage - -```tsx -import { Section, SectionContainer } from "@/components/ui/section" - -export function FeatureSection() { - return ( -
- -

Features

-

Everything you need to ship a marketing site.

-
-
- ) -} -``` - -Key parts: - -1. `Section` renders a `section` with vertical padding, column flex layout, and a scroll margin so anchor links land cleanly below a fixed header. -2. The `variant` prop picks the shell: `"default"` (full-width on the page background) or `"floating"` (a centered, rounded, bordered card with a shadow, capped at the `--container` width). -3. `SectionContainer` centers the content, caps it at `max-w-7xl`, and adds horizontal padding and an 8-unit column gap. -4. `sectionVariants` is exported for reusing the variant classes elsewhere. - -Both parts are static wrappers around their children, so no client directive is needed. diff --git a/apps/docs/src/pages/docs/components/theme-toggle.mdx b/apps/docs/src/pages/docs/components/theme-toggle.mdx deleted file mode 100644 index 929a921..0000000 --- a/apps/docs/src/pages/docs/components/theme-toggle.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: "doc" -title: "Theme Toggle" -description: "Light/dark toggle button with a head script that prevents theme flash." ---- - -A sun/moon button that switches the `light` or `dark` class on the document root and remembers the choice in `localStorage`. A small head script applies the stored theme before first paint so pages never flash. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/theme-toggle.json -``` - -## Usage - -Render `ThemeScript` in the document head and `ThemeToggle` as an island wherever the button belongs. Give both the same options. - -```tsx -import { ThemeScript, ThemeToggle } from "@/components/ui/theme-toggle" - -const theme = { defaultTheme: "system", storageKey: "theme" } as const - -export default function Layout({ children }) { - return ( - - - - - -
- -
- {children} - - - ) -} -``` - -Key parts: - -1. `ThemeScript` renders an inline script that reads `storageKey` from `localStorage`, falls back to `defaultTheme`, and adds the resolved class to `document.documentElement`. Without it the theme is applied after hydration, which flashes. -2. `ThemeToggle` works on its own as an island. It renders with `defaultTheme` on the server and on the first client render, then reads the stored preference after mount, so hydration never mismatches. It flips between the resolved `light` and `dark` values, sets `aria-pressed` and `data-state`, and accepts all `button` props. -3. `defaultTheme` accepts `"light"`, `"dark"`, or `"system"` (default). `system` follows `prefers-color-scheme` and updates when the OS setting changes. `storageKey` defaults to `"theme"`. -4. `ThemeProvider` and `useTheme` share one theme state between several controls inside the same island. Context does not cross island boundaries, so the provider and its controls must hydrate together. -5. The `Theme` and `ThemeOptions` types are exported for typing your own code. diff --git a/apps/docs/src/pages/docs/components/tile.mdx b/apps/docs/src/pages/docs/components/tile.mdx deleted file mode 100644 index abc568f..0000000 --- a/apps/docs/src/pages/docs/components/tile.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -layout: "doc" -title: "Tile" -description: "Card-like tile and tile group for feature grids." ---- - -A card-like tile with a responsive group wrapper for laying out feature grids, link collections, or image galleries. Tiles can render as links, and the group supports grid and masonry layouts. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/tile.json -``` - -## Usage - -```tsx -import { Tile, TileContent, TileGroup } from "@/components/ui/tile" - -export function FeatureGrid() { - return ( - - - -

Pages

-

File-based routing for MDX and TSX pages.

-
-
- - -

Layouts

-

Shared wrappers with SEO baked in.

-
-
-
- ) -} -``` - -Key parts: - -1. `Tile` has four variants: `default` (transparent border), `inset` (negative horizontal margin), `outline` (bordered), and `muted` (filled background). Passing `href` renders an `a` instead of a `div` and enables a hover state. -2. `TileGroup` wraps tiles with `variant="default"` (auto-fit grid) or `variant="masonry"` (CSS columns), and `size` `sm`, `default`, or `lg` controlling the minimum column width (240px, 300px, or 400px). -3. `TileContent` adds the inner padding and vertical flex layout for text content. -4. `tileVariants` and `tileGroupVariants` are exported for reusing the variant classes elsewhere. diff --git a/apps/docs/src/pages/docs/components/toc.mdx b/apps/docs/src/pages/docs/components/toc.mdx deleted file mode 100644 index 2c9c9fe..0000000 --- a/apps/docs/src/pages/docs/components/toc.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -layout: "doc" -title: "Toc" -description: "Table of contents navigation for page headings." ---- - -A table of contents navigation for the headings on the current page. Use it in a doc layout sidebar so readers can jump to any section. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/toc.json -``` - -## Usage - -Sitex passes the extracted MDX headings to layouts via the `headings` prop, so a doc layout can map them straight into the menu: - -```tsx -import type { LayoutProps } from "@fulldotdev/sitex" - -import { - Toc, - TocMenu, - TocMenuItem, - TocMenuLink, - TocTitle, -} from "@/components/ui/toc" - -export default function DocLayout({ headings }: LayoutProps) { - return ( - - On this page - - {headings?.map((heading) => ( - - - {heading.label} - - - ))} - - - ) -} -``` - -Key parts: - -1. `Toc` renders the outer `nav`, `TocTitle` the heading (a `p` by default, changeable via `as`), and `TocMenu`/`TocMenuItem` the `ul`/`li` structure. -2. `TocMenuLink` indents by `depth` (default `2`; `3` and deeper get increasing left padding), matching the `depth` of each Sitex heading. -3. Pass `isActive` (or `active`) to `TocMenuLink` to highlight the current section; it sets `data-active` and `aria-current="location"`. diff --git a/apps/docs/src/pages/docs/components/typography.mdx b/apps/docs/src/pages/docs/components/typography.mdx deleted file mode 100644 index d550619..0000000 --- a/apps/docs/src/pages/docs/components/typography.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -layout: "doc" -title: "Typography" -description: "Prose typography primitives for MDX content." ---- - -Styled typography primitives for headings, paragraphs, lists, tables, and inline elements. Use them as MDX component overrides or directly wherever you render prose. - -## Install - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/typography.json -``` - -## Usage - -```tsx -import { - TypographyH1, - TypographyInlineCode, - TypographyLead, - TypographyList, - TypographyListItem, - TypographyP, -} from "@/components/ui/typography" - -export function Intro() { - return ( - <> - Getting started - Build static sites with MDX and React. - - Install with pnpm add and - create your first page. - - - Install the package. - Add a page. - - - ) -} -``` - -Key parts: - -1. Headings: `TypographyH1` through `TypographyH4`, each with a `size` prop (`sm`, `default`, `lg`) that steps the font size up or down one level. -2. Text: `TypographyP`, `TypographyLead` (larger muted intro text), `TypographyBlockquote`, `TypographyA` (underlined link), and `TypographyInlineCode`. -3. Lists: `TypographyList` renders a `ul` by default or an `ol` via `as="ol"`; use `TypographyListItem` for items. -4. Tables: `TypographyTable` (wrapped in a scrollable bordered container, styleable via `containerClassName`) with `TypographyTableRow`, `TypographyTableHead`, and `TypographyTableCell`; the cells respect the `align` attribute. -5. `Typography` is a generic wrapper (`div` by default, changeable via `as`) with a `size` prop, useful as a prose container. diff --git a/apps/docs/src/pages/docs/layouts.mdx b/apps/docs/src/pages/docs/layouts.mdx index a19646f..c708a76 100644 --- a/apps/docs/src/pages/docs/layouts.mdx +++ b/apps/docs/src/pages/docs/layouts.mdx @@ -67,15 +67,11 @@ The layout owns the ``. After rendering, Sitex adds only what is missing: 4. robots `noindex` when the page sets `noindex: true` 5. favicon link and generator meta -For JSON-LD, render a `script type="application/ld+json"` anywhere in the layout — Sitex hoists it into the head. The `layout` component from [pre-built components](/docs/components) wires title, description, Open Graph, Twitter, and JSON-LD props for you. +For JSON-LD, render a `script type="application/ld+json"` anywhere in the layout; Sitex hoists it into the head. The [Layout component](https://ui.full.dev/react/components/layout/) from Fulldev UI's React components wires title, description, canonical, Open Graph, and Twitter props for you. ## MDX Components -Override Markdown elements globally in the plugin config, or per layout with an `mdxComponents` export. Layout entries win. Use this for elements that need more than CSS — the main case is code blocks with syntax highlighting and a copy button, which the registry ships as the [`code` component](/docs/components/code): - -```bash -pnpm dlx shadcn@latest add https://sitex.full.dev/r/code.json -``` +Override Markdown elements globally in the plugin config, or per layout with an `mdxComponents` export. Layout entries win. Use this for elements that need more than CSS, such as code blocks with syntax highlighting and a copy button: ```ts // vite.config.ts diff --git a/apps/docs/src/pages/docs/rendering.mdx b/apps/docs/src/pages/docs/rendering.mdx new file mode 100644 index 0000000..ffba0e3 --- /dev/null +++ b/apps/docs/src/pages/docs/rendering.mdx @@ -0,0 +1,31 @@ +--- +layout: "doc" +title: "Rendering" +description: "Static rendering by default, client directives for islands, and prefetched navigation." +--- + +Components are plain React. Everything renders to static HTML at build time, and Sitex follows each page's module graph to include exactly the CSS that page imports. Pages ship zero JavaScript unless a component opts in. For ready-made components, such as sections, typography and a table of contents, see [Fulldev UI's React components](https://ui.full.dev/react/). + +## Client Directives + +Imported React components opt into browser rendering with a `client:*` directive. Directives live in TSX (layouts, blocks, components), never in MDX bodies, and never on HTML elements or same-file components. + +```tsx +import Search from "@/components/search" + +export default function Header() { + return +} +``` + +1. `client:load` hydrates immediately. For controls that must work right away. +2. `client:idle` hydrates when the browser is idle. For page chrome like sidebars. +3. `client:visible` hydrates when scrolled into view. For below-the-fold widgets. +4. `client:media="(min-width: 64rem)"` hydrates when the media query matches. +5. `client:only` skips static rendering and renders in the browser only. + +Island props must be JSON-serializable: functions, symbols, React elements, and cyclic values throw at build time. Children pass through as static HTML; a nested `client:*` component inside them becomes its own independent island. Components that share client state belong inside one island root. + +## Prefetch + +Navigation is prefetched by default: Sitex injects speculation rules so supporting browsers prerender links on hover (instant navigation), plus a small hover-prefetch script for the rest. Disable it with `sitex({ prefetch: false })`, or per link tree with a `data-no-prefetch` attribute. diff --git a/apps/docs/vite.config.ts b/apps/docs/vite.config.ts index bf1789d..c8c59a2 100644 --- a/apps/docs/vite.config.ts +++ b/apps/docs/vite.config.ts @@ -1,6 +1,6 @@ import type { Plugin } from "vite-plus" -import { readFile } from "node:fs/promises" +import { glob, readFile } from "node:fs/promises" import path from "node:path" import { defineConfig } from "vite-plus" @@ -9,7 +9,9 @@ import { parse } from "yaml" import tailwindcss from "@tailwindcss/vite" import react from "@vitejs/plugin-react" +import { pageFileToRoutePath } from "../../packages/sitex/src/router/runtime" import { sitex } from "../../packages/sitex/src/vite/plugin" +import { markdownPath } from "./src/lib/markdown" const siteUrl = "https://sitex.full.dev" @@ -27,13 +29,23 @@ export default defineConfig({ }, }), tailwindcss(), - llmsTxt(), + markdown(), ], }) -type Frontmatter = { title: string; description: string } +type Frontmatter = { + title: string + description: string + hero?: { + title: string + description: string + buttons?: { href: string; label: string }[] + } + features?: { title: string; description: string }[] +} type Globals = { + name: string sidebar: { sections: { navigation: { label: string; links: { href: string }[] }[] @@ -41,12 +53,18 @@ type Globals = { } } -/** Builds /llms.txt from the docs navigation and each page's frontmatter. */ -function llmsTxt(): Plugin { +/** + * Builds each page's Markdown version and /llms.txt. A page's MDX file is + * served as written at its path with .md (src/lib/markdown.ts); the home + * layout renders its hero and features from the frontmatter, so they follow + * the file as Markdown. /llms.txt lists the docs navigation with links to + * the Markdown versions. + */ +function markdown(): Plugin { let root = "" return { - name: "docs:llms-txt", + name: "docs:markdown", apply: "build", applyToEnvironment: (environment) => environment.name === "client", configResolved(config) { @@ -54,30 +72,55 @@ function llmsTxt(): Plugin { }, async generateBundle() { const read = (file: string) => readFile(path.join(root, file), "utf8") - const readPage = async (href: string) => { - const base = path.join("src/pages", href === "/" ? "index" : href) - const source = await read(`${base}.mdx`).catch(() => - read(path.join(base, "index.mdx")) - ) - const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source)?.[1] - - return parse(frontmatter ?? "") as Frontmatter + const pages = new Map() + + for await (const file of glob("src/pages/**/*.mdx", { cwd: root })) { + const source = await read(file) + const page = parse( + /^---\r?\n([\s\S]*?)\r?\n---/.exec(source)?.[1] ?? "" + ) as Frontmatter + const href = pageFileToRoutePath(file) + const rendered = [ + page.hero && `# ${page.hero.title}`, + page.hero?.description, + ...(page.hero?.buttons ?? []).map( + (button) => `[${button.label}](${new URL(button.href, siteUrl)})` + ), + ...(page.features ?? []).map( + (feature) => `## ${feature.title}\n\n${feature.description}` + ), + ].filter(Boolean) + + pages.set(href, page) + this.emitFile({ + type: "asset", + fileName: markdownPath(href).slice(1), + source: rendered.length + ? `${source.trimEnd()}\n\n${rendered.join("\n\n")}\n` + : source, + }) } - const home = await readPage("/") const globals = parse(await read("src/globals/index.yaml")) as Globals const groups = globals.sidebar.sections.flatMap( (section) => section.navigation ) - const lines = [`# ${home.title}`, "", `> ${home.description}`] + const lines = [ + `# ${globals.name}`, + "", + `> ${pages.get("/")?.description}`, + "", + `Every page has a Markdown version: add \`.md\` to its URL. The homepage's is ${siteUrl}/index.md.`, + ] for (const group of groups) { lines.push("", `## ${group.label}`, "") for (const { href } of group.links) { - const page = await readPage(href) + const page = pages.get(href) + if (!page) throw new Error(`No page for navigation link ${href}`) lines.push( - `- [${page.title}](${siteUrl}${href}): ${page.description}` + `- [${page.title}](${siteUrl}${markdownPath(href)}): ${page.description}` ) } } diff --git a/netlify.toml b/netlify.toml index 586c8a9..51e3c15 100644 --- a/netlify.toml +++ b/netlify.toml @@ -45,32 +45,89 @@ status = 301 [[redirects]] from = "/docs/rendering-and-assets" -to = "/docs/components" +to = "/docs/rendering" status = 301 [[redirects]] from = "/docs/island-rendering" -to = "/docs/components" +to = "/docs/rendering" status = 301 +# The component docs moved to Fulldev UI's React components: each page to +# its match there, the rest to the start of the React docs. [[redirects]] -from = "/docs/rendering" -to = "/docs/components" +from = "/docs/components" +to = "https://ui.full.dev/react/components/" +status = 301 + +[[redirects]] +from = "/docs/components/banner" +to = "https://ui.full.dev/react/components/banner/" +status = 301 + +[[redirects]] +from = "/docs/components/header" +to = "https://ui.full.dev/react/components/header/" +status = 301 + +[[redirects]] +from = "/docs/components/layout" +to = "https://ui.full.dev/react/components/layout/" +status = 301 + +[[redirects]] +from = "/docs/components/marquee" +to = "https://ui.full.dev/react/components/marquee/" +status = 301 + +[[redirects]] +from = "/docs/components/price" +to = "https://ui.full.dev/react/components/price/" +status = 301 + +[[redirects]] +from = "/docs/components/rating" +to = "https://ui.full.dev/react/components/rating/" +status = 301 + +[[redirects]] +from = "/docs/components/section" +to = "https://ui.full.dev/react/components/section/" +status = 301 + +[[redirects]] +from = "/docs/components/theme-toggle" +to = "https://ui.full.dev/react/components/theme-toggle/" +status = 301 + +[[redirects]] +from = "/docs/components/toc" +to = "https://ui.full.dev/react/components/toc/" +status = 301 + +[[redirects]] +from = "/docs/components/typography" +to = "https://ui.full.dev/react/components/typography/" +status = 301 + +[[redirects]] +from = "/docs/components/*" +to = "https://ui.full.dev/react/" status = 301 [[redirects]] from = "/docs/ui" -to = "/docs/components" +to = "https://ui.full.dev/react/" status = 301 [[redirects]] from = "/docs/ui/installation" -to = "/docs/components" +to = "https://ui.full.dev/react/docs/installation/" status = 301 [[redirects]] from = "/docs/ui/components" -to = "/docs/components" +to = "https://ui.full.dev/react/components/" status = 301 [[redirects]] @@ -88,5 +145,17 @@ X-Content-Type-Options = "nosniff" Referrer-Policy = "strict-origin-when-cross-origin" Content-Security-Policy = "frame-ancestors 'self'" +# Build files have a hash in their name, so browsers can keep them. +[[headers]] +for = "/assets/*" +[headers.values] +Cache-Control = "public, max-age=31536000, immutable" + +# The Markdown version of every page is for agents, not for search results. +[[headers]] +for = "/*.md" +[headers.values] +X-Robots-Tag = "noindex" + [context.deploy-preview] ignore = "false" diff --git a/packages/sitex/README.md b/packages/sitex/README.md index 99b770c..ae18af7 100644 --- a/packages/sitex/README.md +++ b/packages/sitex/README.md @@ -86,7 +86,7 @@ description: "My first Sitex page." Hello from Sitex. ``` -Sitex adds the missing head defaults (charset, viewport, canonical, `og:url`, favicon) and hoists JSON-LD into the head. `@fulldotdev/sitex` itself exports types only; ready-made layout and content components install from the Sitex shadcn registry at https://sitex.full.dev/docs/components. +Sitex adds the missing head defaults (charset, viewport, canonical, `og:url`, favicon) and hoists JSON-LD into the head. `@fulldotdev/sitex` itself exports types only; ready-made layout and content components come from Fulldev UI's React components at https://ui.full.dev/react/. ## Globals