Skip to content

End-user oriented frontpage, footer and person listings - #22

Merged
namedgraph merged 5 commits into
masterfrom
feat/end-user-app-polish
Sep 2, 2026
Merged

End-user oriented frontpage, footer and person listings#22
namedgraph merged 5 commits into
masterfrom
feat/end-user-app-polish

Conversation

@namedgraph

Copy link
Copy Markdown
Member

Makes the deployed dataspace read as a public data portal rather than a developer demo, and gives every person listing usable filter and sort columns.

Frontpage (app/root.ttl)

The intro dropped its RDF vocabulary — knowledge graph, global URIs, Wikidata/EU vocabulary alignment, the SPARQL spec link and the GitHub EXAMPLES.md link — for what the site is and what you can do with it: official registry data in one place, linked so you can walk from a county to its municipalities or from an MP to their committees and party. dct:description got the same treatment (it renders directly above the intro), the browse prompt lost LDH's "konteineriai" jargon, and the factions chart no longer explains itself in terms of the temporal model ("narystė dar neturi pabaigos datos"). The Turtle comments explaining the DESCRIBE/GridMode constraints stay — they never reach the page.

Footer (files/layout.xsl)

Replaces the stock LinkedDataHub footer with an LTLOD one: own wordmark, dataset shortcuts, source attribution (Registrų centras, get.data.gov.lt, Seimo kanceliarija, Wikidata), licence, and one column of developer entry points — SPARQL, example queries, dataset downloads, {base}ns — which is where they belong instead of the frontpage.

It keeps the stock markup contract, because app.css styles the footer structurally: .ldh-footer > .cols is a 1.4fr repeat(4, 1fr) grid, so exactly one .brand-col plus four .col children, each a .ftitle followed by bare <a>s, then .legal with two space-between spans. The only deviation is the wordmark .mark, restyled inline to the Lithuanian tricolour with the class geometry kept.

Footer-only in layout.xsl rather than overrides.xsl: bs2:Footer is applied once server-side and never re-rendered during CSR, so it needs no client-side twin and no SEF rebuild.

Person listings (app/persons.ttl, app/ns.ttl, files/client.xsl)

