docs(site): freshen up site README and CLAUDE

This commit is contained in:
Darius Cepulis
2026-02-10 14:37:04 -06:00
parent 77eeeef701
commit 2b59b1cabb
3 changed files with 191 additions and 78 deletions
+106 -12
View File
@@ -16,7 +16,6 @@ From `site/` directory:
| -------------------- | ---------------------------------------------------- |
| `pnpm dev` | Start dev server at `localhost:4321` |
| `pnpm build` | Build production site to `./dist/` |
| `pnpm preview` | Preview production build locally |
| `pnpm api-docs` | Regenerate API reference JSON files |
| `pnpm test` | Run all tests once |
| `pnpm test:watch` | Run tests in watch mode |
@@ -164,7 +163,9 @@ site/
│ └── api-docs-builder/ # Generates API reference from TypeScript
├── public/ # Static assets (served untransformed)
├── integrations/ # Custom Astro integrations
── pagefind.ts # Pagefind search integration
── pagefind.ts # Pagefind search integration
│ ├── llms-markdown.ts # LLM-optimized markdown generation
│ └── check-v8-urls.ts # v8 URL migration audit
├── astro.config.mjs # Astro configuration
├── tsconfig.json # TypeScript config with path aliases
└── vitest.config.ts # Test configuration
@@ -367,10 +368,9 @@ export const sidebar: Sidebar = [
- `getSectionsForGuide()`: Get breadcrumb trail to a guide
**`routing.ts`** — URL building and redirect logic:
- `buildDocsUrl()`: Construct docs URLs from framework/style/slug
- `buildDocsUrl()`: Construct docs URLs from framework and slug
- `resolveIndexRedirect()`: Intelligent redirect for index pages
- Handles user preferences from localStorage
- Validates framework/style combinations
- Validates framework from URL params or cookie preferences
- Falls back to defaults when invalid
### Docs Routing Pattern
@@ -415,8 +415,9 @@ All content must be written in **MDX format** to support:
{
title: string;
description: string;
pubDate: Date; // From filename or git history
pubDate: Date; // From filename date prefix
authors: string[]; // Reference to authors.json
canonical?: string; // Canonical URL override
devOnly?: boolean; // Show only in development
}
```
@@ -447,9 +448,17 @@ All content must be written in **MDX format** to support:
{
[key: string]: {
name: string;
shortName: string;
bio?: string;
avatar?: string;
socialLinks?: { platform: string; url: string }[];
socialLinks?: {
x?: string;
bluesky?: string;
mastodon?: string;
github?: string;
linkedin?: string;
website?: string;
};
}
}
```
@@ -457,7 +466,7 @@ All content must be written in **MDX format** to support:
### Git Integration
`src/utils/gitService.ts` uses `simple-git` to enrich content with metadata:
- Blog posts: `pubDate` from filename or first commit
- Blog posts: `pubDate` from filename date prefix
- All content: `updatedDate` from last modification
## State Management with Nanostores
@@ -468,6 +477,7 @@ All content must be written in **MDX format** to support:
- `preferences.ts`: User framework/style preferences (persisted to localStorage)
- `homePageDemos.ts`: Home page demo state
- `tabs.ts`: Tab component state
- `installation.ts`: Installation page state (renderer, skin, install method)
**Usage pattern:**
```ts
@@ -491,8 +501,9 @@ function MyComponent() {
setupFiles: ['./src/test-setup.ts'], // Imports @testing-library/jest-dom
coverage: {
provider: 'v8',
include: ['src/utils/**', 'src/components/**', 'src/types/**'],
exclude: ['**/*.test.ts', '**/*.spec.ts', '**/__tests__/**'],
reporter: ['text', 'json', 'html'],
include: ['src/utils/**', 'src/components/**', 'src/types/**', 'scripts/api-docs-builder/src/**'],
exclude: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx', '**/test/**'],
},
}
```
@@ -531,6 +542,7 @@ vi.mock('@/types/docs', async () => {
- **[Astro 5.14.4](https://astro.build)**: Static site generation with island architecture
- **[React 18](https://react.dev)**: Client-side interactive components (`client:load`)
- **[React Compiler](https://react.dev/learn/react-compiler)**: Enabled via `babel-plugin-react-compiler` targeting React 18
- **[Tailwind v4](https://tailwindcss.com)**: CSS utility classes via `@tailwindcss/vite`
- **[Nanostores 1.0.1](https://github.com/nanostores/nanostores)**: Cross-island state
- **[Base UI 1.0.0-beta.4](https://base-ui.com)**: Headless accessible components
@@ -618,6 +630,88 @@ export default defineConfig({
});
```
## Environment Variables
The site uses OAuth for authentication and Mux for video management. Required variables are needed for auth features to work; the site degrades gracefully without them.
**Required for authentication:**
| Variable | Purpose |
| --- | --- |
| `OAUTH_CLIENT_ID` | OAuth client ID |
| `OAUTH_CLIENT_SECRET` | OAuth client secret |
| `OAUTH_REDIRECT_URI` | OAuth callback URL |
| `OAUTH_URL` | OAuth provider base URL |
| `SESSION_COOKIE_PASSWORD` | Encryption key for `iron-session` cookies |
**Optional:**
| Variable | Purpose |
| --- | --- |
| `MUX_API_URL` | Override Mux API endpoint (defaults to `https://api.mux.com`) |
| `SENTRY_AUTH_TOKEN` | Sentry error tracking auth token |
## Authentication & Mux Integration
OAuth and Mux integration exist to support the **video uploader** on the installation page (`src/components/installation/MuxUploaderPanel.tsx`). This is the only consumer of the auth system.
**Key files:**
- `src/middleware/index.ts` — Validates and refreshes OAuth sessions on every request via `iron-session`
- `src/utils/auth.ts` — Session encryption, JWKS verification, token refresh
- `src/pages/api/auth/callback.ts` — OAuth callback endpoint
- `src/actions/auth.ts``initiateLogin()`, `logout()` server actions
- `src/actions/mux.ts``createDirectUpload()`, `getUploadStatus()`, `getAssetStatus()`, `listAssets()`, `getAsset()` server actions
**How sessions work:**
1. Middleware decrypts session cookie, verifies access token via JWKS
2. If expired, automatically refreshes using the refresh token
3. Populates `context.locals.user` (safe to render) and `context.locals.accessToken` (server-only, never expose to client)
4. Invalid/corrupted sessions are silently cleared
**Action gating:** All `mux.*` actions (except `createDirectUpload`) return 401 without a valid session. `createDirectUpload` handles its own auth so the client can detect UNAUTHORIZED and show a login UI.
## MDX Processing Plugins
Four plugins transform MDX content during build. Registered in `astro.config.mjs`:
**`remarkConditionalHeadings`** (`src/utils/remarkConditionalHeadings.js`)
Walks the MDX AST and tracks headings inside `<FrameworkCase>` / `<StyleCase>` components, attaching conditional metadata (which frameworks/styles a heading belongs to). Also reads `<ApiReference>` component props, loads the generated JSON, and injects heading entries so API reference sections appear in the table of contents. Outputs to `frontmatter.conditionalHeadings`.
**`remarkReadingTime`** (`src/utils/remarkReadingTime.mjs`)
Calculates reading time and injects `frontmatter.minutesRead` (text) and `frontmatter.readingTimeMinutes` (number).
**`rehypePrepareCodeBlocks`** (`src/utils/rehypePrepareCodeBlocks.js`)
Tags `<code>` children of `<pre>` with a `codeBlock` property, and marks `<pre>` blocks with `hasFrame: true` when inside a `<TabsPanel>` JSX component. This controls code block styling (framed vs. standalone).
**`shikiTransformMetadata`** (`src/utils/shikiTransformMetadata.js`)
Shiki transformer that extracts `title="..."` from code fence metadata, enabling titled code blocks:
~~~markdown
```tsx title="Example.tsx"
~~~
## Custom Astro Integration: LLM Markdown
**Location:** `integrations/llms-markdown.ts`
Generates LLM-optimized markdown files and a `llms.txt` index after build.
**How it works:**
1. Scans all built HTML pages for elements with `[data-llms-content]`
2. Strips elements with `[data-llms-ignore]` from the content
3. Converts remaining HTML to markdown via Turndown
4. Writes `.md` files alongside built HTML
5. Generates `llms.txt` index grouped by framework/style
**Data attributes for content authors:**
| Attribute | Purpose |
| --- | --- |
| `data-llms-content` | Mark an element's content for LLM markdown extraction |
| `data-llms-ignore` | Exclude an element (and its children) from LLM output |
| `data-llms-description` | Description text for the `llms.txt` index entry |
| `data-llms-sort` | Sort key for ordering entries in the index |
## TypeScript Configuration
**Path aliases** (`tsconfig.json`):
@@ -727,10 +821,10 @@ Standard MDX elements (headings, paragraphs, lists, etc.) are defined here and u
**Usage in layouts:**
```astro
---
import { components } from '@/components/typography';
import defaultMarkdownComponents from '@/components/typography/defaultMarkdownComponents';
---
<slot Components={components} />
<Content components={{ ...defaultMarkdownComponents }} />
```
## Important Development Notes
+85 -55
View File
@@ -4,30 +4,40 @@ For docs, blog, and more: [v10.videojs.org](https://v10.videojs.org).
Mostly a standard [Astro](https://astro.build/) project.
> [!NOTE]
> This README serves as a high-level introduction to the site. For detailed technical documentation — architecture, conventions, patterns, quirks, features — see [CLAUDE.md](CLAUDE.md). It may be written for Claude, but it's useful for humans, too ;)
## Project Structure
```text
├── public/ # assets served, un-transformed, as v10.videojs.org/[filename]
├── public/ # Static assets served as v10.videojs.org/[filename]
├── scripts/
│ └── api-docs-builder/ # Generates API reference JSON from TypeScript sources
├── integrations/ # Custom Astro integrations (pagefind, llms-markdown, etc.)
├── src/
│ ├── assets/ # assets that might be imported into components, pages, etc.
│ ├── assets/ # Assets imported into components, pages, etc.
│ ├── components/
├── content/ # MDX goes here
├── examples/ # temporary, until we figure out how to generate component docs
├── layouts/ # Astro components that are typically used to wrap pages
│ ├── pages/ # where Astro looks to generate routes
├── stores/ # we communicate between components with nanostores
│ ├── styles/
│ └── docs/
│ ├── api-reference/ # API reference Astro components
│ └── demos/ # Interactive component demos
│ ├── content/ # Content collections (blog/, docs/, authors.json)
│ └── generated-api-reference/ # Generated JSON (gitignored)
│ ├── examples/ # Temporary, until folded into component docs
│ ├── layouts/ # Astro layout components
│ ├── pages/ # File-based routing
│ ├── stores/ # Nanostores for cross-island client-side state
│ ├── styles/ # Global CSS and Tailwind config
│ ├── types/
│ ├── utils/
│ ├── consts.ts # stuff a lot of components, utils, pages, etc. use
│ ├── content.config.ts # [read more](https://docs.astro.build/en/guides/content-collections/)
│ ├── docs.config.ts # Where we define the docs sidebar
│ └── test-setup.ts # for vitest
│ ├── consts.ts # Site-wide constants
│ ├── content.config.ts # Content collection schemas
│ ├── docs.config.ts # Docs sidebar structure
│ └── test-setup.ts # Vitest setup
├── astro.config.mjs
├── CLAUDE.md
├── CLAUDE.md # Detailed technical docs for this site
├── package.json
├── README.md
├── TODO.md # not comprehensive. Should be turned into issues, eventually.
├── TODO.md
├── tsconfig.json
└── vitest.config.ts
```
@@ -49,26 +59,35 @@ If you're in `site/`...
| `pnpm dev` | Starts local dev server at `localhost:4321` |
| `pnpm build` | Build your production site to `./dist/` |
| `pnpm preview` | Preview your build locally, before deploying |
| `pnpm api-docs` | Regenerate API reference JSON from TypeScript |
| `pnpm astro ...` | Run CLI commands like `astro add`, `astro check` |
| `pnpm test` | Run tests with Vitest |
| `pnpm test:watch` | Run tests in watch mode |
| `pnpm test:ui` | Run Vitest with its web-based UI |
| `pnpm test:coverage` | Generate test coverage report |
## Environment Variables
The installation page's video uploader uses OAuth + Mux. See [CLAUDE.md](CLAUDE.md) for the full list of environment variables. The site works without these — the uploader just won't be available.
## Technology Stack
Here are most of the technologies you should get to know when you're building this site:
Here are some of the technologies you should get to know when you're building this site:
- [**Astro**](https://astro.build) - Mostly-static site generation with [island architecture](https://docs.astro.build/en/concepts/islands/)
- [**React**](https://react.dev) - Most of our client-side interactivity is built with React components (each with `client:*` is an isolated React root)
- [**Tailwind v4**](https://tailwindcss.com) - CSS utility class generator
- [**Tailwind v4**](https://tailwindcss.com) - CSS utility class generator. We use custom tokens — see [globals.css](styles/globals.css) before reaching for standard Tailwind classes. We also have a few patterns we try to stick to — see [CLAUDE.md](CLAUDE.md) for details.
- [**clsx**](https://github.com/lukeed/clsx) - Class name concatenation (in React; Astro has `class:list`)
- [**React**](https://react.dev) - Most of our client-side interactivity is built with React components. **React Compiler is enabled**.
- [**Nanostores**](https://github.com/nanostores/nanostores) - Shared client-side state (React Context doesn't work across islands)
- [**Base UI**](https://base-ui.com) - Headless accessible components
- [**Pagefind**](https://pagefind.app) - Static search with build-time indexing
- [**Shiki**](https://shiki.style) - Syntax highlighting
- [**Vitest**](https://vitest.dev) - Testing framework
## Content
### The blog
We have three-ish main types of content on the site. The blog, docs guides, and docs references. Each of these is created and rendered in a slightly different way.
### Blog
Let's start with the blog because it's more simple.
@@ -79,65 +98,76 @@ Let's start with the blog because it's more simple.
The only weird thing about the blog? Blog posts use date-prefixed filenames: `YYYY-MM-DD-slug.mdx`. For example: `2024-01-15-new-release.mdx`. The date prefix is removed by [utils/globWithParser.ts](src/utils/globWithParser.ts) during content collection transformation, so the post's slug just becomes `new-release` (and its url, `/blog/new-release/`).
### The docs
### Guides
#### How to add a docs page
You'll learn most of what you need to know about writing guides by reading [`src/content/docs/how-to/write-guides.mdx`](src/content/docs/how-to/write-guides.mdx).
Just looking to add a doc and don't really care about the implementation?
High-level primer?
Check out [`src/content/docs/docs/how-to/write-docs.mdx`](src/content/docs/docs/how-to/write-docs.mdx).
- Guides are written in MDX and stored in `src/content/docs/`
- Guides are separated into how-to guides (focused on an outcome) and concept guides (focused on understanding) according to the [Diataxis](https://diataxis.fr) framework.
- Astro's [Content Collections API](https://docs.astro.build/en/guides/content-collections/) transforms the MDX into data
- That data is rendered in `src/pages/docs/framework/[framework]/[...slug].astro`
- Standard MDX typography is defined in `src/components/typography/`
Still interested in implementation? Ok, let's dive in:
It's also worth pausing and explaining one big quirk of our docs...
#### Docs are generated for multiple frameworks and styles
#### Guides are generated for multiple frameworks
We want docs to feel idiomatic, no matter your framework or styling preference. React users shouldn't have to learn about Web Components, HTML users shouldn't have to understand React Hooks, and so on.
We currently support two frameworks (HTML, React) and one styling approach (CSS). This is defined in [types/docs.ts](src/types/docs.ts).
Every doc is generated for every framework / style combination. E.g., `how-to/installation.mdx` becomes:
Every doc generates a route per framework. E.g., `how-to/installation.mdx` becomes:
- `/docs/framework/html/style/css/how-to/installation/`
- `/docs/framework/react/style/css/how-to/installation/`
- `/docs/framework/html/how-to/installation/`
- `/docs/framework/react/how-to/installation/`
Content that applies to only certain frameworks or styles can be restricted in two ways:
1. Within the MDX content itself, by wrapping framework- or style-specific content in `<FrameworkCase>` or `<StyleCase>` components. (Read more about these components in [`src/content/docs/docs/how-to/write-docs.mdx`](src/content/docs/docs/how-to/write-docs.mdx).)
2. In the sidebar config ([docs.config.ts](src/docs.config.ts)), by specifying `frameworks` and/or `styles` on a per-guide basis, e.g.,
1. Within the MDX content itself, by wrapping framework- or style-specific content in `<FrameworkCase>` or `<StyleCase>` components. (Read more about these components in [`src/content/docs/how-to/write-guides.mdx`](src/content/docs/how-to/write-guides.mdx).)
2. In the sidebar config ([docs.config.ts](src/docs.config.ts)), by specifying `frameworks` on a per-guide basis, e.g.,
```ts
const sidebar = {
title: 'React Concepts',
guides: [
{ slug: 'concepts/hooks' }, // Available to all
{
slug: 'concepts/styling',
frameworks: ['react'], // Only for React
styles: ['styled-components'] // Only for styled-components
}
]
};
const sidebar: Sidebar = [
{
sidebarLabel: "Getting started",
contents: [
{ slug: "how-to/installation" }, // Available to all
{
slug: "how-to/react-hooks",
frameworks: ["react"], // Only for React
},
],
},
];
```
### Guides
### References
The docs consist of two parts:
API reference pages are generated from TypeScript source code by the builder in [`scripts/api-docs-builder/`](scripts/api-docs-builder/). It extracts props, state, data attributes, and part information, then outputs JSON to `src/content/generated-api-reference/` (gitignored).
1. Guides, written in [MDX](https://mdxjs.com)
2. 🚧 Not yet built 🚧 References, generated from source code
The JSON is regenerated automatically on `pnpm dev` and `pnpm build`, or manually via `pnpm api-docs`.
Let's talk about guides, first.
See [`scripts/api-docs-builder/README.md`](scripts/api-docs-builder/README.md) for full documentation.
You'll learn most of what you need to know about writing guides by reading [`src/content/docs/docs/how-to/write-docs.mdx`](src/content/docs/docs/how-to/write-docs.mdx).
## MDX Plugins
High-level primer?
MDX content is transformed by custom remark/rehype plugins during build:
- Guides are written in MDX and stored in `src/content/docs/`
- Guides are separated into how-to guides (focused on an outcome) and concept guides (focused on understanding) according to the [Diátaxis](https://diataxis.fr) framework.
- Astro's [Content Collections API](https://docs.astro.build/en/guides/content-collections/) transforms the MDX into data
- That data is rendered in `src/pages/docs/framework/[framework]/style/[style]/[...slug].astro`
- Standard MDX typography is defined in `src/components/typography/`
- **remarkConditionalHeadings** — Tracks which headings are inside `<FrameworkCase>` / `<StyleCase>` so the table of contents only shows headings relevant to the active framework and style. Also injects API reference headings into the TOC.
- **remarkReadingTime** — Calculates and injects reading time metadata
- **rehypePrepareCodeBlocks** — Prepares code blocks for styled rendering in tabs
- **shikiTransformMetadata** — Enables `title="..."` on code fences
### Generated references
See [CLAUDE.md](CLAUDE.md) for details on each plugin.
🚧 Under construction 🚧
## Custom Integrations
Three custom Astro integrations in `integrations/`:
- **pagefind** — Indexes HTML after build for static search; serves previous index in dev
- **llms-markdown** — Generates LLM-optimized `.md` files and `llms.txt` index from `[data-llms-content]` elements
- **check-v8-urls** — Audits Video.js v8 URL migration coverage at build time
See [CLAUDE.md](CLAUDE.md) for implementation details.
-11
View File
@@ -1,11 +0,0 @@
# TODO
not comprehensive. just the things I'm worried about forgetting
- an seo strategy; after all one page will have a bunch of lang/style combos. One canonical? Good meta/description tags? Idk...
- a kick-ass 404 page
- a11y audit. some of those colors are sus.
- animations
- docs features
- /docs/[slug] redirect ("share this guide")
- update READMEs following dev