Backlinks
Built-in XSLT, CSS and JavaScript assets, and how to override them
XSLT¶
XSLT is a functional, Turing-complete XML transformation language.
LinkedDataHub's XSLT 3.0 stylesheets work by transforming the RDF/XML response body from the underlying HTTP API. Additional metadata from RDF vocabularies is used to improve the user experience.
Plain RDF/XML¶
RDF/XML is an important RDF syntax which functions as a bridge to the XML technology stack. The stylesheets use Jena's "plain" RDF/XML output which groups statements by subject and does not nest resource descriptions. This allows for predictable XPath patterns:
| XPath | Represents |
|---|---|
/rdf:RDF |
The RDF graph |
/rdf:RDF/rdf:Description or /rdf:RDF/*[*][@rdf:about] | /rdf:RDF/*[*][@rdf:nodeID] |
Resource description which contains properties |
/rdf:RDF/rdf:Description/@rdf:about |
Subject resource URI |
/rdf:RDF/rdf:Description/@rdf:nodeID |
Subject blank node ID |
/rdf:RDF/rdf:Description/* |
Predicate (e.g. rdf:type) whose URI is concat(namespace-uri(), local-name()) |
/rdf:RDF/rdf:Description/*/@rdf:resource |
Object resource |
/rdf:RDF/rdf:Description/*/@rdf:nodeID |
Object blank node ID |
/rdf:RDF/rdf:Description/*/text() |
Literal value |
The Northwind Chai product in that shape — the input that the templates match:
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:schema="https://schema.org/">
<rdf:Description rdf:about="https://localhost:4443/products/1/#this">
<rdf:type rdf:resource="https://schema.org/Product"/>
<schema:name>Chai</schema:name>
<schema:category rdf:resource="https://localhost:4443/categories/1/#this"/>
<schema:provider rdf:resource="https://localhost:4443/suppliers/1/#this"/>
</rdf:Description>
</rdf:RDF>Stylesheet structure¶
XSLT stylesheet components used by LinkedDataHub:
- Includes
<xsl:include>is used to include one stylesheet into another. The inclusion mechanism is specified in 3.10.2 Stylesheet Inclusion of the XSLT 3.0 specification. The templates from the included stylesheets have the same priority as those of the including stylesheet.- Imports
<xsl:import>is used to import one stylesheet into another. The import mechanism is specified in 3.10.3 Stylesheet Import of the XSLT 3.0 specification. The templates from the imported stylesheets have lower priority than those of the importing stylesheet.- Parameters
- XSD-typed global parameters passed to the stylesheet
- Keys
- Lookup keys
- Templates
- Template rules for XML node processing
One XSLT stylesheet can be specified per dataspace. In order to reuse LinkedDataHub's built-in templates, it should import the system stylesheet layout.xsl and only override the necessary templates. That is not a requirement, however; a stylesheet can also use its own independent transformation logic.
If there is no stylesheet specified for the dataspace, the system stylesheet is used. It defines the overall layout and imports resource-level and container-specific stylesheets, as well as per-vocabulary stylesheets.
Note that LinkedDataHub itself imports stylesheets from Web-Client, which uses the same template modes but produces a much simpler layout.
The same XSLT 3.0 stylesheets run in two environments. On the server they are executed by Saxon-HE to produce the initial HTML response. In the browser they are executed by Saxon-JS 3, which provides IXSL (Interactive XSLT extensions) for reading and manipulating the browser DOM.
There is a dedicated client-side stylesheet client.xsl that drives the browser. After the server-rendered page loads, its main template renders the layout client-side — the left sidebar, navigation, document
and tab panes, content blocks, forms and modal dialogs are injected into the DOM using
IXSL (e.g. ixsl:append-content). It also handles all subsequent navigation, so following links and switching documents
re-renders in place without a full page reload. It imports and reuses the same document-,
resource- and property-level templates as the server-side system stylesheet, but avoids
loading per-vocabulary stylesheets in order to improve page load time. Templates of
the client-side stylesheet can also be overridden.
Namespaces¶
| Prefix | Namespace | Vocabulary | Description |
|---|---|---|---|
rdf: |
http://www.w3.org/1999/02/22-rdf-syntax-ns# |
The RDF Concepts Vocabulary | Namespace for the RDF/XML elements, mostly used for matching input data |
srx: |
http://www.w3.org/2005/sparql-results# |
SPARQL Query Results XML Format | Namespace for the SPARQL Query Results XML elements, mostly used for matching input data |
xsl: |
http://www.w3.org/1999/XSL/Transform |
XSL Transformations (XSLT) 3.0 | Namespace for the XSLT stylesheet elements |
ixsl: |
http://saxonica.com/ns/interactiveXSLT |
Saxon Interactive XSLT extensions | Namespace for the Interactive XSL extensions |
ldh: |
https://w3id.org/atomgraph/linkeddatahub# |
LinkedDataHub vocabulary | LinkedDataHub concepts, also used for LinkedDataHub's own component template modes (named after the design system's components) |
xhtml: |
http://www.w3.org/1999/xhtml |
XHTML 1.0 | XHTML namespace, also used for generic (X)HTML templates |
ac: |
https://w3id.org/atomgraph/client# |
Web-Client vocabulary | Client-side concepts |
lds: |
https://w3id.org/atomgraph/linkeddatahub/dataspaces# |
LinkedDataHub dataspace ontology | LinkedDataHub dataspace concepts |
lacl: |
https://w3id.org/atomgraph/linkeddatahub/admin/acl# |
LinkedDataHub ACL ontology | ACL concepts |
Templates¶
XSLT template components:
- Match
- XPath-based match pattern which either does or does not match an XML node
- Mode
- Groups templates and distinguishes them from other groups that share the same match patterns (e.g. different layout modes)
- Parameters
- XSD-typed parameters passed to the template invocation
- Body
- Contains the XML output nodes as well as XSLT processing instructions
XSLT processing starts at the root of the RDF/XML document and produces HTML elements by applying templates to all of the RDF/XML nodes while moving down the XML tree. In other words, it starts at the graph level, moves down to resource description elements, then to property elements, and ends with identifier attributes and literal text nodes.
Templates are applied (invoked) using <xsl:apply-templates>. Mode can be specified, e.g. <xsl:apply-templates mode="ac:BlockHeader">. To stay in the current mode without explicitly specifying it, use <xsl:apply-templates mode="#current">. <xsl:with-param> is used to supply parameters.
LinkedDataHub provides the following default template modes, which are used to render the layout modes:
- Graph-level modes that apply to
rdf:RDF- default mode renders full resource descriptions
ac:Listrenders a list of resourcesac:ResultsTablerenders a table with resources as rows and properties as columnsac:Gridrenders a gallery of thumbnailsac:Maprenders resources with coordinates on a mapac:Graphrenders the resources as a node-and-edge graphac:ResourceFormrenders an RDF/POST form for creating new resources or editing an existing one
- Resource-level template modes that apply to
rdf:Description- default mode renders one full resource description (by default header and property list)
ac:BlockHeaderrenders resource header (by default with type information)ac:PropertyEditorrenders definition list with property names and values (by default grouped by resource types)
When adding new user-defined modes, it is recommended to choose a new namespace for them as well as a user-defined prefix.
An example of a template that matches rdf:Description:
<xsl:template match="*[*][@rdf:about] | *[*][@rdf:nodeID]">
<xsl:param name="id" as="xs:string?"/>
<xsl:param name="class" as="xs:string?"/>
<div>
<xsl:if test="$id">
<xsl:attribute name="id" select="$id"/>
</xsl:if>
<xsl:if test="$class">
<xsl:attribute name="class" select="$class"/>
</xsl:if>
<xsl:apply-templates select="." mode="ac:BlockHeader"/>
<xsl:apply-templates select="." mode="ac:PropertyEditor"/>
</div>
</xsl:template>Applied to the Chai description above, it produces markup of this shape (abridged):
<div about="https://localhost:4443/products/1/#this" typeof="https://schema.org/Product">
<h2>Chai</h2>
<dl>
<dt>Name</dt>
<dd>Chai</dd>
<dt>Category</dt>
<dd><a href="https://localhost:4443/categories/1/#this">Beverages</a></dd>
<dt>Provider</dt>
<dd><a href="https://localhost:4443/suppliers/1/#this">Exotic Liquids</a></dd>
</dl>
</div>Live: /products/1/ — the same description, rendered by the full stylesheet
ldh:ContentList mode renders the content specified by the rdf:_1, rdf:_2, … values of the current document.
There are a few special template modes such as ac:label and ac:description, together with the related ac:label() and ac:description() functions, which are
used not to render layout but to extract metadata from resource descriptions. They
can be used to retrieve a resource label and description no matter which RDF
vocabularies are used in the data. They do so by invoking templates of the respective
mode from vocabulary-specific stylesheets.
Overriding templates¶
Templates are overridden by redefining them in the importing stylesheet and providing the same or more specific match pattern and the same mode. The XSLT specification defines exactly how template priorities are determined in 6.4 Conflict Resolution for Template Rules.
The overriding template can then get the output of the overridden template by invoking
either <xsl:apply-imports> or <xsl:next-match>. Read more in 6.7 Overriding Template Rules.
Always override the most specific template, i.e. if you want to change how a property is rendered, do not override the template for the resource description — override only the one for the property.
Keys¶
Keys are a lookup mechanism. They are defined on the stylesheet level using <xsl:key> and invoked using the
key() function. For example:
<xsl:key name="resources" match="*[*][@rdf:about] | *[*][@rdf:nodeID]" use="@rdf:about | @rdf:nodeID"/>
<xsl:template match="*">
<xsl:for-each select="key('resources', ldh:base-uri(.))">
<xsl:value-of select="ac:label(.)"/>
</xsl:for-each>
</xsl:template>The key definition matches rdf:Description elements and uses their identifiers (URI or blank node ID). The template then looks
up the RDF description of the current resource, i.e. the resource with URI that equals ldh:base-uri(.) which is the absolute URI of the current document, and outputs its label.
Loading data¶
The stylesheet processes one main RDF/XML document at a time, supplied by LinkedDataHub's
HTML writer. However, it is possible to load additional XML documents over HTTP
using the document() XSLT function. To avoid XSLT errors on any possible error responses, it is advisable
to do a conditional check using the doc-available() function before doing the actual document() call.
For example, instead of hardcoding the title of this document as Stylesheets, you can use the following code to load it and output it on the fly:
<xsl:value-of select="key('resources', 'https://docs.linkeddatahub.com/reference/stylesheets/', document('https://docs.linkeddatahub.com/reference/stylesheets/'))"/>In case this document changes its title, all such references would automatically render the updated title. On the other hand, it incurs the overhead of making an HTTP request.
LinkedDataHub's default stylesheets use this feature extensively. In fact, one HTML page is rendered from a dozen RDF/XML documents.
The descriptions of the instance's dataspaces are available through the lds:context() function, which returns the
dataspace context on both the server and the client. lds:dataspace-description() returns the current
dataspace's description from it, including its rdf:type (lds:EndUserDataspace or lds:AdminDataspace) and, for an
end-user dataspace, the lds:admin base URI. Stylesheets receive no other configuration: the service endpoints and credentials
are not available to them.
Built-in ontologies, as well as some other system and well-known ontologies, have a local copy in
each LinkedDataHub instance. As a result, retrieving their descriptions by dereferencing
their URIs using document() does not incur an HTTP request and is much faster. The URI-to-file mapping
is defined as Jena's location mapping and can be found in
location-mapping.ttl and prefix-mapping.ttl.
Client-side stylesheets use <ixsl:schedule-action> (deprecated) and <ixsl:promise> to load XML documents asynchronously. The metadata required to render forms — constructors,
SHACL shapes, and property and object descriptions — is loaded client-side through
chains of <ixsl:promise>, so the browser fetches it on demand rather than the server fetching everything up
front.
Parameters¶
Both global (i.e. stylesheet-level) and template parameters are declared using <xsl:param>. LinkedDataHub sets the following global parameters:
| Parameter | Type | Description |
|---|---|---|
$lds:origin |
xs:anyURI |
Origin of the application shell that serves the static assets (the pane-scoped counterpart
is the lds:origin() function) |
$foaf:Agent |
document-node()? |
RDF/XML metadata of the authenticated agent (if any) |
$acl:agent |
xs:anyURI? |
URI of the authenticated agent |
$ldh:requestUri |
xs:anyURI |
Full request URI including query string |
$ldh:httpHeaders |
map(xs:string, xs:string*) |
HTTP response headers by name — Content-Language, Link, Memento-Datetime etc. are read from it |
$ldh:ajaxRendering |
xs:boolean |
Whether client-side (Saxon-JS) rendering is enabled |
$ldh:renderSystemResources |
xs:boolean |
Whether system resources are included in the rendering |
$ac:contextUri |
xs:anyURI |
Root deployment URI, against which system endpoints such as oauth2/* are resolved |
$ac:uri |
xs:anyURI? |
URI of the external resource when the request goes through the Linked Data proxy; absent for local documents (prefer the ac:uri() function) |
Former parameters $ac:mode, $acl:mode, $sd:endpoint, $ac:langs and $lds:Dataspace have been
replaced by the functions ac:mode(), acl:mode(), sd:endpoint(), ac:langs() and lds:dataspace() —
their values can change per pane or per response, which a global parameter cannot
express.
Functions¶
LinkedDataHub and Web-Client provide XSLT functions available to all stylesheets.
URI utility functions¶
| Function | Returns | Runs on | Description |
|---|---|---|---|
ac:absolute-path($href as xs:anyURI) |
xs:anyURI |
Both | Strips query string and fragment from a URI, returning the path-only form |
ac:build-uri( |
xs:anyURI? |
Both | Appends a percent-encoded query string built from $query-params to $absolute-path |
ac:document-uri($uri as item()) |
xs:anyURI |
Both | Strips the fragment identifier from a URI, making it suitable for use with document() |
ac:fragment-id($uri as xs:anyURI) |
xs:string? |
Both | Extracts the fragment identifier (the part after #) |
ac:uri() |
xs:anyURI? |
Both | URI of the external resource being viewed through the Linked Data proxy; absent for local documents. The server reads it from the $ac:uri parameter, the browser from the uri query parameter |
lds:origin() |
xs:anyURI |
Both | Returns the origin (scheme, host and port) of the current dataspace, e.g. https://localhost:4443/. Used to build absolute URIs for static resources and same-site requests |
lds:base() |
xs:anyURI |
Both | Returns the base URI of the current dataspace |
ldh:request-uri() |
xs:anyURI |
Both | Returns the full request URI including query string |
ldh:base-uri($arg as node()) |
xs:anyURI |
Both | Returns the base URI of an XML node; wraps the built-in base-uri() |
ldh:href($uri as xs:anyURI?) |
xs:anyURI |
Both | Resolves a URI to a local href, proxying external URIs through the LinkedDataHub proxy.
Overloads accept $query-params as map(xs:string, xs:string*) and $fragment as xs:string? |
ldh:parse-href($href as xs:anyURI) |
map(xs:string, item()?) |
Both | Inverse of ldh:href() — parses a local href back into its target URI, query parameters and fragment. Shared
by the client-side navigation handlers |
ldh:query-params() |
map(xs:string, xs:string*) |
Both | Parses the current request URI's query string into a map of parameter names to values |
ldh:build-query($mode as xs:anyURI*) |
map(xs:string, xs:string*) |
Both | Builds the mode query parameter map that selects the given layout mode(s) |
ldh:parse-query-params($query-string as xs:string) |
map(xs:string, xs:string*) |
Both | Parses a URL query string into a map of key to value(s) |
ldh:url-decode($encoded-string as xs:string) |
xs:string |
Both | Percent-decodes a URL-encoded string |
RDF metadata functions¶
| Function | Returns | Runs on | Description |
|---|---|---|---|
ac:label($resource as element()) |
xs:string? |
Both | Extracts a human-readable label from an RDF resource description, trying multiple vocabulary properties |
ac:description($resource as element()) |
xs:string? |
Both | Extracts a human-readable description from an RDF resource description |
ac:image($resource as element()) |
attribute()* |
Both | Extracts image references (e.g. foaf:depiction, foaf:img) from a resource description |
ac:property-label($property as element()) |
xs:string? |
Both | Label of a predicate element, capitalized — resolved by the ac:property-label mode templates. An overload accepts $property-metadata as document-node(), the property descriptions loaded for the current document |
ac:object-label($object as node()) |
xs:string? |
Both | Label of a property's object: a resource-valued object is looked up in the current
graph, the loaded metadata or its dereferenced document, falling back to the URI's
fragment or last path segment; a literal yields its lexical value. An overload accepts
$object-metadata as document-node() |
ac:mode($doc as document-node()) |
xs:anyURI |
Both | The active layout mode of the given document. Replaces the former $ac:mode parameter |
ac:langs() |
xs:string* |
Both | The reader's accepted languages, deduplicated to primary subtags and always ending
with en as a fallback — negotiated server-side, read from navigator.languages in the browser |
ac:lang-rank($value as element()) |
xs:integer |
Both | Ranks a property value's language against ac:langs(), so values sort by the reader's preference |
ldh:date-time($value as xs:string?) |
xs:dateTime? |
Both | Parses a lexical value into a xs:dateTime where possible |
ldh:datatype-family($datatype as xs:anyURI?) |
xs:string |
Both | Groups an XSD datatype into a family (numeric, dateTime, string etc.) — drives table column sorting |
ldh:sort-key($key as xs:string?, $datatype as xs:anyURI?) |
xs:anyAtomicType? |
Browser | Converts a lexical value into a typed sort key for its datatype family |
ldh:sort-key-lexical($resource as element(), $predicates as xs:anyURI*) |
xs:string? |
Browser | The lexical sort key of a resource: coalesces across $predicates in order, preferring values whose language matches the reader's first accepted language.
Feeds ldh:sort-key() |
ldh:sort-datatype($resources as element()*, $predicates as xs:anyURI*) |
xs:anyURI? |
Browser | The datatype shared by a sort column's literals; absent when the column mixes datatypes
or carries plain literals or resources. Keys the typed re-sort that keeps a view block's
client-side order agreeing with its SPARQL ORDER BY |
HTTP and SPARQL functions¶
| Function | Returns | Runs on | Description |
|---|---|---|---|
ldh:query-result($endpoint as xs:anyURI, $query as xs:string) |
document-node()? |
Both | Runs $query against $endpoint over the SPARQL Protocol and returns the response: RDF/XML for CONSTRUCT and DESCRIBE, SPARQL Results XML for SELECT and ASK. Call it from any stylesheet: each engine has its own implementation. Server-side,
it POSTs the query as application/sparql-query through ldh:send-request(), so a long query does not hit a URL length limit; the result is empty when the response
has no body. Client-side, it sends a GET and loads the response with document(). An endpoint on another origin is fetched through the Linked Data proxy (?uri=). document() is synchronous, so the page waits for the response. SaxonJS also keeps the document
for the rest of the page session, so a repeated query does not reach the network.
A failed request raises a dynamic error, so wrap the call in xsl:try when the endpoint may be unavailable. Example: ldh:query-result(sd:endpoint(), 'DESCRIBE <' || @rdf:about || '>') |
ldh:send-request( |
document-node()? |
Server | Sends an HTTP request with any $method and parses the response body as XML; returns an empty sequence when the response
has no body. A body is sent only when both $media-type and $body are given, and $body is sent as its string value, so serialize a node before passing it. $headers maps a header name to its value, of which only the first is sent; pass map{} for none. A response status outside 2xx, a timeout or an unparseable body raises
a dynamic error that xsl:try can catch. Server-side only: it is the SendHTTPRequest Java extension function and has no browser declaration, so calling it from a stylesheet
that also runs in the browser fails to compile. Guard such a call with use-when="system-property('xsl:product-name') = 'SAXON'"; for a SPARQL query, call ldh:query-result() instead, which works on both sides. In the browser, ixsl:http-request() is the asynchronous counterpart. Example: ldh:send-request($href, 'GET', (), (), map{ 'Accept': 'application/rdf+xml' }) |
ldh:link-targets($link-headers as xs:string*, $marker as xs:string) |
xs:anyURI* |
Both | Extracts the target URIs of the Link response header entries, across all Link header values, whose parameters contain $marker (e.g. a rel value) — on the server, acl:mode() and ldh:timemap() read the response's Link header through it; in the browser, it reads each response's Link header into the values that acl:mode(), sd:endpoint() and ldh:timemap() return |
ldh:reserialize($doc as document-node()) |
document-node() |
Both | Round-trips a document through serialization, normalizing it |
Dataspace and access functions¶
| Function | Returns | Runs on | Description |
|---|---|---|---|
sd:endpoint() |
xs:anyURI |
Both | SPARQL endpoint of the current data pane — the local endpoint, or the remote dataspace's
when browsing another dataspace through the proxy. Replaces the former $sd:endpoint parameter |
lds:context() |
document-node()? |
Both | RDF/XML description of the instance's dataspaces, read from the dataspace context; absent if it cannot be loaded. Replaces the former $lds:Context parameter |
lds:dataspace-description() |
element()* |
Both | Description of the current dataspace, looked up in lds:context() by origin |
lds:dataspace() |
xs:anyURI? |
Browser | URI of the dataspace resource describing the current dataspace. Replaces the former
$lds:Dataspace parameter (its description is returned by lds:dataspace-description()) |
acl:mode() |
xs:anyURI* |
Both | Access modes granted on the current document, read from the response's Link headers. Replaces the former $acl:mode parameter |
Versioning functions¶
| Function | Returns | Runs on | Description |
|---|---|---|---|
ldh:memento-datetime() |
xs:string? |
Both | The Memento-Datetime of the currently viewed historical version, absent on the live document |
ldh:timemap() |
xs:anyURI? |
Both | The document's TimeMap URI from the Link response header; absent when the document is not versioned |
Utility functions¶
| Function | Returns | Runs on | Description |
|---|---|---|---|
ac:uuid() |
xs:string |
Both | Generates a random UUID |
ldh:hash-code($string as xs:string) |
xs:integer |
Browser | Deterministic 32-bit djb2 hash of a string's codepoints — the counterpart to ac:uuid() for identifiers that must survive a reload because they are re-derived rather than
stored |
The many remaining ac: and ldh: functions are internal machinery, not a stable API, and are deliberately not documented
here: the client-side render-*, load-*, set-*, *-response and *-thunk families
together with the promise, progress, cursor and DOM helpers they call; the constructor,
form and SPARQL-manipulation helpers behind the editing
UI; and Web-Client functions that LinkedDataHub has superseded, such as ac:construct() and the SVG rendering helpers.
Other assets¶
CSS¶
The stylesheet links are emitted by the ac:Stylesheets XSLT template mode, in cascade order: the vendored design-system stylesheets — fonts.css, colors_and_type.css (tokens), app.css (components; it imports core.css, which imports controls.css and overlays.css) and retro.css (the skin) — then ldh.css, LinkedDataHub's one app layer over the kits, loaded last so it wins the ties it
is written to win. Feature-specific stylesheets are linked ahead of them and only
when needed: rdfa-editor.css for rich-text editing when an agent is authenticated, yasqe.css for the SPARQL editor. Dataspace stylesheets that import layout.xsl can override the ac:Stylesheets template mode to add or replace stylesheets.
Components are named by class rather than by element: ac-* for the design system's own components (ac-btn, ac-alert, ac-card, ac-badge) and ldh-* for the ones LinkedDataHub adds over them (ldh-block, ldh-actionbar, ldh-address). A component is varied by stacking one class from each axis onto it, not by a compound
name — the action bar's Actions button is ac-btn in-neutral ap-outline sz-md, a form's Save button ac-btn in-primary ap-solid sz-md.
| Axis | Classes |
|---|---|
| Intent | in-primary, in-neutral, in-accent, in-destructive, in-negative, in-inverse |
| Appearance | ap-solid, ap-outline, ap-ghost, ap-fixed |
| Size | sz-xs, sz-sm, sz-md, sz-lg, sz-xl |
| Variant | va-informative, va-positive, va-negative, va-neutral, va-emphasis, va-enclosed, va-line, va-hero, va-inverse, va-fixed — carried by components that vary by kind rather than by intent, such as ac-alert |
That vocabulary is the contract the templates emit, so restyling is a matter of writing
rules against those classes. Changes belong in a stylesheet of the dataspace's own:
ldh.css is the platform's app layer and is replaced wholesale on upgrade. Override the ac:Stylesheets template mode and call xsl:next-match first, which emits the platform's links and leaves yours last in the cascade, where
they win the ties they need to win. Omitting xsl:next-match drops the vendored kits along with ldh.css and leaves the components unstyled.
JavaScript¶
The script links are emitted by the xhtml:Script XSLT template mode. It loads functions.js, the Saxon-JS 3 runtime together with the compiled client stylesheet (when client-side
rendering is enabled), and feature-specific libraries: the YASQE SPARQL editor, the
SPARQL builder, OpenLayers for maps, Google Charts for charts, and three.js with 3d-force-graph
for the 3D graph mode.
LinkedDataHub only uses JavaScript for the functionality that cannot be achieved using client-side XSLT.
To customize the built-in stylesheets, follow the Change layout and Build apps guides