The persons/ container swaps the stock ldh:ChildrenView for its own ldh:View + sp:Select in ac:GridMode. LDH derives a view's facet pills and sort options from the BGP triples whose subject is the query's first projected variable and whose object is a variable, so the focus has to be the person entity (?person = {doc}#this) rather than the dh:Item document — that is where foaf:givenName / foaf:familyName / foaf:gender / ltlod:nominatedBy sit. The stock ChildrenView projects the document and offers no person columns at all. The same columns go into the person-valued 1:N views (:CurrentMembers, :FormerMembers, :NominatedMembers, :MembersElectedHere) so every person listing filters and sorts alike, and the faceted predicates get rdfs:labels in {base}ns (an unlabelled predicate degrades to its local name in the pill).

That needed a performance fix to be usable. To label a filter pill's values LDH appends an OPTIONAL with a 7-way alternative property path, and ARQ does not resolve an alternative path through the quad indexes inside a GRAPH block — it walks all ~117k named graphs, ~104 s per pill, a 500 in the browser. The ldh:bgp-value-counts override emits the equivalent variable predicate + FILTER (?labelProp IN (…)) — same predicates in the same order, so SAMPLE(?label) is unchanged — at ~0.1 s per pill.

Two rules this surfaced (CLAUDE.md)

  • Declare xmlns="http://www.w3.org/1999/xhtml" on xsl:stylesheet in any override that emits literal result elements. LDH's own layout.xsl declares it, so its bare <div>s are XHTML; without it ours land in no namespace and the serialiser emits a stray xmlns="" on the block. HTML parsers ignore the attribute, but namespace-sensitive XPath/XSLT matching on xhtml:* then misses those nodes. Verified on the running server before and after.
  • docker compose restart linkeddatahub does not pick up an edited mounted stylesheet — it keeps serving the one compiled at the previous start. docker compose up -d --force-recreate linkeddatahub does, followed by an nginx/varnish restart per the 502 gotcha.

Notes

  • docker-compose.override.yml pins the runtime image to LDH e38f693d; the design system the footer targets only exists there, and the SEF and layout override are compiled and mounted against it.
  • .gitignore now lists docker-compose.override.yml, which is inert while the file stays tracked. Untracking it would also drop the tdb-loader service and the TZ/signup settings that make load and make up rely on, so it stays committed for now — worth a separate decision.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PoiMWyMGtWZos9wWoVUCwg

namedgraph and others added 5 commits August 29, 2026 21:49
app/persons.ttl — replace the stock ldh:ChildrenView with an ldh:View +
sp:Select in ac:GridMode. LDH derives a view's facet pills and sort options
from the BGP triples whose subject is the query's FIRST projected variable and
whose object is a variable, so the focus has to be the person entity
(?person = {doc}#this) rather than the dh:Item document — that is where
foaf:givenName / foaf:familyName / foaf:gender / ltlod:nominatedBy sit. The
stock ChildrenView projects the document and therefore offers no person
columns at all. Only ?person is projected (LDH wraps the SELECT into
DESCRIBE *, so every projected term becomes a rendered card), sorted by family
then given name.

app/ns.ttl — carry the same columns into the person-valued 1:N views
(:CurrentMembers, :FormerMembers, :NominatedMembers, :MembersElectedHere) so
every person listing filters and sorts alike, and label the faceted predicates
(a predicate with no rdfs:label in {base}ns degrades to its local name in the
pill).

files/client.xsl — override ldh:bgp-value-counts, without which the pills are
unusable. To label a filter pill's values LDH appends an OPTIONAL with a
7-way alternative property path, and ARQ does not resolve an alternative path
through the quad indexes inside a GRAPH block: it walks all ~117k named
graphs, ~104 s per pill (a 500 in the browser). The override emits the
equivalent variable predicate + FILTER (?labelProp IN (…)) — same predicates
in the same order, so SAMPLE(?label) is unchanged — at ~0.1 s per pill. Also
tidies the :Memberships table client-side (role / organization / memberDuring
only, no anchor column, the interval rendered as its dct:title period text).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PoiMWyMGtWZos9wWoVUCwg
CLAUDE.md — files/layout.xsl now also replaces the stock bs2:Footer, so record
what the replacement has to honour: app.css styles the footer structurally
(.ldh-footer > .cols is a 1.4fr repeat(4, 1fr) grid, hence exactly one
.brand-col plus four .col children, each a .ftitle followed by bare <a>s, then
.legal with two space-between spans), so the markup contract is the design
system and only the content is ours. The one deviation is the wordmark .mark,
restyled inline to the Lithuanian tricolour with the class geometry kept.

It belongs in layout.xsl rather than overrides.xsl because bs2:Footer is
applied once server-side and never re-rendered during CSR — so unlike the
block suppression it needs no client-side twin and no SEF rebuild.

Two rules the footer work surfaced:

- declare xmlns="http://www.w3.org/1999/xhtml" on xsl:stylesheet whenever the
  override emits literal result elements. LDH's own layout.xsl declares it, so
  its bare <div>s are XHTML; without it ours land in no namespace and the
  serialiser emits a stray xmlns="" on the block. HTML parsers ignore the
  attribute, but namespace-sensitive XPath/XSLT matching on xhtml:* misses
  those nodes.
- `docker compose restart linkeddatahub` does NOT pick up an edited mounted
  stylesheet — it keeps serving the one compiled at the previous start.
  `docker compose up -d --force-recreate linkeddatahub` does, followed by an
  nginx/varnish restart per the 502 gotcha.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PoiMWyMGtWZos9wWoVUCwg
docker-compose.override.yml — the new design system (the ldh-footer grid the
LTLOD footer override targets, the vendored latin-ext typefaces, the acl:Write
gate on the block edit pencil) only exists in the image built from that LDH
commit, so the SEF and the layout override are both compiled and mounted
against it. Note .gitignore now lists docker-compose.override.yml, which is
inert while the file stays tracked — untracking it would also drop the
tdb-loader service and the TZ/signup settings that make load and make up rely
on, so it stays committed for now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PoiMWyMGtWZos9wWoVUCwg
@namedgraph
namedgraph merged commit 64721de into master Sep 2, 2026
1 check passed
@namedgraph
namedgraph deleted the feat/end-user-app-polish branch September 2, 2026 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant