# Eine Leseliste aus einem BibTeX-Export von Zotero

> Eine Seminar-Leseliste, deren Literatur ein unveränderter BibTeX-Export aus Zotero ist, zitiert im Chicago-Autor-Jahr-Stil, mit nocite für nie zitierte Werke.

- HTML-Fassung: https://postext.dev/de/cookbook/zotero-bibtex-nocite
- Rezept Nº 097 · Buchaufbau · Stufe 2 (Mittel) · Ausgaben: Canvas
- Gattungen: Lehrbücher, Berichte
- Benötigt postext ≥ 1.25.0 · getestet mit 1.31.0 am 2026-10-13
- Seiten: [1](https://postext.dev/cookbook/zotero-bibtex-nocite/en/p01.webp?v=a4407678), [2](https://postext.dev/cookbook/zotero-bibtex-nocite/en/p02.webp?v=a4407678)
- In der Sandbox öffnen: https://postext.dev/de/sandbox#recipe=zotero-bibtex-nocite&lang=en (.postext: https://postext.dev/cookbook/zotero-bibtex-nocite/en/zotero-bibtex-nocite.postext)
- Zuletzt aktualisiert: 2026-10-10
- Andere Sprachen: [en](https://postext.dev/en/cookbook/zotero-bibtex-nocite.md), [es](https://postext.dev/es/cookbook/zotero-bibtex-nocite.md), [ca](https://postext.dev/ca/cookbook/zotero-bibtex-nocite.md), [fr](https://postext.dev/fr/cookbook/zotero-bibtex-nocite.md), [it](https://postext.dev/it/cookbook/zotero-bibtex-nocite.md), [pt](https://postext.dev/pt/cookbook/zotero-bibtex-nocite.md), [zh](https://postext.dev/zh/cookbook/zotero-bibtex-nocite.md), [ja](https://postext.dev/ja/cookbook/zotero-bibtex-nocite.md), [ar](https://postext.dev/ar/cookbook/zotero-bibtex-nocite.md)

## Kurz gesagt

Die Leseliste eines Universitätsseminars. Die Lehrkraft fügt die Datei ein, die Zotero exportiert; Postext druckt die Zitate und eine Liste, die auch Bücher enthält, die der Text nicht erwähnt.

## Was Sie setzen

Die Leseliste eines sechswöchigen Seminars zur Geschichte und Soziologie der Wissenschaft, auf einer Seite von 210 × 280 mm in zwei Spalten gesetzt. Ein tiefgrünes Feld nimmt die obere Hälfte der ersten Seite ein, mit dem Kurstitel in Spectral Light zu 50 pt; der Text darunter ist der Leitfaden der Seminarleitung zu jeder Woche. Die Literatur ist die Zotero-Sammlung des Seminars, als BibTeX exportiert und ins Dokument eingefügt, ohne eine Zeile anzurühren: die Zitierschlüssel von Zotero, seine geschweiften Klammern um Großbuchstaben, seine Akzente `{\"o}`, `month = dec`, die Felder `file`, `keywords` und `urldate`. Der Text zitiert acht der Werke. Die anderen fünf, Hintergrundlektüre, die das Seminar nicht bespricht, kommen über `nocite` in die Liste.

**Dieses Rezept beantwortet:**

- Wie verwende ich einen BibTeX-Export aus Zotero und liste Werke auf, die der Text nie zitiert?
- Wie zitiere ich Werke und erstelle das Literaturverzeichnis nach APA, IEEE oder einem anderen Zitierstil?

## Die kurze Antwort

```js
// script.js, Zeilen 28–40
// The works come from a :::references{format=bibtex} block: the Zotero export pasted as it
// is. The text cites some of them; `nocite` in the front matter adds the rest by key, and
// `nocite: "@*"` would add the whole export. citeproc-js writes both in the style named.
registerCitationEngine(createCiteprocEngine({ styles: STYLES, locales: LOCALES }));
const citations = {
  style: 'chicago-author-date', // '(Gieryn 1983)', 'Shapin and Schaffer (1985)'
  link: true, // a citation jumps to its entry
  bibliography: {
    // includeUncited: true is the same as nocite: "@*", set from the design side.
    fontSize: em(0.94), lineHeight: pt(12.4), hangingIndent: mm(5), entrySpacing: pt(3.5),
    doi: 'link', // Zotero's doi field prints as https://doi.org/…; its url field is dropped
  },
};
```

## Zutaten

**Zeigt**

- [Literaturverzeichnis aus den Quellen](https://postext.dev/de/docs/document-format.md#zitate-und-literaturverzeichnis): Die Liste der zitierten Werke, aus den Quellen des Dokuments erzeugt (Frontmatter oder ein BibTeX-Block), dort, wo :::bibliography steht, oder nach dem letzten Kapitel.
- [Zitate in einem Zitierstil](https://postext.dev/de/docs/document-format.md#zitate-und-literaturverzeichnis): Werke, zitiert als [@key, p. 33] und in einem CSL-Stil formatiert (APA, Chicago, MLA, IEEE, Vancouver, ISO 690, GB/T 7714…), der in den Einstellungen gewählt wird, mit Links zu ihren Einträgen.

**Verwendet außerdem**

- [Gestaltete Auftakte](https://postext.dev/de/docs/configuration-text.md#seitenbreite-und-erweiterte-gestaltung)
- [Überschriftenattribute](https://postext.dev/de/docs/document-format.md#attribute-von-überschriften)
- [Eine oder zwei Spalten](https://postext.dev/de/docs/configuration-page-layout.md#layout-typen)
- [Kolumnentitel und Seitenzahlen](https://postext.dev/de/docs/configuration-page-layout.md#kopf--und-fußzeilen)
- [Absatzformate](https://postext.dev/de/docs/configuration-styles.md#absatzformate)
- [Semantische Farbpalette](https://postext.dev/de/docs/configuration-fonts-colors-viewers.md#farbpalette)
- [Kapitelband über die ganze Breite](https://postext.dev/de/docs/configuration-text.md#seitenbreite-und-erweiterte-gestaltung)
- [Kopfzeilen nach Seitenrolle](https://postext.dev/de/docs/configuration-page-layout.md#textelemente)

**Die Konfiguration auf einen Blick**

- [`bodyText`](https://postext.dev/de/docs/configuration-text.md#fließtext), [`citations`](https://postext.dev/de/docs/configuration-notes-references.md#zitationen), [`colorPalette`](https://postext.dev/de/docs/configuration-fonts-colors-viewers.md#farbpalette), [`footer`](https://postext.dev/de/docs/configuration-page-layout.md#kopf--und-fußzeilen), [`header`](https://postext.dev/de/docs/configuration-page-layout.md#kopf--und-fußzeilen), [`headings`](https://postext.dev/de/docs/configuration-text.md#überschriften), [`layout`](https://postext.dev/de/docs/configuration-page-layout.md#layout), [`locale`](https://postext.dev/de/docs/configuration-text.md#silbentrennung), [`page`](https://postext.dev/de/docs/configuration-page-layout.md#seite), [`paragraphStyles`](https://postext.dev/de/docs/configuration-styles.md#absatzformate)

**APIs**

- [`LOCALES`](https://postext.dev/de/docs/document-format.md#zitate-und-literaturverzeichnis), [`STYLES`](https://postext.dev/de/docs/document-format.md#zitate-und-literaturverzeichnis), [`buildDocumentWithFonts`](https://postext.dev/de/docs/configuration-programmatic-usage.md#schriften-vor-dem-layout-laden), [`createCiteprocEngine`](https://postext.dev/de/docs/document-format.md#zitate-und-literaturverzeichnis), [`registerCitationEngine`](https://postext.dev/de/docs/document-format.md#zitate-und-literaturverzeichnis), [`renderPageToCanvas`](https://postext.dev/de/docs/configuration-programmatic-usage.md#eine-seite-als-bitmap-rendern)

**Schriften**

- Spectral (OFL-1.1), IBM Plex Sans Condensed (OFL-1.1)

## Zubereitung

### 1 · Den Stil einmal benennen

Der Code steht oben in [der kurzen Antwort](https://postext.dev/de/cookbook/zotero-bibtex-nocite.md#die-kurze-antwort). Die Zitier-Engine wird vor dem ersten Build registriert, und der Stil ist eine einzige Einstellung: Chicago Autor-Jahr schreibt „(Shapin 1984)“ für `[@shapin_pump_1984]` und „Shapin and Schaffer (1985)“ für `@shapin_leviathan_1985` in einem Satz. Die Schlüssel sind die, die Zotero aus dem ersten Autor, dem ersten bedeutungstragenden Wort des Titels und dem Jahr gebildet hat, deshalb behält `gieryn_boundary-work_1983` seinen Bindestrich; Postext liest einen Bindestrich oder Unterstrich innerhalb eines Schlüssels als Teil davon.

### 2 · Den Export so einfügen, wie Zotero ihn schreibt

Der letzte Block des Dokuments enthält den Export, einen Eintrag je Werk, mit Tabulatoren eingerückt, wie Zotero ihn schreibt:

```bibtex
@article{gieryn_boundary-work_1983,
	title = {Boundary-{Work} and the {Demarcation} of {Science} from {Non}-{Science}: …},
	url = {https://www.jstor.org/stable/2095325},
	doi = {10.2307/2095325},
	urldate = {2026-08-21},
	journal = {American Sociological Review},
	author = {Gieryn, Thomas F.},
	month = dec,
	year = {1983},
	keywords = {demarcation, week 4},
	pages = {781--795},
	file = {JSTOR Full Text PDF:/Users/hps214/Zotero/storage/2HJ8M6PA/Gieryn - 1983 - ….pdf:application/pdf},
}
```

Der Leser entfernt die Klammern, die Großbuchstaben schützen, setzt `{\"o}` und `{\'e}` zu ö und é zusammen, macht aus `--` einen Halbgeviertstrich und liest `month = dec` als bloßen Monatsnamen. `urldate` wird zum Zugriffsdatum und `language` zur Sprache des Eintrags; `file`, `keywords`, `abstract` und `shorttitle` werden überlesen, sodass nichts von Ihrer Festplatte auf die Seite gelangt.

### 3 · Die Werke aufnehmen, die der Text nicht zitiert

Das Frontmatter listet sie nach Schlüssel auf:

```yaml
---
title: "Science as a Social Practice"
nocite: "@fleck_genesis_1979, @merton_sociology_1973, @latour_science_1987, @rheinberger_toward_1997, @lopez_pinero_ciencia_1979"
---
```

Die Liste enthält jedes zitierte Werk plus diese fünf, gemeinsam nach Autor sortiert, wie Chicago es verlangt; nichts kennzeichnet einen Eintrag als nicht zitiert. `nocite: "@*"` listet den ganzen Export auf, und `bibliography.includeUncited: true` tut dasselbe aus der Konfiguration heraus, für eine Gestaltung, die immer die ganze Bibliothek druckt.

### 4 · Ein grünes Feld für die Karte

```js
// script.js, Zeilen 44–68
const BAND = 132; // mm from the trim's top to the band's foot
const onBand = { color: col('paper'), align: 'left', overflow: 'wrap' };
const opener = {
  enabled: true,
  minHeight: mm(BAND - TOP + 9), // the columns start 9 mm under the band
  slot: { elements: [
    { kind: 'box', id: 'band', style: { backgroundColor: col('accent') },
      placement: { anchor: { to: 'page', edge: 'top-left' },
        size: { width: 'fill', height: mm(BAND) } } },
    { kind: 'text', id: 'kicker', content: '{attr.kicker}', ...onBand, fontFamily: LABEL,
      fontSize: pt(8.5), fontWeight: 600, letterSpacing: pt(1.7), textTransform: 'uppercase',
      placement: { anchor: { to: 'container', edge: 'top-left' }, offset: { y: mm(14) } } },
    { kind: 'text', id: 'title', content: '{titleText}', ...onBand, fontFamily: TEXT,
      fontSize: pt(50), fontWeight: 300, lineHeight: 1.02,
      placement: { anchor: { to: '#kicker', edge: 'below' }, offset: { y: mm(7) },
        size: { width: mm(150), height: 'auto' } } },
    { kind: 'rule', id: 'rule', direction: 'horizontal', thickness: pt(1), color: col('tint'),
      placement: { anchor: { to: '#title', edge: 'below' }, offset: { y: mm(7) },
        size: { width: mm(18) } } },
    { kind: 'text', id: 'lead', content: '{attr.lead}', ...onBand, fontFamily: TEXT,
      italic: true, fontSize: pt(13), lineHeight: 1.3, color: col('tint'),
      placement: { anchor: { to: '#rule', edge: 'below' }, offset: { y: mm(6) },
        size: { width: mm(128), height: 'auto' } } },
  ] },
};
```

Das Band ist ein am Endformat verankerter Kasten von 132 mm Tiefe, und die beiden Spalten beginnen 9 mm darunter. Titel, Linie und Vorspann sind mit Ankern `below` verkettet, sodass ein längerer Kurstitel den Vorspann nach unten schiebt, statt mit ihm zusammenzustoßen.

```js
// script.js, Zeilen 15–21
const palette = {
  ink: '#1c211f', accent: '#2d5b48', tint: '#dfe9e2', rule: '#c3cdc6', muted: '#5c6862',
  paper: '#ffffff',
};
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = Object.entries({ ...palette, 'main-color': palette.accent })
  .map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } }));
```

## Das ganze Rezept

Eine Datei, aus dem Ordner des Rezepts zusammengesetzt, mit dem Beispieltext und dem gemeinsamen Baukasten der Rezepte bereits eingebettet; sie baut ihre eigene Seite auf. Zum Ausführen setzen Sie sie in ein `<script type="module">` auf einer leeren Seite oder fügen sie in das JS-Feld eines neuen CodePen-Pens ein (als Modul). Sie importiert postext von esm.sh, es gibt also nichts zu installieren oder zu bauen.

- Quellordner: https://github.com/drnachio/postext/tree/main/cookbook/zotero-bibtex-nocite

### script.js

```js
// ═══ Postext Cookbook · Nº 097 · A reading list from a Zotero BibTeX export ═══════
// https://postext.dev/en/cookbook/zotero-bibtex-nocite
// Code: MIT · Text: original (CC BY 4.0) · Pictures: none
// Fonts: Spectral, IBM Plex Sans Condensed (SIL OFL 1.1) · Needs postext ≥ 1.25.0
import {
  buildDocumentWithFonts, renderPageToCanvas, registerCitationEngine,
} from 'https://esm.sh/postext';
import { createCiteprocEngine, STYLES, LOCALES } from 'https://esm.sh/postext-citeproc';

const LANG = 'en'; // @lang: the language of the sample document ('en' | 'es')
const RECIPE = 'zotero-bibtex-nocite';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: a department green, one tint, everything else in ink
const palette = {
  ink: '#1c211f', accent: '#2d5b48', tint: '#dfe9e2', rule: '#c3cdc6', muted: '#5c6862',
  paper: '#ffffff',
};
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = Object.entries({ ...palette, 'main-color': palette.accent })
  .map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } }));
// #endregion
const [TEXT, LABEL] = ['Spectral', 'IBM Plex Sans Condensed'];
const LEAD = 13.4; // pt
const [TOP, OUTER] = [24, 18]; // mm

// #region answer: Chicago author-date, and a list that holds more than the text cites
// The works come from a :::references{format=bibtex} block: the Zotero export pasted as it
// is. The text cites some of them; `nocite` in the front matter adds the rest by key, and
// `nocite: "@*"` would add the whole export. citeproc-js writes both in the style named.
registerCitationEngine(createCiteprocEngine({ styles: STYLES, locales: LOCALES }));
const citations = {
  style: 'chicago-author-date', // '(Gieryn 1983)', 'Shapin and Schaffer (1985)'
  link: true, // a citation jumps to its entry
  bibliography: {
    // includeUncited: true is the same as nocite: "@*", set from the design side.
    fontSize: em(0.94), lineHeight: pt(12.4), hangingIndent: mm(5), entrySpacing: pt(3.5),
    doi: 'link', // Zotero's doi field prints as https://doi.org/…; its url field is dropped
  },
};
// #endregion

// #region opener: a green field over the top half, the course in white on it
const BAND = 132; // mm from the trim's top to the band's foot
const onBand = { color: col('paper'), align: 'left', overflow: 'wrap' };
const opener = {
  enabled: true,
  minHeight: mm(BAND - TOP + 9), // the columns start 9 mm under the band
  slot: { elements: [
    { kind: 'box', id: 'band', style: { backgroundColor: col('accent') },
      placement: { anchor: { to: 'page', edge: 'top-left' },
        size: { width: 'fill', height: mm(BAND) } } },
    { kind: 'text', id: 'kicker', content: '{attr.kicker}', ...onBand, fontFamily: LABEL,
      fontSize: pt(8.5), fontWeight: 600, letterSpacing: pt(1.7), textTransform: 'uppercase',
      placement: { anchor: { to: 'container', edge: 'top-left' }, offset: { y: mm(14) } } },
    { kind: 'text', id: 'title', content: '{titleText}', ...onBand, fontFamily: TEXT,
      fontSize: pt(50), fontWeight: 300, lineHeight: 1.02,
      placement: { anchor: { to: '#kicker', edge: 'below' }, offset: { y: mm(7) },
        size: { width: mm(150), height: 'auto' } } },
    { kind: 'rule', id: 'rule', direction: 'horizontal', thickness: pt(1), color: col('tint'),
      placement: { anchor: { to: '#title', edge: 'below' }, offset: { y: mm(7) },
        size: { width: mm(18) } } },
    { kind: 'text', id: 'lead', content: '{attr.lead}', ...onBand, fontFamily: TEXT,
      italic: true, fontSize: pt(13), lineHeight: 1.3, color: col('tint'),
      placement: { anchor: { to: '#rule', edge: 'below' }, offset: { y: mm(6) },
        size: { width: mm(128), height: 'auto' } } },
  ] },
};
// #endregion

const head = (id, content, parity, edge, x, extra = {}) => ({
  kind: 'text', id, content, parity, pages: 'body', fontFamily: LABEL, fontSize: pt(7.5),
  fontWeight: 600, letterSpacing: pt(1.2), textTransform: 'uppercase', color: col('muted'),
  placement: { anchor: { to: 'page', edge }, offset: { x: mm(x), y: mm(13) } }, ...extra,
});
const folio = { color: col('accent') };
const listName = t({ en: 'HPS 214 · Reading list', es: 'HPS 214 · Lecturas' });

const config = () => ({
  locale: t({ en: 'en-us', es: 'es' }),
  colorPalette,
  citations,
  page: {
    sizePreset: 'custom', width: mm(210), height: mm(280), dpi: 150,
    margins: { top: mm(TOP), bottom: mm(22), left: mm(20), right: mm(OUTER), mirror: true },
  },
  layout: { layoutType: 'double', gutterWidth: mm(7) },
  bodyText: {
    fontFamily: TEXT, fontSize: pt(9.6), lineHeight: pt(LEAD), color: col('ink'),
    boldColor: col('ink'), italicColor: col('ink'), referenceColor: col('accent'),
    textAlign: 'justify', firstLineIndent: mm(4), indentAfterHeading: false,
    hyphenation: { enabled: true }, optimalLineBreaking: true,
    avoidWidows: true, avoidOrphans: true, avoidRunts: true,
  },
  headings: {
    fontFamily: TEXT, color: col('ink'), fontWeight: 600,
    levels: [
      // parity 'any': the H1 opens on the next page, either side (the default waits for a recto).
      { level: 1, span: 'page', breakBefore: { enabled: true, parity: 'any' },
        marginBottom: pt(0), advancedDesign: opener },
      { level: 2, fontFamily: LABEL, fontSize: pt(10), lineHeight: pt(LEAD), fontWeight: 600,
        color: col('accent'), marginTop: pt(LEAD), marginBottom: pt(0),
      },
    ],
  },
  paragraphStyles: [
    { id: 'colophon', fontFamily: LABEL, fontSize: pt(7), lineHeight: pt(9.5),
      color: col('muted'), textAlign: 'left', firstLineIndent: pt(0), marginTop: pt(LEAD) },
  ],
  header: { elements: [
    head('verso-folio', '{pageNumber}', 'even', 'top-left', OUTER, folio),
    head('verso-title', '{title}', 'even', 'top-left', OUTER + 8),
    head('recto-title', listName, 'odd', 'top-right', -(OUTER + 8)),
    head('recto-folio', '{pageNumber}', 'odd', 'top-right', -OUTER, folio),
  ] },
  footer: { elements: [] },
});

// ─── 2 · Content ────────────────────────────────────────────────────────────
const markdown = String.raw`---
title: "Science as a Social Practice"
nocite: "@fleck_genesis_1979, @merton_sociology_1973, @latour_science_1987, @rheinberger_toward_1997, @lopez_pinero_ciencia_1979"
---

# Science as a Social Practice {kicker="HPS 214 · Seminar reading list · Autumn term 2026" lead="Six weeks on how scientific knowledge is made, tested and trusted, from the paradigm to the laboratory notebook."}

This seminar reads the history and sociology of science as one subject. Its question is how a claim about nature becomes a fact that people who never saw the experiment are willing to rely on. The answers we read come from historians of the seventeenth century, from sociologists who sat in laboratories for two years, and from philosophers who looked at what scientists actually do rather than at what their textbooks say they do.

Each week has one set reading, which everyone reads before the seminar, and one or two companion texts for the person introducing the discussion. The set readings are available in the course reader; the others are in the library’s reserve collection.

## Week 1 · Paradigms and normal science

We start with the book that made the subject fashionable. Read the first five sections and the Postscript of @kuhn_structure_1970, which Kuhn added for the second edition to answer the charge that “paradigm” meant too many things at once. His preface credits a monograph by Ludwik Fleck, written in 1935 and nearly forgotten until Kuhn mentioned it; it is in the list at the end, and the student introducing the week should read its second chapter.

## Week 2 · Witnessing an experiment

The set reading is the first two chapters of @shapin_leviathan_1985, on Robert Boyle’s air-pump and the dispute with Thomas Hobbes over whether an experiment could settle anything. The companion text is the shorter article in which Shapin first described Boyle’s “literary technology”, the way a report written in plain, circumstantial prose turned its readers into witnesses [@shapin_pump_1984].

## Week 3 · Inside the laboratory

Bruno Latour spent two years at the Salk Institute as an observer of a neuroendocrinology laboratory. Read the second and third chapters of @latour_laboratory_1979 and follow how a statement loses its qualifications (“it seems that”, “X has claimed”) as it travels from one paper to the next until it is printed without any.

## Week 4 · Where science ends

The demarcation problem asks what separates science from what is not science. Philosophers looked for a criterion; @gieryn_boundary-work_1983 looked at the people who draw the line in practice, and at what they gain by drawing it where they do. Bring an example from the news of the last month.

## Week 5 · Objectivity has a history

Read the article first [@daston_image_1992], then the first chapter and the chapter on “trained judgment” in the book it grew into [@daston_objectivity_2007]. The argument is that objectivity, as a virtue of the scientist, appeared in the nineteenth century, and that atlases of skeletons, snowflakes and clouds show it arriving.

## Week 6 · Whom to believe

The last week returns to the seventeenth century with a broader question: how did gentlemen-philosophers decide whose reports to accept? Read the first chapter of @shapin_social_1994 and come ready to compare its answer with how you decide whom to believe when you read a paper outside your field.

## Further reading

The list below is the seminar’s Zotero collection, exported as BibTeX and set as it came out of Zotero. It includes every work cited above and five that the seminar does not discuss but that you will meet in the essays you read: Fleck’s monograph, Merton’s collected essays on the norms of science, Latour’s later handbook, Rheinberger’s study of experimental systems, and a history of Spanish science in the sixteenth and seventeenth centuries for those who read Spanish.

:::bibliography{title=""}

:::paragraphs{style="colophon"}
Set in Spectral and IBM Plex Sans Condensed (SIL OFL) · Reading list and notes: original, CC BY 4.0 · The works are real; the Zotero file paths and access dates are illustrative.
:::

:::references{format=bibtex}

@book{kuhn_structure_1970,
	address = {Chicago},
	edition = {2},
	title = {The {Structure} of {Scientific} {Revolutions}},
	abstract = {Normal science, anomaly and crisis, and the paradigm changes that end them, with the Postscript of 1969.},
	language = {en},
	publisher = {University of Chicago Press},
	author = {Kuhn, Thomas S.},
	year = {1970},
	keywords = {paradigms, week 1},
	file = {Kuhn - 1970 - The Structure of Scientific Revolutions.pdf:/Users/hps214/Zotero/storage/7QW2KD9N/Kuhn - 1970 - The Structure of Scientific Revolutions.pdf:application/pdf},
}

@book{fleck_genesis_1979,
	address = {Chicago},
	title = {Genesis and {Development} of a {Scientific} {Fact}},
	language = {en},
	publisher = {University of Chicago Press},
	author = {Fleck, Ludwik},
	editor = {Trenn, Thaddeus J. and Merton, Robert K.},
	translator = {Bradley, Fred and Trenn, Thaddeus J.},
	year = {1979},
	keywords = {thought collectives, week 1},
}

@book{shapin_leviathan_1985,
	address = {Princeton, NJ},
	title = {Leviathan and the {Air}-{Pump}: {Hobbes}, {Boyle}, and the {Experimental} {Life}},
	shorttitle = {Leviathan and the {Air}-{Pump}},
	language = {en},
	publisher = {Princeton University Press},
	author = {Shapin, Steven and Schaffer, Simon},
	year = {1985},
	keywords = {experiment, week 2},
}

@article{shapin_pump_1984,
	title = {Pump and {Circumstance}: {Robert} {Boyle}'s {Literary} {Technology}},
	volume = {14},
	shorttitle = {Pump and {Circumstance}},
	url = {https://doi.org/10.1177/030631284014004001},
	doi = {10.1177/030631284014004001},
	language = {en},
	number = {4},
	urldate = {2026-08-21},
	journal = {Social Studies of Science},
	author = {Shapin, Steven},
	month = nov,
	year = {1984},
	keywords = {literary technology, week 2},
	pages = {481--520},
	file = {Shapin - 1984 - Pump and Circumstance.pdf:/Users/hps214/Zotero/storage/V3NC8RTE/Shapin - 1984 - Pump and Circumstance.pdf:application/pdf},
}

@book{latour_laboratory_1979,
	address = {Beverly Hills},
	title = {Laboratory {Life}: {The} {Social} {Construction} of {Scientific} {Facts}},
	shorttitle = {Laboratory {Life}},
	language = {en},
	publisher = {Sage},
	author = {Latour, Bruno and Woolgar, Steve},
	year = {1979},
	keywords = {ethnography, week 3},
}

@book{latour_science_1987,
	address = {Cambridge, MA},
	title = {Science in {Action}: {How} to {Follow} {Scientists} and {Engineers} through {Society}},
	shorttitle = {Science in {Action}},
	language = {en},
	publisher = {Harvard University Press},
	author = {Latour, Bruno},
	year = {1987},
}

@book{rheinberger_toward_1997,
	address = {Stanford, CA},
	title = {Toward a {History} of {Epistemic} {Things}: {Synthesizing} {Proteins} in the {Test} {Tube}},
	language = {en},
	publisher = {Stanford University Press},
	author = {Rheinberger, Hans-J{\"o}rg},
	year = {1997},
	keywords = {experimental systems},
}

@article{gieryn_boundary-work_1983,
	title = {Boundary-{Work} and the {Demarcation} of {Science} from {Non}-{Science}: {Strains} and {Interests} in {Professional} {Ideologies} of {Scientists}},
	volume = {48},
	shorttitle = {Boundary-{Work} and the {Demarcation} of {Science} from {Non}-{Science}},
	url = {https://www.jstor.org/stable/2095325},
	doi = {10.2307/2095325},
	language = {en},
	number = {6},
	urldate = {2026-08-21},
	journal = {American Sociological Review},
	author = {Gieryn, Thomas F.},
	month = dec,
	year = {1983},
	keywords = {demarcation, week 4},
	pages = {781--795},
	file = {JSTOR Full Text PDF:/Users/hps214/Zotero/storage/2HJ8M6PA/Gieryn - 1983 - Boundary-Work and the Demarcation of Science from Non-Science.pdf:application/pdf},
}

@article{daston_image_1992,
	title = {The {Image} of {Objectivity}},
	url = {https://www.jstor.org/stable/2928741},
	doi = {10.2307/2928741},
	language = {en},
	number = {40},
	urldate = {2026-08-22},
	journal = {Representations},
	author = {Daston, Lorraine and Galison, Peter},
	year = {1992},
	keywords = {objectivity, week 5},
	pages = {81--128},
	file = {JSTOR Full Text PDF:/Users/hps214/Zotero/storage/F9XK4C2B/Daston and Galison - 1992 - The Image of Objectivity.pdf:application/pdf},
}

@book{daston_objectivity_2007,
	address = {New York},
	title = {Objectivity},
	language = {en},
	publisher = {Zone Books},
	author = {Daston, Lorraine and Galison, Peter},
	year = {2007},
	keywords = {objectivity, week 5},
}

@book{shapin_social_1994,
	address = {Chicago},
	series = {Science and {Its} {Conceptual} {Foundations}},
	title = {A {Social} {History} of {Truth}: {Civility} and {Science} in {Seventeenth}-{Century} {England}},
	language = {en},
	publisher = {University of Chicago Press},
	author = {Shapin, Steven},
	year = {1994},
	keywords = {trust, week 6},
}

@book{merton_sociology_1973,
	address = {Chicago},
	title = {The {Sociology} of {Science}: {Theoretical} and {Empirical} {Investigations}},
	language = {en},
	publisher = {University of Chicago Press},
	author = {Merton, Robert K.},
	editor = {Storer, Norman W.},
	year = {1973},
}

@book{lopez_pinero_ciencia_1979,
	address = {Barcelona},
	title = {Ciencia y t{\'e}cnica en la sociedad espa{\~n}ola de los siglos {XVI} y {XVII}},
	language = {es},
	publisher = {Labor},
	author = {L{\'o}pez Pi{\~n}ero, Jos{\'e} Mar{\'i}a},
	year = {1979},
}
:::
`;

// ─── 3 · Fonts ──────────────────────────────────────────────────────────────
const FONTS = {
  Spectral: ['300', '300i', '400', '400i', '600', '600i'],
  'IBM Plex Sans Condensed': ['400', '600'],
};

// ─── 4 · Build & show ───────────────────────────────────────────────────────
const doc = await buildDocumentWithFonts({ markdown }, config(), kitFonts(FONTS));
showPages(doc, { title: t({ en: 'A reading list from Zotero', es: 'Lecturas desde Zotero' }) });

// ─── Kit ── helpers shared by every Cookbook recipe · postext.dev/cookbook ─────

// ─── Kit · core v1 ── the same in every recipe · postext.dev/cookbook
function mm(value) { return { value, unit: 'mm' }; }
function pt(value) { return { value, unit: 'pt' }; }
function em(value) { return { value, unit: 'em' }; }
/** The sample language's string: t({ en: 'Figure', es: 'Figura' }). */
function t(strings) { return strings[LANG] ?? Object.values(strings)[0]; }
/** A file in this recipe's assets folder, served from the Postext repo by jsDelivr. */
function asset(file) { return `https://cdn.jsdelivr.net/gh/drnachio/postext@main/cookbook/${RECIPE}/assets/${file}`; }

// ─── Kit · fonts v3 ── the same in every recipe · postext.dev/cookbook
// Postext loads the faces a config asks for before layout (prepareFonts,
// buildDocumentWithFonts) and asks the kit for the files: Fontsource's, the
// files the PDF embeds too.

/** The options of buildDocumentWithFonts / prepareFonts / withLoadedFonts:
 *  the Fontsource resolver, and the faces of FONTS loaded with the ones the
 *  config names. FONTS = { 'Family Name': ['400', '400i', '700'] }. */
function kitFonts(FONTS = {}) {
  const faces = Object.entries(FONTS).flatMap(([family, specs]) => specs.map((spec) =>
    ({ family, weight: parseInt(spec, 10), style: spec.endsWith('i') ? 'italic' : 'normal' })));
  return { resolve: fontsourceResolver, faces };
}

/** A face's Fontsource files, in a fixed order: latin, then latin-ext and greek
 *  when `text` needs them and the family ships them (kitSubsetsFor), each with
 *  its unicode-range. Null for a weight or slant Fontsource does not ship. */
async function fontsourceResolver(family, weight, style, { text = '' } = {}) {
  const id = fontsourceId(family);
  const meta = await fontsourceMeta(family);
  if (meta && !(meta.weights.includes(weight) && meta.styles.includes(style))) return null;
  kitStatus('Loading fonts…');
  const at = await fontsourceAt(id);
  return ['latin', ...kitSubsetsFor(text, meta)].map((subset) => ({
    source: `${at}files/${id}-${subset}-${weight}-${style}.woff2`,
    unicodeRange: kitRanges()[subset],
  }));
}

/** The unicode-range of each Fontsource file the kit loads (a function: the
 *  kit is inlined after the pen's code, which runs before a const would be set). */
function kitRanges() {
  return {
    latin: 'U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,'
      + 'U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD',
    'latin-ext': 'U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,'
      + 'U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF',
    greek: 'U+0370-03FF',
  };
}

/** For the cjk, arabic and comics blocks, which add their own files: the
 *  faces of `faces` = { 'Family Name': ['400', '400i', '700'] }. `text` is the sample:
 *  č ł † α χ also load latin-ext and greek files (kitSubsetsFor). With
 *  `optional`, a face Fontsource does not ship is skipped instead of failing.
 *  The files are added once all have loaded, in the order asked (latin, then
 *  latin-ext, then greek), whatever order they arrive in: the last one added
 *  wins a character two files hold. Resolves to the number of faces added. */
async function loadFonts(faces, text = '', { optional = false } = {}) {
  kitStatus('Loading fonts…');
  const ranges = kitRanges();
  const jobs = [];
  for (const [family, specs] of Object.entries(faces)) {
    const id = fontsourceId(family);
    const todo = [...new Set(specs)].map((spec) => [parseInt(spec, 10), spec.endsWith('i') ? 'italic' : 'normal'])
      .filter(([weight, style]) => !hasFace(family, weight, style)); // before any await
    const at = fontsourceAt(id);
    const meta = optional || /[^\0-ÿ]/u.test(text) ? await fontsourceMeta(family) : null;
    const subsets = ['latin', ...kitSubsetsFor(text, meta)];
    for (const [weight, style] of todo) {
      if (optional && !(meta?.weights.includes(weight) && meta.styles.includes(style))) continue;
      for (const subset of subsets) {
        const url = `${await at}files/${id}-${subset}-${weight}-${style}.woff2`;
        const face = new FontFace(family, `url(${url}) format('woff2')`,
          { weight: String(weight), style, unicodeRange: ranges[subset] });
        jobs.push(face.load().catch(() => {
          if (subset === 'latin' && !optional) throw new Error(`Fontsource has no ${family} ${weight} ${style}`);
        }));
      }
    }
  }
  const ready = await Promise.all(jobs).catch((error) => { kitFail(error); throw error; });
  return ready.filter((face) => face && document.fonts.add(face)).length;
}

/** A loaded FontFace covers this family, weight and style (fonts.check() would
 *  also say yes for families nobody declared). */
function hasFace(family, weight, style) {
  for (const face of document.fonts) {
    if (face.status !== 'loaded' || face.style !== style) continue;
    if (face.family.replace(/^["']|["']$/g, '') !== family) continue;
    const [low, high = low] = face.weight.split(' ').map(Number);
    if (weight >= low && weight <= high) return true;
  }
  return false;
}

/** The files beyond latin `text` needs that `meta`'s family ships. */
function kitSubsetsFor(text, meta) {
  return [[/[Ā-˿ᴀ-ᶿḀ-ỿ†ℓⱠ-Ɀ꜠-ꟿ]/u, 'latin-ext'], [/[Ͱ-Ͽ]/u, 'greek']]
    .filter(([re, x]) => re.test(text) && meta?.subsets?.includes(x)).map(([, x]) => x);
}

/** Fontsource's id for a family: 'Source Serif 4' → 'source-serif-4'. */
function fontsourceId(family) { return family.toLowerCase().replace(/\s+/g, '-'); }

/** A package's folder at the one version @5 names now (#766). */
function fontsourceAt(id, u = `https://cdn.jsdelivr.net/npm/@fontsource/${id}@`) {
  return (fontsourceAt.c ??= {})[id] ??= fetch(u + '5/package.json').then((r) => r.json())
    .then((p) => p.version || 5, () => 5).then((v) => `${u}${v}/`);
}

/** The family's Fontsource metadata (weights, styles, subsets), or null. */
function fontsourceMeta(family) {
  fontsourceMeta.cache ??= new Map();
  const id = fontsourceId(family);
  if (!fontsourceMeta.cache.has(id)) {
    fontsourceMeta.cache.set(id, fetch(`https://api.fontsource.org/v1/fonts/${id}`)
      .then((res) => (res.ok ? res.json() : null), () => null));
  }
  return fontsourceMeta.cache.get(id);
}

// ─── Kit · viewer v2 ── the same in every recipe · postext.dev/cookbook
/** The pages as spreads on a dark desk, page 1 alone, then verso | recto,
 *  each painted when it scrolls near. Other options go to renderPageToCanvas. */
function showPages(docs, { title, width = 460, ...render } = {}) {
  const root = viewer(title);
  const pages = [docs].flat().flatMap((doc) =>
    doc.pages.map((page) => ({ doc, page, n: (doc.pageIndexOffset ?? 0) + page.index })));
  const spreads = [];
  let verso = null;
  for (const p of pages) {
    if (p.n % 2 === 1) { if (verso) spreads.push([verso, null]); verso = p; }
    else { spreads.push([verso, p]); verso = null; }
  }
  if (verso) spreads.push([verso, null]);
  const density = Math.min(window.devicePixelRatio || 1, 2);
  showPages.painter?.disconnect();
  const painter = new IntersectionObserver((entries) => {
    for (const { isIntersecting, target } of entries) {
      if (!isIntersecting) continue;
      painter.unobserve(target);
      const { doc, page } = target.postext;
      renderPageToCanvas(page, doc, target, { ...render, scale: (width * density) / page.width });
    }
  }, { rootMargin: '800px' });
  showPages.painter = painter;
  root.replaceChildren(...spreads.map((pair) => {
    const spread = document.createElement('div');
    spread.className = 'pt-spread';
    for (const p of pair) {
      const figure = document.createElement('figure');
      if (p) {
        const label = p.page.pageLabel || String(p.n + 1);
        const canvas = document.createElement('canvas');
        canvas.postext = p;
        canvas.style.aspectRatio = `${p.page.width} / ${p.page.height}`;
        canvas.setAttribute('role', 'img');
        canvas.setAttribute('aria-label', `Page ${label}`);
        const folio = document.createElement('figcaption');
        folio.textContent = label;
        figure.append(canvas, folio);
        painter.observe(canvas);
      } else figure.className = 'pt-blank';
      spread.append(figure);
    }
    return spread;
  }));
  kitStatus(`${pages.length} ${pages.length === 1 ? 'page' : 'pages'}`);
  document.documentElement.dataset.postext = 'ready';
  return pages.length;
}

/** The desk, the bar and the error reporting, created once. */
function viewer(title) {
  if (!document.getElementById('pt-kit')) {
    document.head.insertAdjacentHTML('beforeend', `<style id="pt-kit">
      :root { color-scheme: dark; }
      body { margin: 0; background: #0e1014; color: #b9bcc4; font: 13px/1.45 system-ui, sans-serif; }
      #pt-bar { position: sticky; top: 0; z-index: 1; display: flex; flex-wrap: wrap; align-items: center;
        gap: 6px 16px; padding: 10px 16px; background: rgb(14 16 20 / .92); backdrop-filter: blur(6px);
        border-bottom: 1px solid #23262d; }
      #pt-bar strong { color: #f4f1ea; font-weight: 600; }
      #pt-actions { display: flex; gap: 12px; margin-left: auto; }
      #pt-actions a, #pt-actions button { color: #d8a21a; font: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
      #pages { display: grid; justify-items: center; gap: 48px; padding: 32px 16px 72px; }
      .pt-spread { display: flex; }
      .pt-spread figure { margin: 0; width: min(460px, 44vw); }
      .pt-spread canvas { display: block; width: 100%; background: #fff;
        box-shadow: 0 1px 2px rgb(0 0 0 / .5), 0 22px 44px -16px rgb(0 0 0 / .8); }
      .pt-spread figure:first-child canvas { box-shadow: inset -14px 0 14px -14px rgb(0 0 0 / .18), 0 1px 2px rgb(0 0 0 / .5), 0 22px 44px -16px rgb(0 0 0 / .8); }
      .pt-spread figcaption { margin-top: 10px; text-align: center; font: 600 10px/1 system-ui, sans-serif;
        letter-spacing: .18em; text-transform: uppercase; color: #6c7079; }
      .pt-blank { visibility: hidden; }
      @media (max-width: 760px) {
        .pt-spread { flex-direction: column; gap: 32px; }
        .pt-spread figure { width: min(460px, 92vw); }
        .pt-blank { display: none; }
      }
    </style>`);
    document.body.insertAdjacentHTML('afterbegin',
      '<header id="pt-bar"><strong id="pt-title"></strong><span id="pt-status" role="status"></span><span id="pt-actions"></span></header>');
    document.getElementById('pt-title').textContent = document.title || 'Postext';
    addEventListener('error', (event) => kitFail(event.error ?? event.message));
    addEventListener('unhandledrejection', (event) => kitFail(event.reason));
  }
  if (title) document.getElementById('pt-title').textContent = title;
  return document.getElementById('pages')
    ?? document.body.appendChild(Object.assign(document.createElement('main'), { id: 'pages' }));
}

function kitStatus(text) {
  viewer();
  document.getElementById('pt-status').textContent = text;
}

function kitFail(error) {
  document.documentElement.dataset.postext = 'error';
  kitStatus(`Error: ${error?.message ?? error}`);
}

// ─── /Kit ───────────────────────────────────────────────────────────────────────
```

## Varianten

### Die ganze Zotero-Sammlung auflisten

Wenn der Export die Liste ist, lassen Sie die Schlüssel weg: Jeder Eintrag im Block wird gedruckt, zitiert oder nicht.

```diff
-nocite: "@fleck_genesis_1979, @merton_sociology_1973, @latour_science_1987, …"
+nocite: "@*"
```

### Den Stil wechseln

Dieselben Schlüssel ergeben ohne weitere Änderung eine Harvard-Liste (Cite Them Right).

```diff
-  style: 'chicago-author-date', // '(Gieryn 1983)', 'Shapin and Schaffer (1985)'
+  style: 'harvard-cite-them-right',
```

## Häufige Fehler

- **Ein nocite-Schlüssel ohne Treffer entfällt stillschweigend.** Ein zitierter Schlüssel ohne Literaturangabe löst unknownCitationKey aus, ein Schlüssel in nocite, der keinem Eintrag entspricht, wird dagegen ohne Warnung übergangen. Kopieren Sie die Schlüssel aus dem Export und zählen Sie die Einträge in der Liste.

- Ein Schlüssel in `nocite`, der zu keinem Eintrag passt, entfällt ohne Warnung. Kopieren Sie die Schlüssel aus dem Export, statt sie abzutippen, und zählen Sie die Einträge auf der Seite.
- Zotero bildet die Schlüssel aus den Daten: Korrigieren Sie in Zotero den Namen eines Autors oder einen Titel, kann der nächste Export den Schlüssel ändern, was jedes Zitat dieses Werks zerstört. Legen Sie die Schlüssel fest, bevor der Text geschrieben wird: Zotero liest eine Zeile `Citation Key: …` im Feld „Extra“ eines Eintrags.
- Zotero exportiert standardmäßig UTF-8, mit den Akzenten, wie sie sind. Der Block hier wurde mit der Kodierungsoption „Westlich“ exportiert, die `{\"o}` schreibt; Postext liest beides.
- Die 18. Auflage von Chicago lässt den Verlagsort weg, deshalb druckt das Feld `address` nicht, und ein Artikel mit DOI druckt die DOI und nicht die `url` von JSTOR.

## Nachweise

- Rezept: Ignacio Ferro ([@drnachio](https://github.com/drnachio))
- Schrift: Spectral (OFL-1.1), IBM Plex Sans Condensed (OFL-1.1)
- Code: MIT · Beispielinhalt: CC-BY-4.0

## Verwandte Rezepte

- [Nº 087 · Ein Kapitel einer Dissertation, zitiert nach APA 7](https://postext.dev/de/cookbook/apa-thesis-with-bibtex.md): Ein Dissertationskapitel, dessen Zitate [@key, p. 33] citeproc-js nach APA 7 formatiert; das Literaturverzeichnis entsteht aus einem BibTeX-Block. · Stufe 2 (Mittel) · Fachartikel und Wissenschaft, Berichte
- [Nº 093 · Ein Text in vier Zitierstilen](https://postext.dev/de/cookbook/one-text-four-citation-styles.md): Ein kurzer Essay mit sechs Zitaten, gebaut in APA 7, Chicago Author-Date, IEEE und ISO 690: Nur die Einstellung des Stils ändert sich. · Stufe 2 (Mittel) · Fachartikel und Wissenschaft, Berichte
- [Nº 073 · Ein Personenregister und ein Sachregister](https://postext.dev/de/cookbook/names-and-subjects-indexes.md): Marken mit index="names" speisen ein Personenregister, der Rest ein Sachregister; :::index druckt beide zweispaltig, und der Build legt die Seiten fest. · Stufe 2 (Mittel) · Lehrbücher
