@clean-jsdoc-theme/rang
@clean-jsdoc-theme/rang is the Preact component library that dwar server-renders and bundles. It owns every byte of page-shell HTML — the Layout, Header, and Footer — the hydratable islands that add progressive enhancement, the MDX element → component map, and the shadcn-style primitives styled with Tailwind utilities that reference CSS variables.
Why the name? rang (रंग) is Hindi/Sanskrit for color — apt for the package that owns the theme's entire visual surface: layout, components, and styling.
The package depends only on the browser-safe contract package (@clean-jsdoc-theme/utils) plus preact, class-variance-authority, clsx, tailwind-merge, and lucide-preact — see package.json.
If you just want to use the theme, you never install this package. It's an internal building block that dwar bundles for you. See the Packages section for the pieces you actually install.
Why it's a separate package
The pipeline is deliberately split: setu produces a SiteManifest, dwar renders it, and all the markup lives in rang. Pulling the component layer into its own package buys the project a clean seam:
- rang owns the markup; dwar owns the orchestration. dwar's
layout.tsxadds no chrome of its own — itsSsrLayoutcomposes rang'sLayoutthrough itsheaderControls/sidebar/toc/tocMobileslots and does nothing but wrap each interactive component in a<div data-island="…">hydration marker. As that file's own doc comment puts it: "The chrome markup (header, grid shell, asides, footer) lives entirely in rang'sLayout. dwar's only job here is hydration." - Browser-safe by construction. Components run in the browser, so rang only imports the node-free
utilscontract — never the build side. The same components render to a string on the server (preact-render-to-string) and hydrate in the browser. - One styling convention. Components style themselves with Tailwind utility classes that reference CSS variables (e.g.
bg-background,text-(--clean-fg),border-(--clean-border)). dwar plumbs the theme tokens into those variables on:root, so the same markup re-themes without a recompile. Seelib/cn.ts— the shadcncn()helper that composesclsx+tailwind-mergeso a caller-supplied class always wins a Tailwind conflict.
Chrome vs. islands
This is the central distinction in rang.
Chrome is pure, static SSR markup with no client JavaScript. The Layout shell — header + a 3-column grid (sidebar · main · toc) + footer — never references an island itself. It renders whatever nodes the caller drops into its slots. Header, Footer, and Brand are likewise static.
Islands are the interactive pieces. They render as plain HTML on the server, then dwar mounts a small per-island JS chunk that hydrates just that subtree — progressive enhancement, not a whole-page SPA. The full set of islands is the authoritative ISLAND_REGISTRY in islands.ts, a Record<IslandName, ComponentType> keyed by the island name dwar marks the DOM with:
Island name (IslandName) | Component |
|---|---|
sidebar | Sidebar |
mobile-nav | MobileNav |
toc | TOC |
toc-mobile | TocPopover |
cmdk | CtrlK |
code-tabs | CodeTabs |
code-viewer | CodeViewer |
embed | EmbedBody |
copy-btn | CopyBtn |
copy-page | CopyPageButton |
theme-toggle | ThemeToggle |
settings | Settings |
tabs | Tabs |
Two of those entries are worth a note, straight from the registry's own comments:
embedmaps toEmbedBody(not theEmbedMDX wrapper): the loader mountsEmbedBodyonto thedata-island="embed"marker and reads its config from the marker'sdata-*attributes.tabsis fully SSR-rendered — theTabsmarkup (ARIA tablist + panels) is emitted on the server and only DOM-enhanced on the client (the panel content is arbitrary SSR HTML, so it's enhanced, not re-hydrated). Its entry exists "purely to satisfyRecord<IslandName, …>."
A third group is SSR-only and not islands: Steps / Step (a static numbered stepper) and PageNav (the prev/next pager) are pure markup with no client JS — they are exported and used in MDX/layout, but they never appear in ISLAND_REGISTRY. Heading anchors are similar: the markup is SSR-only and a delegated inline script handles the click (see mdx-utils.tsx).
The MDX element map
When dwar compiles a page's MDX, it renders it with rang's defaultMdxComponents — the element-name → component registry in mdx-components.tsx. It maps the intrinsic tags MDX emits (h1–h6, a, img, p, pre, code, lists, blockquote, tables, hr) onto styled renderers from mdx-tags.tsx and CodeBlock.tsx, plus the capitalized components setu emits so they route through the map: Callout, Embed, SourceLink, MemberMeta, MemberHeading, Steps / Step, and Tabs / Tab. (For the user-facing side of these, see Callouts and Tabs.)
How dwar consumes it
dwar imports rang both at render time and at bundle time:
- SSR.
dwar/src/layout.tsximports rang'sLayoutand the chrome islands (Sidebar,MobileNav,TOC,TocPopover,CtrlK,ThemeToggle,Settings), wraps each in adata-islandmarker viarenderIsland, records its props for the per-page payload, and hands the wrapped nodes toLayout's slots. - Hydration.
dwar/src/islands-bundle.tsruns a single split esbuild build with every island as an entry point. esbuild hoists the code shared across entries (Preact, rang's registry, shared helpers) into onechunk-<hash>.jsthat each island chunk imports — which is what cuts the emitted_islands/payload from ~4.74 MB to ~0.40 MB. The island sources import@clean-jsdoc-theme/rangfrom dwar's ownnode_modules.
Read the source
The maintainer wants you sent to the code — start here:
- Package directory: packages/rang · packages/rang/src
- The public barrel:
src/index.ts - The two registries:
islands.ts(the island name → component map) ·mdx-components.tsx(the MDX element → component map) - The chrome:
Layout.tsx·Header.tsx·Footer.tsx - The MDX renderers + styling helper:
mdx-tags.tsx·mdx-utils.tsx·CodeBlock.tsx·lib/cn.ts - How dwar uses it:
dwar/src/layout.tsx·dwar/src/islands-bundle.ts
Next
- rang Examples — concrete snippets using these exports.
- dwar Overview — the package that renders and bundles rang.
- utils Overview — the browser-safe contract rang imports.