Appearance
Antikythera
When you simply need to query information about an Antikythera article, annotation or information on the Antikythera project itself (such as the settings). The library provides an object that can be used to query all that information. You simply need to instantiate the Antikythera object and call one of the following methods on it.
INFO
Please contact Antikythera to get the correct 'entry' slug for your article.
javascript
import { Antikythera } from '@antikythera/antikythera'
import '@antikythera/antikythera/fonts'
const antikythera = new Antikythera({ entry: 'example-entry' })init()
By default, the Antikythera class automatically initializes the antikythera-menu Web Component and crawls the DOM for .annotation tagged elements.
You can override this behavior by passing the manual: true parameter to the Antikythera class and then calling the init() method manually:
javascript
const antikythera = new Antikythera({ entry: 'example-entry', manual: true })
// wait 2 seconds before initializing:
setTimeout(() => {
antikythera.init()
}, 2000)When manually initializing the antikythera-menu, your optional naming overrides are passed directly to the init() method:
javascript
antikythera.init({
menuName: 'antikythera-menu', // should match the name you've specified in the Antikythera class
annotationClass: '.annotation',
detectAnnotationsOnInit: true // set false here if for some reason you want to call detectAnnotations() method at a later point in time
})init() Options
| Name | Type | Required | Usage | Default |
|---|---|---|---|---|
menuName | String | false | Name of Antikythera Menu Web Component | antikythera-menu |
annotationClass | String | false | Name of Selector Class used to find inline annotations | .annotation |
detectAnnotationsOnInit | Boolean | false | The current body will be scanned for ${annotionClass} annotations | true |
reinit()
In case your website is dealing with multiple antikythera articles and you need to switch between them, a reinit method is provided to reinitialize all of the antikythera components. This method behaves just the same way as the init method does but accepts an additional entry argument which resets all of the inner workings to deal with this new entry instead of the original one. After reinitialisation an event will be emited which you can listen to for further processing on your website. This event is called antikythera:initComplete and is emitted on the document object.
javascript
const antikythera = new Antikythera({ entry: 'example-entry' })
document.addEventListener('antikythera:initComplete', (e) => {
console.log('reinitialisation complete')
console.log(e.detail.entry) // <-- latest entry
// do further processing / cleanup here...
})
setTimeout(() => {
antikythera.reinit('other-entry')
}, 3000)reinit() Options
| Name | Type | Required | Usage | Default |
|---|---|---|---|---|
entry | String | true | Name of the new antikythera entry to use | undefined |
menuName | String | false | Name of Antikythera Menu Web Component | antikythera-menu |
annotationClass | String | false | Name of Selector Class used to find inline annotations | .annotation |
detectAnnotationsOnInit | Boolean | false | The current body will be scanned for ${annotionClass} annotations | true |
detectAnnotations()
This method crawls the html body of the current page and scans for html span elements with a matching annotation class. It consequently applies styling to the found elements and sets up the interaction with the antikythera-menu component so that a corresponding annotation card is displayed when the article's annotation is in the viewport.
javascript
<span class="annotation">my-annotation</span>It will be called by default in the init() method unless specified by passing in detectAnnotationsOnInit = false as discussed previously. In most cases you will probably not need to worry about all of this and not need to call this method manually. A fitting example when you should/could use it however is for example when dealing with a multi-language website where the DOM changes depending on the current language chosen. If your website is dynamically changing the article' content it might be fitting and even required to call this method.
javascript
const toggleLanguageToEnglish = async () => {
langIsEng.value = true
await antikythera.detectAnnotations()
}
const toggleLanguageToFrench = async () => {
langIsEng.value = false
await antikythera.detectAnnotations()
}getEntry()
javascript
const article = antikythera.getEntry()Response
Details
json
{
"settings": {
"externalLinks": [
{
"linkTitle": "instagram",
"linkUrl": "https://www.instagram.com"
}
],
"shortDescription": "Journal of Philosophy of Planetary Computation.",
"blurb": "Journal of Philosophy of Planetary Computation."
},
"entry": {
"doi": "123456789",
"designers": [
{
"title": "Firstname Lastname",
"externalLink": null,
"featuredImage": {
"url": null,
"alt": null,
"caption": null
},
"biography": null
}
],
"featuredImage": {
"url": "https://cdn.sanity.io/images/y9ih15q4/production/fe543c33bd7f472edd9d40b7c899e637033e609b-3000x2024.jpg",
"alt": "a very religious scene",
"caption": null
},
"credits": "Yuri Bultheel",
"releaseDate": "2024-09-04",
"authors": [
{
"title": "Philip Maughan",
"externalLink": null,
"featuredImage": {
"url": null,
"alt": null,
"caption": null
},
"biography": null
}
],
"annotations": [
{
"featuredImage": {
"url": "https://cdn.sanity.io/images/y9ih15q4/production/fe543c33bd7f472edd9d40b7c899e637033e609b-3000x2024.jpg",
"alt": "religious scene",
"caption": "a painful scene"
},
"id": "example-annotation",
"title": "Example Annotation",
"doi": "example DOI",
"content": "Just some text about a certain annotation...",
"annotationType": "commentary",
"externalLink": "https://www.google.com"
},
{
"externalLink": "https://www.dataexcess.xyz",
"featuredImage": {
"url": "https://cdn.sanity.io/images/y9ih15q4/production/fe543c33bd7f472edd9d40b7c899e637033e609b-3000x2024.jpg",
"alt": null,
"caption": "good picture"
},
"id": "second-annotation",
"title": "A second Annotation",
"doi": null,
"content": "This is another annotation...",
"annotationType": "related"
}
],
"title": "Example Entry",
"introduction": "Just an introduction to the article.\n\n\n\nPart 1.\n\nSome more text.\n\nPart 2.\n\nSome more text.\n\n\n\nFinal words.",
"bibliography": "start of bibliography.\n\n\n\nthis is actual portable text by the way....\n\n\n\nend of bibliography."
}
}Advanced
Parameters
By default, getEntry() returns Portable Text for any richtext content. The getEntry() method exposes an optional parameter to return a String or raw HTML instead:
javascript
const articleWithStrings = antikythera.getEntry({ textStyle: 'string' })
const articleWithHtml = antikythera.getEntry({ textStyle: 'html' })| Name | Type | Required | Usage | Default |
|---|---|---|---|---|
textStyle | String | false | Format of returned richtext | portabletext |
getAnnotations()
javascript
const annotations = antikythera.getAnnotations()Side Note
The Antikythera API will only return annotations that have been added to a specific entry from within the Antikythera CMS.
Response
Details
json
[
{
"content": "is a systematic study of general and fundamental questions concerning topics like",
"scanText": {
"enableScanText": true,
"scanSegments": [
{
"scanKeyword": "philosophy",
"scanPhrase": "Sciences are born when philosophy learns to ask the right questions"
},
{
"scanKeyword": "Philosophie",
"scanPhrase": "Wissenschaften entstehen, wenn die Philosophie lernt, die richtigen Fragen zu stellen"
}
]
},
"url": "philosophy",
"annotationType": "commentary",
"externalLink": null,
"doi": null,
"title": "philosophy",
"id": "philosophy",
"featuredImage": {
"url": "https://cdn.sanity.io/images/y9ih15q4/production/cc9a720edc9fec8bde756dbb6fc655195528a9bf-520x693.jpg",
"alt": "penseur",
"caption": ""
}
},
{
"url": "cosmology",
"scanText": {
"enableScanText": true,
"scanSegments": [
{
"scanKeyword": "cosmology",
"scanPhrase": "This is the planetary in planetary computation: a cosmology in every sense of the word"
},
{
"scanKeyword": "Kosmologie",
"scanPhrase": "Dies ist das Planetarische in der planetarischen Berechnung: eine Kosmologie im wahrsten Sinne des Wortes"
}
]
},
"annotationType": "commentary",
"doi": null,
"content": "Ancient Greek"
"featuredImage": {
"url": "https://cdn.sanity.io/images/y9ih15q4/production/3d5387885f63443a8bb6e31a702eb76fba04dbbd-620x541.jpg",
"alt": null,
"caption": ""
},
"externalLink": null,
"title": "cosmology",
"id": "cosmology"
}
]Advanced
Parameters
By default, getAnnotations() returns Portable Text for any richtext content. The getAnnotations() method exposes an optional parameter to return a String or raw HTML instead:
javascript
const annotationsWithStrings = antikythera.getAnnotations({ textStyle: 'string' })
const annotationsWithHtml = antikythera.getAnnotations({ textStyle: 'html' })| Name | Type | Required | Usage | Default |
|---|---|---|---|---|
textStyle | String | false | Format of returned richtext | portabletext |
getSettings()
javascript
const settings = antikythera.getSettings()Response
Details
json
{
"externalLinks": [
{
"linkTitle": "instagram",
"linkUrl": "https://www.instagram.com"
}
],
"shortDescription": [
{
"markDefs": [],
"children": [
{
"_type": "span",
"marks": [],
"text": "A Journal of Philosophy of Planetary Computation.\n\n",
"_key": "45fb4d058d160"
}
],
"_type": "block",
"style": "normal",
"_key": "2d544012f8bd"
},
{
"markDefs": [],
"children": [
{
"marks": [],
"text": "Coming Spring 2025",
"_key": "d079d3f606f00",
"_type": "span"
}
],
"_type": "block",
"style": "normal",
"_key": "5ee97e6097ec"
}
],
"blurb": "A Journal of Philosophy of Planetary Computation."
}Advanced
Parameters
By default, getSettings() returns Portable Text for any richtext content. The getSettings() method exposes an optional parameter to return a String or raw HTML instead:
javascript
const settingsWithStrings = antikythera.getSettings({ textStyle: 'string' })
const settingsWithHtml = antikythera.getSettings({ textStyle: 'html' })| Name | Type | Required | Usage | Default |
|---|---|---|---|---|
textStyle | String | false | Format of returned richtext | portabletext |
Options
When creating the Antikythera class, only the entry parameter is required.
| Name | Type | Required | Usage | Default |
|---|---|---|---|---|
entry | String | true | Entry slug (provided by Antikythera) | undefined |
menuName | String | false | Name of Antikythera Menu Web Component | antikythera-menu |
annotationClass | String | false | Name of Selector Class used to find inline annotations | .annotation |
customCss | Boolean | false | Turn on to disable the .annotation custom CSS. | false |
manual | Boolean | false | Option to initialize Antikythera Web Component Manually (via the antikythera.init() method) | false |
detectAnnotationsOnInit | Boolean | false | Option to scan for annotations in init method (as opposed to manually via the antikythera.detectAnnotations() method) | true |
environment | String | false | Option to specify dataset. | production |
Advanced queries
The Antikythera npm package is built on top of a Sanity CMS backend. If its capabilities are insufficient, you can use the Sanity API directly to perform more complex queries. Please consult the Sanity Client Documentation to learn how to use the Sanity API. Once you have the projectId, you can query the database with the Sanity client like this:
INFO
Please contact Antikythera to get the sanity projectId.
javascript
import { createClient } from '@sanity/client'
const client = createClient({
projectId: '${antikythera-project-id}',
dataset: 'production',
apiVersion: '2023-06-01',
useCdn: false
})
const query = `{
'entry': *[_type == "entry" && url.current == "example-entry"][0]{
title,
introduction,
content[]{
...,
"markDefs": markDefs[] {
...,
"slug": @->url.current
}
}
}
}`
const fetchEntry = async () => {
try {
const result = await client.fetch(query)
console.log('Entry data:', result.entry)
return result.entry
} catch (error) {
console.error('Error fetching entry:', error)
}
}
const entry = await fetchEntry()