Citations#
A site that is published with a DOI is that DOI’s landing page, and a landing page is expected to show the reader a complete bibliographic citation with the DOI written as a resolvable https://doi.org/ link.
A guide displays its citations in three places:
The site footer, on every page.
A citation card, wherever a page asks for one.
An inline DOI link, wherever a sentence, a bullet, or a table cell refers to one of the works.
All three read the same citations, so they can never disagree about what the site asks to be cited as.
Declaring the citations#
Every one of those surfaces renders the citations the site declares in the [[project.citations]] array of documenteer.toml; none carries bibliographic fields of its own.
A site that documents a data release typically declares two citations — the release itself, which this site is the landing page for, and the paper that describes it:
# documenteer.toml
[[project.citations]]
doi = "10.71929/rubin/2570308"
label = "Dataset"
type = "dataset"
self = true
note = "To be used when citing the DP2 dataset and this documentation."
title = "Data Preview 2"
publisher = "NSF-DOE Vera C. Rubin Observatory"
date = 2025-06-30
authors = [{ name = "NSF-DOE Vera C. Rubin Observatory" }]
[[project.citations]]
doi = "10.5281/zenodo.1234567"
label = "Paper"
type = "article"
title = "The Data Preview 2 release"
publisher = "Zenodo"
date = 2025-06-30
authors = [{ name = "NSF-DOE Vera C. Rubin Observatory" }]
Two of the fields there answer two different questions, and it is worth keeping them apart:
- self
Whether this site is the DOI’s registered landing page — whether doi.org sends a reader here. It alone drives the machine-readable metadata in each page’s
<head>.- preferred
Which citation the site asks readers to use. It is what a card with no argument renders.
They coincide above, and a site that publishes its own DOI never needs to think about the difference: an entry marked self is the preferred citation unless another entry claims that.
They part ways for a site whose citation is a work published somewhere else — a software repository whose CITATION.cff prefers the paper that describes it, say.
That paper’s landing page is its publisher’s, so the site marks it preferred and marks nothing self:
# documenteer.toml
[[project.citations]]
cff = "../CITATION.cff"
label = "Paper"
preferred = true
note = "Cite this paper in publications that use the package."
The site then displays that citation everywhere it displays one, while no page of it claims to be the paper’s landing page. Setting self on that entry instead is an error the build reports, since it would publish every page of the site as the paper’s full text — a claim only the paper’s publisher can make.
Such a repository is often worth citing twice — the paper for the work, and the package for the code that was run. A second entry against the same file, with cff_preferred = false, reads the file’s top-level record rather than its preferred citation:
# documenteer.toml
[[project.citations]]
cff = "../CITATION.cff"
cff_preferred = false
label = "Software"
in_footer = true
note = "Cite the version you ran; this page names the version it documents."
A package that has never been deposited for a DOI is cited by where it lives: the file’s url, or its repository-code when it names no landing page.
The entry states no type either, and does not need to: a top-level record that declares none is read as software, the default CFF defines for that key, so the citation composes as @software and publishes as a SoftwareSourceCode.
Only the self entry needs a DOI, because that entry is the claim that this site is a DOI’s landing page.
It states no version either, and gets one anyway.
An entry reading a repository’s own record describes this site’s own package, so it takes the file’s version when the file states one and the site’s own project.version otherwise.
Against a CITATION.cff for safir, released in 2020 and documented at 12.3.0, the entry renders:
SOFTWARE
NSF-DOE Vera C. Rubin Observatory (2020). Safir (version 12.3.0).
https://github.com/lsst-sqre/safir
Cite the version you ran; this page names the version it documents.
That release is what a software citation exists to carry, which is why the note is written around it rather than around the year. Software released continuously has no publication date to speak of. Its date is conventionally the year of its first release, which is defensible precisely because it never moves — every later release cites the same way, so a reader’s bibliography does not churn — but it is not what identifies the code that ran. The version is. That is also why an entry like this one — software located by its repository, stating no date — is not reported as undated.
A site can also set self and preferred on different entries, which is the site published with a DOI of its own that nonetheless asks readers to cite something else.
The footer then shows both by default, because a landing page owes its reader the citation of the DOI it is the landing page of whether or not that is the citation it asks for.
Such a site shows one of them instead by writing in_footer = false on the other.
Software that has never been deposited#
Most packages carry no DOI at all.
Releasing to PyPI and tagging on GitHub mints none, and a library that has never been deposited with Zenodo or DataCite has nothing a https://doi.org/ link could resolve to.
Such a work is cited by where it lives instead — a url in place of a doi — which is the shape a repository’s CITATION.cff file already takes, and which a site that keeps no such file states in documenteer.toml directly:
# documenteer.toml
[[project.citations]]
url = "https://github.com/lsst-sqre/safir"
label = "Software"
type = "software"
preferred = true
title = "Safir"
publisher = "NSF-DOE Vera C. Rubin Observatory"
date = 2020
authors = [
{ name = "NSF-DOE Vera C. Rubin Observatory", ror = "https://ror.org/048g3cy84" },
]
The entry is marked preferred, and a citation with no DOI always is: self is the claim that this site is a DOI’s registered landing page, so it needs a DOI to be the landing page of, while preferred only says which citation the site asks readers to use.
Marking neither is what leaves a site with nothing on its surfaces — the footer shows no citation, and a card with no argument has none to render and warns (see Unresolvable citations) — so a package site’s single entry sets preferred.
Nothing else about the entry is special. The footer and a card render it the way they render a cited DOI, ending in a link to the repository rather than to doi.org, and it reaches the site-wide JSON-LD block in full as the work the site asks to be cited. The one surface it cannot reach is the inline doi role, whose link text is the DOI: an entry that has none is linked with ordinary hyperlink syntax instead. Nor does such a site carry the per-page citation meta tags, which describe a DOI’s landing page and are emitted only for a self entry; see Related metadata.
A site whose repository keeps a CITATION.cff file does not restate any of this: cff takes the URL from the file’s url, or from its repository-code when the file names no landing page, along with the rest of the record.
Citation cards#
The citation-card directive renders one of the site’s citations as a card carrying the full citation, the citation’s label, and its note.
It is the page-level counterpart to the footer citations, and is the right tool for a dedicated “Citing this site” page, or for a section of the home page.
- .. citation-card:: [label]#
Render one of the site’s [[project.citations]] entries as a card.
The card shows the citation’s label, the full bibliographic citation with the DOI as a
https://doi.org/hyperlink, and the citation’s note. An entry that sets no note renders no note, and an entry with no label renders no label.Default: the site’s own citation
With no argument, the card renders the site’s preferred citation — the work the site asks readers to cite, which is the entry marked self = true on a site that marks no other:
.. citation-card::
:::{citation-card} :::With the configuration above, that card reads:
DATASET NSF-DOE Vera C. Rubin Observatory (2025). Data Preview 2. NSF-DOE Vera C. Rubin Observatory. https://doi.org/10.71929/rubin/2570308 To be used when citing the DP2 dataset and this documentation.
Selecting a citation
The optional argument names the entry to render, so a page can also show a citation that isn’t the site’s own:
.. citation-card:: Paper
:::{citation-card} Paper :::An entry answers to three names, matched exactly and case-sensitively:
.. citation-card:: Paper .. citation-card:: RTN-115 .. citation-card:: 10.71929/rubin/3382540
Paperis the entry’s label,RTN-115its bibtex_key, and the last its DOI — which is also accepted asdoi:10.71929/rubin/3382540and ashttps://doi.org/10.71929/rubin/3382540, so a DOI copied from anywhere selects.A label is a display string and may repeat; the key and the DOI are unique site-wide, and are what a page selects with when several entries share a heading. An argument two entries answer to renders nothing and warns, naming both by key (see Unresolvable citations).
Copying the BibTeX entry
Below the citation, the card carries a collapsed
BibTeXdisclosure holding the entry composed from the same metadata, and aCopy BibTeXbutton that puts it on the clipboard. Copying is what GitHub’s “Cite this repository”, Zenodo, and ADS all offer, and it is what a reader wants: the entry goes into a.bibfile they already keep. A guide therefore never generates.bibfiles of its own, and there is nothing to download.The entry type follows the citation’s type, so a copied entry says what the work is instead of filing everything under
@misc:type = "dataset"composes@dataset,"article"composes@article,"software"composes@software, and"report"composes@techreport. A citation typed"other", and one that declares no type at all, composes as@misc.A
@softwareor@datasetentry also carries aversionfield when the citation states a version — those are the two entry types biblatex defines the field on, and the others omit it. The entry’s key never carries the version, so a reader’s.bibfile keeps working when the cited software is released again.Citation keys
Every entry is keyed by something a reader already recognizes. The site’s own work is keyed by the
lsst.iosubdomain it is published at — a site athttps://dp2.lsst.iocomposes@dataset{dp2,— and every other work that has a DOI is keyed by the DOI,@misc{10.71929/rubin/3382528,, which is howlsst.bibkeys its DataCite records. A work with neither falls back to author, year, and title (jenness2022vera).Those keys are stable: neither a subdomain nor a DOI changes when the site is rebuilt or released again, so a manuscript that copied an entry keeps citing the same key. A site that needs a particular key — the
RTN-115an existing bibliography already uses, say — pins it:[[project.citations]] doi = "10.71929/rubin/2570308" bibtex_key = "RTN-115"
See bibtex_key for the full rules, including what happens when two entries would take the same key.
The entry is in the page rather than in a script, so it can always be selected and copied by hand. A browser that gives the page no clipboard access — an insecure origin, say — has the button removed and keeps the entry; a page whose scripts never load keeps both. A non-HTML builder renders the entry as a plain literal block, since a disclosure and a button mean nothing there.
Options
classAdditional CSS classes to set on the card.
nameA cross-reference target for the card.
Inline DOI links#
A card is a block, so a page that only needs to mention a work — the first bullet of an access list, a cell in a table of data products, a sentence pointing at the paper — cannot use one.
The doi role links a declared citation’s DOI inline instead, reading the same [[project.citations]] entries the card and the footer do.
A page that would otherwise write https://doi.org/10.71929/rubin/3382539 into a sentence by hand, or into a substitution that holds it, names the entry’s label and gets whatever the configuration declares.
- :doi:#
Link one of the site’s [[project.citations]] entries by its DOI.
The role’s content names the entry, resolved the same way the citation-card directive’s argument is: by label, by bibtex_key, or by DOI in any spelling, matched exactly and case-sensitively. The link’s text is the resolvable
https://doi.org/URL, the form the Crossref and DataCite display guidelines ask for:The catalog is published as :doi:`Object catalog`.
The catalog is published as {doi}`Object catalog`.Custom link text
The standard
text <target>spelling puts your own words on the link, for a sentence that should read as prose rather than as an identifier:For processing details see :doi:`the DP2 paper <Paper>`.
For processing details see {doi}`the DP2 paper <Paper>`.It is also what lets a sentence display one word over an entry it selects unambiguously, which is how a site whose products all need the word “TAP” writes them:
* TAP: :doi:`TAP <10.71929/rubin/3382540>`
Where it works
The role renders one external hyperlink and nothing else, so it composes wherever inline markup does: a sentence, a list item, a table cell, and the body of a
replacesubstitution definition. A data product’s “Access” list is the case it was added for:Access ------ * DOI: :doi:`Object catalog` * TAP table name: ``dp02_dc2_catalogs.Object``
.. |dp2_paper| replace:: :doi:`the DP2 paper <Paper>`
There is no default entry: the role always names one. A role appears mid-sentence, where an implicit subject would be a guess at which of the site’s works the sentence is about.
The role is a link, and only a link — no note, no BibTeX entry, no author-year text. A page that is a work’s landing page should therefore carry a card for it and use the role only for short references elsewhere, since the card is what shows a reader the full citation and the entry they came for.
Citing works that are not among the site’s own declared citations — a bibliography of the literature a guide discusses — is not what this role is for, and is not yet supported.
Unresolvable citations#
A card whose argument no entry answers to — and a card with no argument on a site that names no preferred citation — renders nothing and emits a build warning.
The warning restates the three ways an entry is selected and names a few of the site’s entries as key (label), counting the rest rather than listing every one of them; an argument that is close to an entry’s label, key, or DOI is answered with that entry instead.
The warning about a missing default asks for preferred = true, since that is the field a site sets when the citation to use is published elsewhere; self = true answers it too, for a site that really is its DOI’s landing page.
A doi role whose target no entry answers to warns the same way, and renders its target as unlinked text so the sentence around it still reads in the page that ships.
An argument several entries answer to — a shared label, or a label that happens to equal another entry’s key — warns too, naming the candidates by key and asking the page to select by key or by DOI. The card renders nothing and the role renders plain text, as they do for a target that resolves to no entry at all. No precedence is defined, so a label never quietly beats a key: either answer would be the wrong DOI on some page, with nothing on it to say so.
A role whose entry declares no DOI warns too, rather than linking that entry’s url. The role’s name is its contract: in the default spelling the link’s text is the DOI, so linking a repository’s landing page here would display that URL as though it were one. Link such a work with ordinary hyperlink syntax, or show it with a card, which displays whichever location the entry has.
Every one of those warnings carries the subtype documenteer.citation_card, so a site that knowingly keeps an unresolved reference can stop it from failing a warnings-as-errors (-W) build:
# conf.py
suppress_warnings = ["documenteer.citation_card"]
Undated citations#
An entry that states no date — and whose cff file supplies none either — is cited undated wherever the site shows it: the plain text loses its (YYYY), the BibTeX entry carries no year field, and the BibTeX key is built from the author and title alone.
Nothing on the rendered page says so, which is why the build does.
Each such entry emits one documenteer.citation_date warning, naming the entry — by its label, or by its title when it has none — and where the date belongs.
That is the entry’s own date field, and, for an entry reading a CITATION.cff file, date-released (or year) in the record it reads there: the file’s preferred-citation, or its top-level record when cff_preferred = false selects that one.
Naming the record matters, because a file whose top-level software record carries no date at all can sit above a dated preferred-citation.
A software entry located by a url rather than by a DOI is the exception, and is not reported.
Software released continuously has no publication event to date — its version is what identifies the code a reader ran, and the date that qualifies it is the date they accessed it — so reporting such an entry would make suppress_warnings the end state of every package site, silencing the dated works its author does want to hear about.
Software that carries a DOI is reported like any other work, because DataCite requires a publication year of every DOI, and so is an entry reading a CITATION.cff file’s preferred-citation, which names a work other than the repository the file describes.
Rendering is unchanged either way, so a site with no date to give silences it by name:
# conf.py
suppress_warnings = ["documenteer.citation_date"]
Sites with a large API reference#
A package guide is mostly generated API pages, and a footer citation appears under every one of them. That is the right default for a data release, whose pages a reader arrives at from a DOI; it is noise on a site where a hundred pages document one function each.
Such a site writes in_footer = false on its preferred entry and shows the citation once, on the page a reader looking for it would visit:
[[project.citations]]
url = "https://github.com/lsst-sqre/safir"
type = "software"
label = "Safir"
preferred = true
in_footer = false
title = "Safir"
authors = [{ name = "NSF-DOE Vera C. Rubin Observatory" }]
.. citation-card::
Nothing is lost by silencing the footer.
in_footer governs the visible surfaces alone: the preferred citation is still described in full in the site-wide JSON-LD block that every page of the site carries, so a crawler finds it wherever it lands, and a reader who arrives at the home page meets the card.
The copy script follows the button, so only the page with the card loads it.
Landing pages inside the site#
A site is the landing page of one DOI — the entry marked self = true — and every page of it carries that DOI’s metadata.
A site that publishes several works can do better than that.
Data Preview 2, for example, mints a DOI per data product, and each of those DOIs resolves to the product’s own page inside dp2.lsst.io rather than to the site root.
Those pages are the registered landing pages of those DOIs, so they, not the home page, should be the ones saying so.
An entry says which page is its landing page with page, a Sphinx docname, and names the work within that page with a fragment. Give each work an explicit target in the page’s source, above the heading that documents it:
.. _object-butler:
Butler
------
.. _object-tap:
TAP
---
and claim those targets:
# documenteer.toml
[project.citation_defaults]
type = "dataset"
publisher = "NSF-DOE Vera C. Rubin Observatory"
date = 2025-06-30
authors = [{ name = "NSF-DOE Vera C. Rubin Observatory" }]
[[project.citations]]
doi = "10.71929/rubin/2570308"
label = "Release"
self = true
title = "Data Preview 2"
[[project.citations]]
doi = "10.71929/rubin/3382539"
label = "Butler"
page = "products/catalogs/object#object-butler"
title = "DP2 Object catalog"
[[project.citations]]
doi = "10.71929/rubin/3382540"
label = "TAP"
page = "products/catalogs/object#object-tap"
title = "DP2 Object catalog"
A release’s products are published by the same observatory, in the same year, and are all datasets, so those fields are stated once in [project.citation_defaults] and each entry is the four fields that tell one product from the next.
An entry that states a field of its own — a product with a different release date, or a paper the site cites — overrides the default, and a CITATION.cff file the entry names beats it too.
The labels are the words the page needs under its Butler and TAP headings, not names that tell the site’s forty entries apart — every product has a TAP entry, so label = "TAP" repeats across the site.
That is what labels are for; the DOI is what selects.
With that configuration, products/catalogs/object.html describes the two catalog DOIs — each located at its own fragment, #object-butler and #object-tap — instead of the release DOI.
Because the page is the landing page of two DOIs, it emits a JSON-LD @graph of both and no citation meta tags at all: every one of those tags is single-valued, and the page has no single title, DOI, or date to give.
A page a single entry claims does emit them, carrying that entry’s DOI.
An explicit target, rather than the anchor a heading generates from its own text, is what a fragment registered against a DOI should be.
A generated anchor is derived from the heading: rewording “TAP” to “TAP service” turns #tap into #tap-service, and the URL DataCite holds for that DOI stops resolving to anything on the page.
Nothing about that build fails, and the symptom surfaces at doi.org months later, to someone other than whoever reworded the heading.
An explicit target is the author’s to name and to keep.
Every page no entry claims — the home page, the rest of the guide — is unchanged and keeps the release DOI’s metadata.
The claim also relates the two works: each catalog’s node names the release as the work it is isPartOf, and the release’s own node names both catalogs under hasPart, so a consumer arriving at either end can reach the other (see Related metadata).
Those hasPart references are the one thing a claimed page adds to every page of the site, since the site-wide block is emitted on all of them.
Each is a reference alone — a schema.org type, a DOI, and a title — which runs to roughly 150 bytes of JSON-LD, so a release with forty product DOIs adds a few kilobytes to every page.
That is the cost of the relation, and it is why an entry names a page by reference there and carries its full record only on the page it claims.
Claiming a page changes only the machine-readable metadata; the visible surfaces are unaffected.
The footer still shows the same citations everywhere, and .. citation-card:: with no argument still renders the site’s preferred citation.
A landing page that wants to show the citation a reader arriving from doi.org came for names it by DOI, since its label is shared with every other product’s TAP entry:
.. citation-card:: 10.71929/rubin/3382540
A page value the build cannot resolve emits a documenteer.citation_page warning and leaves the entry working everywhere else.
That covers both halves of the claim: a docname the project does not contain — a renamed page, or a value written as a file path rather than a docname — and a fragment that names no anchor on the page it claims, whose warning also names the explicit targets that page does record, so a claim broken by a renamed target says what to claim instead.