File-ref
{% file-ref %} is the path-based sibling of xref and expand: where those resolve a registered entity by id, file-ref points at an arbitrary project file by path. The inline form renders an <a> to the file's canonical GitHub URL; the preview="drawer" form hoists a drawer containing the file's snippet plus a "View source on GitHub →" footer link, leaving an inline link in prose that opens it (SPEC-078).
File-ref requires a repoUrl (and optional repoBranch) on the site config so the canonical GitHub URL can be built. See Site configuration → repoUrl.
Linking to a file
The minimal call points at a file by path (project-root-relative, same sandbox snippet uses). The link text defaults to the filename:
See {% file-ref path="package.json" /%} for the project metadata.<p>
See
<span data-rune="file-ref">
<meta data-field="file-ref-path" content="package.json">
<meta data-field="file-ref-lines" content="">
<meta data-field="file-ref-label" content="">
<meta data-field="file-ref-preview" content="">
<meta data-field="__file-ref-sentinel" content="true">
<a data-name="link">package.json</a>
</span>
for the project metadata.
</p>See package.json for the project metadata.
<p>
See
<span class="rf-file-ref" data-rune="file-ref" data-density="full">
<a href="https://github.com/refrakt-md/refrakt/blob/main/package.json">package.json</a>
</span>
for the project metadata.
</p>Pass an explicit label when you're referring to a symbol inside the file rather than the file itself — that's the usual case:
See {% file-ref path="packages/types/src/theme.ts" label="SiteThemeConfig" /%}
for the shape.
Anchoring to a line range
lines accepts a single line ("42") or a range ("42-58"). Drives both the GitHub #L42-L58 anchor and the snippet slice when previewing.
{% file-ref path="packages/types/src/theme.ts" lines="42-58" label="SiteThemeConfig" /%}
The href becomes https://github.com/{owner}/{repo}/blob/{repoBranch}/packages/types/src/theme.ts#L42-L58 — clicking jumps straight to the highlighted range on GitHub.
Preview drawer
preview="drawer" is where file-ref earns its keep for docs. The inline link stays in prose, and clicking opens a hoisted drawer containing the file's snippet:
See {% file-ref path="packages/types/src/theme.ts" lines="74-125" label="SiteConfig" preview="drawer" /%} for the full shape.<p>
See
<span data-rune="file-ref">
<meta data-field="file-ref-path" content="packages/types/src/theme.ts">
<meta data-field="file-ref-lines" content="74-125">
<meta data-field="file-ref-label" content="SiteConfig">
<meta data-field="file-ref-preview" content="drawer">
<meta data-field="__file-ref-sentinel" content="true">
<a data-name="link">SiteConfig</a>
</span>
for the full shape.
</p>See SiteConfig for the full shape.
<p>
See
<span class="rf-file-ref" data-rune="file-ref" data-density="full">
<a href="#drawer-packages-types-src-theme.ts-L74-L125" aria-controls="drawer-packages-types-src-theme.ts-L74-L125" aria-expanded="false" data-target-type="drawer">SiteConfig</a>
</span>
for the full shape.
</p>- The inline
<a>getshref="#drawer-{slug}",aria-controls,aria-expanded— the drawer behavior layer flipsaria-expandedon open. - The drawer body is a
{% snippet path=… lines=… /%}of the file. Syntax-highlighted via the standard highlight pass. - The drawer chrome footer holds a
View source on GitHub →link with the line-range anchor. - Per-page dedup: N mentions of the same
path+linescollapse to one hoisted drawer.
Without JS
The inline href="#drawer-{slug}" is a real in-page anchor that scrolls to the hoisted drawer's visible block fallback (the same SSR shape any {% drawer %} produces). Readers without JS get the snippet inline instead of behind chrome — graceful degradation.
Nested-preview caveat. A {% file-ref … preview="drawer" /%} placed inside another drawer's body or footer still hoists, producing a drawer-from-within-a-drawer shape. Supported but discouraged; the build emits an info-level note when detected. Same rule as the xref preview drawer.
Label conventions
The filename default (e.g. theme.ts) is conservative — when the file-ref refers to a symbol inside the file, pass an explicit label. Until a future symbol="…" attribute lands (file-ref rune + shared preview attribute on reference runes future extensions), label is the only knob:
{# Refers to the file: filename is fine #}
{% file-ref path="package.json" /%}
{# Refers to a symbol in the file: pass a label #}
{% file-ref path="packages/types/src/theme.ts" lines="74-125" label="SiteConfig" /%}
Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
path | string | yes | Project-root-relative file path. Same sandbox as snippet — absolute paths, traversal escapes (..), and out-of-root symlinks are rejected; missing files error at build time. |
lines | string | no | Line range: "42-58" or "42". Drives the GitHub #L42-L58 anchor and (with preview="drawer") the snippet slice. |
label | string | no | Display text for the inline link. Defaults to the filename. Pass an explicit label when referring to a symbol within the file. |
preview | drawer | no | Hoist a preview drawer with the file's snippet + a GitHub footer link; the inline link opens it. |
Site configuration
The GitHub URL is built from SiteConfig.repoUrl + SiteConfig.repoBranch. Both live in refrakt.config.json:
{
"site": {
"contentDir": "./content",
"theme": { "package": "@refrakt-md/lumina" },
"repoUrl": "https://github.com/owner/repo",
"repoBranch": "main"
}
}
| Field | Default | Notes |
|---|---|---|
repoUrl | — | Canonical repo URL. When absent, the inline link has no href (or falls back to an in-page snippet anchor when one exists on the page) and a one-time per-page build warning fires. |
repoBranch | "main" | Accepts any git ref — branch / tag / commit SHA. Use a SHA for archival URLs that won't drift when the file is edited. |
Output contract
Without preview, the inline form:
<span class="rf-file-ref" data-rune="file-ref">
<a href="https://github.com/.../blob/main/path/to/file.ts#L42-L58">label</a>
</span>
With preview="drawer":
<span class="rf-file-ref" data-rune="file-ref">
<a href="#drawer-path-to-file-ts-L42-L58"
aria-controls="drawer-path-to-file-ts-L42-L58"
aria-expanded="false"
data-target-type="drawer">label</a>
</span>
<!-- ... and at the page root: -->
<section class="rf-drawer" id="drawer-path-to-file-ts-L42-L58" data-rune="drawer">
<header class="rf-drawer__header">…</header>
<div class="rf-drawer__body">
<figure class="rf-snippet" data-source-path="path/to/file.ts" data-lines="42-58">
<pre data-language="typescript"><code>…</code></pre>
</figure>
</div>
<footer class="rf-drawer__footer">
<a href="https://github.com/.../#L42-L58">View source on GitHub →</a>
</footer>
</section>
See also
- xref — id-based sibling. Same
preview="drawer"attribute, different body shape (entity expand vs file snippet). - expand — id-based content-inlining counterpart.
xref preview="drawer"is the on-demand reveal;expandis the in-flow substitution. - snippet — the block-level file embedding rune.
file-refuses snippet's sandbox + rendering for its drawer body. - drawer — the chrome the preview hoists into. The same
<section class="rf-drawer">shape author-declared drawers use.
SiteConfig
export interface SiteConfig {
/** Path to content directory, relative to project root */
contentDir: string;
/** Active theme — accepts either a package name string (legacy shorthand)
* or a full {@link SiteThemeConfig} with presets, token overrides, mode
* overlays, and `colorScheme`. */
theme: string | SiteThemeConfig;
/** Documentation-only adapter hint (`svelte`, `astro`, `next`, `nuxt`, `eleventy`, `html`).
* No adapter reads or validates this field today; it serves as an in-config record of
* which adapter the site is intended for. Slated for removal in v1.0. */
target?: string;
/** Component overrides — maps typeof names to relative paths of replacement components */
overrides?: Record<string, string>;
/** Route-to-layout mapping rules, evaluated in order (first match wins) */
routeRules?: RouteRule[];
/** Declarative entity → page routes (SPEC-069). Each rule generates one page
* per registered entity matching `type` + optional `filter`. */
entityRoutes?: EntityRoute[];
/** Syntax highlighting options */
highlight?: {
theme?: string | { light: string; dark: string };
};
/** Custom icon SVGs — merged into the theme's global icon group */
icons?: Record<string, string>;
/** Plugins to merge into this site's ThemeConfig (runes, layouts, hooks, etc.) */
plugins?: string[];
/** Project-level tint presets — merged after theme tints (last wins).
* Field shape matches `TintDefinition` from `@refrakt-md/transform` per
* SPEC-053; `Record<string, unknown>` is used here to avoid a cross-package
* type dependency in `@refrakt-md/types`. */
tints?: Record<string, Record<string, unknown>>;
/** Project-level background presets — merged after theme backgrounds (last wins) */
backgrounds?: Record<string, Record<string, unknown>>;
/** Sandbox configuration */
sandbox?: {
examplesDir?: string;
};
/** Base URL for canonical links and og:url */
baseUrl?: string;
/** Human-readable site name for og:site_name */
siteName?: string;
/** Default og:image for pages without their own image */
defaultImage?: string;
/** Site logo for Organization JSON-LD schema */
logo?: string;
/** Canonical GitHub (or compatible) repository URL — e.g.
* `"https://github.com/owner/repo"`. Used by file-ref (SPEC-078) to
* build deep-link "View source" URLs of the form
* `{repoUrl}/blob/{repoBranch}/{path}#L{start}-L{end}`. When absent,
* `file-ref` falls back to a no-href link / in-page anchor with a
* build warning. */
repoUrl?: string;