Skip to content

Design system port: replace Bootstrap 2 with the LinkedDataHub design system - #371

Open
namedgraph wants to merge 61 commits into
developfrom
rf-design-system
Open

Design system port: replace Bootstrap 2 with the LinkedDataHub design system#371
namedgraph wants to merge 61 commits into
developfrom
rf-design-system

Conversation

@namedgraph

Copy link
Copy Markdown
Member

Ports the core end-user UI from Bootstrap 2.3.2 to the LinkedDataHub design system (vendored export), replacing markup — not skinning — while keeping every CSR interaction contract intact. Verified throughout against the running instance with Playwright (owner WebID client cert), screenshot-by-screenshot.

What's ported (design markup, div-based, no HTML5 semantic elements)

  • App shell: ldh-header (wordmark, address bar showing the document URI, Material-glyph icon buttons, initials avatar), ldh-sidebar with sb-* tree/classes/other sections; flyout mechanics unchanged
  • Action bar: zoned ldh-actionbarldh-btn Create with ldh-add-menu (design .it constructor entries), ghost Add, pills breadcrumb (bc-pill chips + chevron separators), schedule-glyph timestamp, single design Actions menu (Edit / Access / Save as · export links · danger Delete), labeled ldh-mode switcher with modes-pop
  • Blocks: ldh-block cards with ldh-block-head (badge/title/toolbar .tb actions incl. the drawer trigger); backlinks/related/parallax live in a per-block ldh-drawer; XHTML prose renders quiet (is-quiet); exactly one card layer (nested cards flatten)
  • Forms/modals: two-column property grid on the control-group contract, field skins with focus rings, modal card above the sticky chrome with a dim backdrop
  • Typography/tokens: colors_and_type.css, app.css, core.css, m3 skin; Geist / Geist Mono / Instrument Serif / Material Symbols vendored as local woff2 — no external requests

What's gone

  • Bootstrap 2 framework CSS deleted from the repo; bootstrap.js + jQuery unlinked (LDH glue rewritten vanilla in functions.js, IXSL contract intact); WYMEditor bundle removed
  • The ldh:logo sprite system: all 73 icon rules and the icons/ directory — Material Symbols everywhere
  • Bootstrap vocabulary purged by census: cosmetic tokens dead (well, span*, input-*, pull-* where inert, table-striped, …), anchored tokens renamed on both sides (span12row-main, row-fluidblock-row, form-horizontalldh-prop-form); LDH's own css/bootstrap.css renamed to ldh.css and pruned

Deliberately kept

  • CSR anchor tokens (block, control-group, dropdown-toggle/open, progress/bar, pager/previous/next, …) — styled by ldh-bridge.css, the documented transitional shim between CSR state classes and the design system
  • bs2: mode names and the xsl/bootstrap/2.3.2/ path — mechanical rename deferred
  • Web-Client's stylesheet link (its markup, its repo)

Known gaps (follow-ups)

  • Facets work in the block drawer, not yet the design's ldh-view-toolbar FacetDropdowns; container sort/view controls not yet view-mode-seg
  • Modal content (constructor forms) still legacy inside the design shell; search/access modals bridged, not mhead/mlist markup
  • Admin & auth screens out of scope this round
  • Upstream design-system landmine documented: the m3 skin's .ldh-header * color blast needs the inheritance fix + self-colored panels in the design project

🤖 Generated with Claude Code

namedgraph and others added 30 commits August 26, 2026 18:17
Blocks now carry the classes ui_kits/app/app.css targets, replacing the
Bootstrap grid ones:

  row-fluid block  ->  block ldh-block
  main span7       ->  main ldh-block-body
  well             ->  ldh-block-head

'block' and 'main' stay alongside the new names: 62 and 11 CSR handler
patterns anchor on those tokens, so dropping them would unhook drag-drop,
inline editing and the block toolbars.

The header gains the two flex children app.css expects - a titles group
(type list as badge, h2.ttl, description as span.sub instead of a <p>)
and an .actions group holding the timestamp and actions.

bs2:Left and bs2:Right emitted reserved grid columns for backlinks,
related resources and parallax navigation. The design system has no
columns; that content belongs to a block-scoped drawer, so they now
render inside div.ldh-drawer. They stay unconditional because CSR fills
them later.

Two CSR call sites had to follow the markup:

- view.xsl took the nav placeholders with a child step, which the drawer
  wrapper broke - faceted search would have lost its facets and parallax
  its controls, silently, since an empty for-each is not an error.
- chart.xsl and query.xsl lift the header out before replacing block
  content via ./div/div[@Class = 'well'], declared as="element()", so the
  rename would have been a runtime type error rather than a silent miss.

Still to port: bs2:Left/bs2:Right internals to the drawer's own
structure, the drawer's open/close behaviour, bs2:Actions buttons to .tb,
the YouTube header's inner markup, and dropping app.css itself in -
without which this renders unstyled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
colors_and_type.css (tokens), app.css (components), retro.css (m3 skin)
copied from the design system export; fonts.css @font-faces the vendored
woff2s - Geist, Geist Mono, Instrument Serif (latin subsets) and the
Material Symbols Rounded variable icon font - so no external font
requests. The root element takes data-retro="m3" data-theme="light",
which the skin keys on.

Linked after the Bootstrap stylesheets so the design system wins where
both target the same elements; the Bootstrap links come out at the end
of the port.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bs2:NavBar becomes the ldh-header grid (wordmark | address | actions),
replacing the Bootstrap fixed-top collapse structure; NavBarLeft/Main/
Right and their span columns go. The brand takes the wordmark shape,
the address bar the ldh-address skin (same form, 'navbar-form' token and
input#uri kept for the CSR submit handler), and the right side becomes
ldh-icon-btn buttons with Material Symbols glyphs - apps grid, settings
gear, notification bell (admin) - plus an initials ldh-avatar for the
agent. btn-group/dropdown-toggle tokens stay: the CSR handler toggles
.open on them.

ldh:LeftSidebar takes the ldh-sidebar skin: sb-search field, sb-section/
sb-heading groups, sb-tree rows with Material carets. The flyout
mechanics are untouched - hidden by default, edge-hover shows, mouseout
hides - and document-tree/class-list/btn-expand-tree/btn-geo/btn-latest
anchors stay for the CSR handlers.

ldh-bridge.css is the deliberate shim between CSR state tokens and the
design system: dropdown .open menus, ul.nav strips, the tab bar, tree
rows and the expand/collapse carets, styled with the system's tokens.
Rules leave this file as components finish porting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The generic bs2:Row got the drawer in the shell port, but two more block
templates - the ldh:XHTML content block and the edit-form row - still
applied bs2:Left/bs2:Right in-flow, so their backlinks headings rendered
inside the block body. The drawer markup is extracted into the
ldh:BlockLinksDrawer named template (dh title + close, db holding
bs2:Left/bs2:Right) and all three block shapes call it; bs2:Left/Right
defaults become drawer groups (left-nav/right-nav dgroup) and the
backlinks well becomes a dgroup with its nav-header kept for the CSR
load-on-click handler.

The header actions gain the tb-links trigger for @Rdf:about resources;
CSR handlers in block.xsl open the drawer (display flex) and close it
from its dh button. bs2:Actions loses the Bootstrap button faces:
btn-copy-uri and btn-edit become .tb toolbar icons (content_copy, edit)
with their handler tokens kept.

Verified in the browser: 14 drawers on the dev root document, all
hidden; clicking tb-links opens the block's drawer with hydrated
facet/backlinks content, its close button hides it again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bs2:ActionBar becomes the zoned ldh-actionbar grid (add | breadcrumb+
timestamp | actions), dropping its container/row/span wrappers. The
Create/Add cluster sits in ldh-add-wrap, breadcrumbs take the classic
ldh-bc skin on the same CSR-built ul, and doc-controls becomes the
quiet mono ldh-ab-ts stamp (History link unchanged).

The right-side document actions lose their sprite-logo faces: delete,
save-as, access and edit are .tb icon buttons with Material glyphs and
their identity tokens (btn-delete, btn-save-as, btn-acl btn-access-form,
btn-edit) plus disabled state written explicitly. Export and the layout
mode switcher stay dropdowns but with .tb trigger faces; mode glyphs
come from the new $ldh:mode-icons map shared by the switcher button and
the bs2:ModeListItem entries.

bs2:TypeList becomes the badge strip (ldh-typelist), facet headers move
from nav-header btn to the drawer's dh2 look, and the bridge styles the
read-mode dl/dt/dd as design-system property rows and ul.pager - whose
exact-class markup the CSR paging handlers match on - as prev/next
buttons.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Modals: the CSR appends div.modal.fade.in and removes it on close, so
the bridge styles those tokens as the ldh-modal card - centered fixed
panel, radius/shadow, mhead-style header with close, scrolling body,
footer bar shared with .form-actions. No modal.xsl template changes.

Forms: control-group/control-label/controls stay (they are CSR anchors
for constraint violations and constructor rows) and become a two-column
property grid with explicit cell placement - RDF/POST hidden inputs and
<hr> separators are also direct children of .control-group and were
silently taking the first grid cell, pushing every label off its row.
Inputs, textareas and selects get the field skin with the focus ring;
type hints (.help-inline) read quiet and turn danger only inside .error
groups; submit buttons take the accent skin.

Verified in the browser: the document edit modal renders the two-column
statement layout with working focus states and footer actions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
static/css/bootstrap.css and bootstrap-responsive.css are no longer
linked. ldh-bridge.css replaces the primitives LDH still emits - .btn
faces, carets, badges, alerts, progress bars, tables, typeahead menus -
at :where() zero specificity so design-system component classes always
win. The two remaining bootstrap.css links are app-specific rule files
(Web-Client's and LDH's own) that keep the historical name only.

Bootstrap-only vocabulary dies per the census of what the CSR actually
anchors on:

- purged outright (no anchors): span2-span10, offset*, well/well-small,
  sidebar-nav, nav-list, input-*, form-search, input-append, btn-small,
  btn-large, inline, table-striped/bordered, progress-striped,
  pull-* trimmed where flex replaced floats
- renamed on both sides, emission and contains-token anchors together:
  span12 -> row-main, row-fluid -> block-row,
  form-horizontal -> ldh-prop-form, alert alert-error -> ldhc-alert
  va-danger, container-fluid folded into content-body
- kept, still meaningful as CSR anchors: block, main, control-group/
  control-label/controls, progress/bar/active, dropdown/btn-group/open,
  modal family, navbar/navbar-form, breadcrumb, pager/previous/next,
  tab-pane/tab-content, nav-header, search-form/search-query
- btn-danger remove buttons become ldhc-btn in-negative, keeping their
  remove-action/link-remove handler tokens

The footer takes the design system's cols/ftitle shape, and the content
body gets its stacking rhythm and max-width from the bridge now that
Bootstrap's row margins are gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A dt followed by several dds dropped the extra dds into the term column
under grid auto-placement; dt/dd now take explicit columns. .btn-primary
picks up a link-colored text rule somewhere upstream, which vanishes on
the accent fill - one real-specificity color rule pins it to
--fg-on-accent. Content column takes a 1280px measure and the block
cards get their stacking margins back (Bootstrap's row margins are
gone).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, WYMEditor

static/css/bootstrap.css and bootstrap.min.css (the 2.3.2 framework
files) are deleted - nothing links them since the framework drop. The
script chain loses static/js/bootstrap.js and jquery.min.js: dropdowns,
modals and tabs have been IXSL-driven for years, and the only jQuery
left in LDH code was the RDF/POST subject-type/value glue.

js/jquery.js becomes js/functions.js with the IXSL contract intact -
fetchDispatchXML, onSubjectTypeChange, onSubjectValueChange and
ixslTemplateListener keep their names and behaviour (the two subject
handlers rewritten from jQuery to plain DOM). The Bootstrap-layout
pieces of its ready block die with the markup they served (btn-navbar
collapse, $().dropdown fallback); outside-click dropdown close and the
typeahead autocomplete-off live on as vanilla listeners.

The WYMEditor bundle (2.2MB, jQuery-based) goes too - replaced by the
RDFa editor in 5.7.0, nothing references it.

Verified in the browser: no jQuery-undefined errors, Export dropdown
opens via IXSL and closes on outside click, the edit modal renders. The
one remaining console 404 is the dev dataset's own broken test object
(?uri=https://example.org/aaa), not a regression.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
com/atomgraph/linkeddatahub/css/bootstrap.css never was the framework -
it holds LDH's own application rules that accumulated under the
historical name. It becomes ldh.css, minus the ten rules whose selector
tokens no longer occur in any LDH or Web-Client markup (old breadcrumb
logos, action-bar span layout, btn-ban/btn-context, dead sidebar list
skins).

Web-Client's bootstrap.css stays linked: it styles Web-Client-emitted
markup and lives in that repo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two things made the page read as a wall of anonymous white cards rather
than the design:

- The SaxonJS bs2:Row wrapper carried the ldh-block card skin AND passed
  it to its next-match inner block, so every CSR-wrapped block rendered
  card-in-card - visible as sliver borders around alerts and empty-
  looking bars. The wrapper keeps only the 'block' anchor token now; the
  inner block is the card.
- ldh:XHTML content blocks are prose, and the design renders prose as
  quiet blocks (no surface, part of the page flow). Their class default
  becomes 'block ldh-block is-quiet', which app.css already styles.

The [Language: en] chip in the reported screenshot does not reproduce in
a fresh Chromium or Firefox - it is the stale-SEF browser cache
(/static/ is served immutable); a hard reload clears it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The legacy .action-bar rule pins it at --action-bar-top with a 51px
fallback - the old Bootstrap navbar height - while the new ldh-header is
56px tall and was not sticky at all, so on scroll the header left the
viewport and content showed through a 51px gap above the stuck action
bar.

The header is now sticky at top 0 and --action-bar-top is defined as the
header height, so the two form one pinned chrome stack; #tab-bar's
legacy 51px offset follows the same variable. The action bar trades the
legacy gray fill for the surface color with a bottom hairline.

Verified scrolled 1200px down: header spans 0-56, action bar starts at
exactly 56, content scrolls beneath both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The breadcrumb items were still li > sprite-classed anchor + divider.
bs2:BreadCrumbListItem now emits the design's crumb anatomy - a.crumb
(is-current on the leaf) holding a Material glyph chosen by document
type (folder for Root/Container, description for Item, link otherwise)
plus the label, with span.sep between crumbs - inside the same ul both
render paths share; the CSR-created ul takes the ldh-breadcrumb classes.
The crumb builds its own <a> with the imports/default.xsl href recipe
rather than overriding the generic xhtml:Anchor mode, which would have
re-defaulted every anchor's href away from ldh:href().

The action-bar port had also silently reversed the document buttons:
the old markup floated each one right, so DOM order delete->edit read
on screen as edit->delete, and the flex container preserved DOM order
instead. The DOM now matches the screen order the floats produced:
edit, access, save-as, delete, then export and the mode switcher.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…alert blocks

The container block's double bottom border had three contributors, found
by DOM forensics rather than guesswork:

- CSR hydration re-renders a row as a full ldh-block card inside the SSR
  wrapper card, stacking two borders 13px apart. One card layer only
  now: a nested .ldh-block flattens (no border/shadow/radius/surface).
- The token rename swept the XSLT but not ldh.css, orphaning its
  row-fluid/span12/form-horizontal/input-append selectors - drag-handle
  hiding, block clipping, drag-over and diff borders, the progress-bar
  sizing that kept the loading skeleton invisible after errors, and the
  search-field flex. All follow the rename (block/block-row/row-main/
  ldh-prop-form); the dead action-bar span rule is deleted.
- A block whose content collapsed to a single error alert kept its card
  chrome, framing the alert with white lips. Such blocks go quiet via
  :has() - the alert brings its own surface - and alerts lose their
  stray margins inside blocks. The m3 skin's resting M3-elevation
  shadow, which reads as a hard line under every card, rests flat per
  the design's card preview (border instead; hover unchanged), and the
  bridge table rule no longer draws a separator under the last row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bar's controls were still Bootstrap skeletons (btn-group +
ul.dropdown-menu) under bridge CSS. They now emit the design system's
own structures, as divs per house style:

- Create: ldh-add-wrap > ldh-btn (add glyph + label + caret) +
  ldh-add-menu with .it entries; bs2:ConstructorListItem renders the
  design item shape (ico/body/lbl, folder for Container, description
  for Item) while keeping the add-constructor/data-for-class/
  data-create-graph contract. Both end-user and admin variants.
- Add: ldh-of-wrap > ghost ldh-btn + ldh-of-menu, generate-containers /
  import-ontology tokens kept.
- Document actions collapse into the design's single Actions menu
  (bolt + label + caret): Edit / Access / Save as, a divider, the three
  RDF export links (target=_blank keeps them outside CSR interception),
  a divider, and a danger-styled Delete - handler tokens and disabled
  states preserved. bs2:MediaTypeList dies with its dead export-rdf
  interception exemption; its links live in the menu now.
- Mode switcher: ldh-mode > label-row trigger (glyph + active-mode label
  + caret) + modes-pop with .mi entries as anchors, mode-class and
  active tokens kept alongside is-active.
- Breadcrumbs switch to the pills variant: bc-pill chips with chevron
  separators in a div container on both render paths.
- The timestamp gains the schedule glyph; the address bar shows the
  document URI on SSR and always mirrors it on navigation (previously
  blanked for local documents - the design's Header shows the URL).

All design menus open through the existing CSR btn-group 'open' toggle;
the bridge maps that state onto ldh-add-menu/ldh-of-menu/modes-pop
visibility and exempts the address field from the form-field skin.

Verified in the browser before the stack was stopped externally: Create
pill, Actions menu with dividers and danger Delete, pill breadcrumb,
labeled Content switcher all render and open; the address-bar change
compiles but its browser check is pending the stack's return.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The XHTML/Object create-block buttons were the last old-school controls:
Bootstrap primary pills with sprite faces. They are ghost ldh-btns now,
with Material glyphs (notes / data_object) and their create-action /
add-constructor / data-for-class contract intact.

The sprite system itself goes: all 73 background-image rules pointing at
the icons/ directory are deleted from ldh.css and the directory itself
is removed - every ported control renders Material Symbols instead. The
ldh:logo templates stay purely as class-token carriers for the CSR
handlers that anchor on them; with the sprite CSS gone they paint
nothing. The one non-CSS consumer, the signup-complete <img>, becomes a
check_circle glyph.

Verified in the browser: the create-block buttons render as quiet design
pills, and the address bar now shows the document URI (the CSR change
from the previous commit, unverifiable then while the stack was down).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bridge's Bootstrap-replacement .caret was a 0x0 border triangle,
which collided with the ported markup's <span class="msi caret">
expand_more</span>: the triangle painted inside the button while the
glyph text overflowed the zero-size box as a stray chevron below it.

.caret is now a Material glyph in both generations - ported spans render
their own expand_more, and legacy empty <span class="caret"/> spans
(login dropdown, sort toggle, the drawer's backlinks heading) get the
same glyph via ::before, with caret-reversed flipping to expand_less.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…les only

Of the empty <span class="caret"/> leftovers, five were static dropdown
carets - they now ship the expand_more glyph directly, like the rest of
the ported markup. The two caret-reversed collapse indicators stay empty
by design: the CSR handlers flip the caret-reversed class on them, and
only ::before content can follow a class change on a ligature-font
glyph. The bridge comment records that distinction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sticky action bar (z-index 999 from the legacy rules) painted over
modal dialogs, hiding their header - the bridge had given modals z-index
110. Modals sit at 1050 now, above the whole chrome stack, with a
non-interactive dim behind them via body:has(); typeahead suggestion
menus move above the modal plane so in-form typeaheads keep working.

Design menus (Create/Actions/modes) also stayed open after picking an
item. The outside-click closer now also closes on menu-item picks,
deferred a tick so it settles after the IXSL btn-group toggle that
handles the same click - otherwise the two could race and re-open the
menu.

Verified in the browser: Create > Container opens the modal above the
chrome with the page dimmed behind it, and the Create menu is closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The m3 skin paints every .ldh-header descendant with the inverse near-
white text color for the dark bar - correct for the bar itself, but the
light dropdown panels inside it (Applications, Settings, agent) rendered
white-on-white and read as empty. Panel content takes the regular ink
back at higher specificity, section headers keep the hint color, and the
avatar's initials stay dark in its hover/open state where the fill goes
light.

Settings had no pull-right token, so its menu left-aligned and clipped
off the viewport; header menus now hug the right edge regardless.

Menu picks also close these header menus via the same deferred closer as
the action-bar menus.

Verified in the browser: all three menus open readable and right-
aligned - System apps list, Application/Administration/Namespace
ontology, and the agent entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Census: zero CSR anchors on pull-*, and floats are inert inside flex
containers and on absolutely positioned elements - so every pull token
whose parent already became flex chrome (header action strips, block
head actions, alerts, dropdown toggles) or that rode an absolute menu
was doing nothing. Those 14 are gone; the Actions wrapper layout keys on
its position in .actions instead of the token.

What stays, deliberately: pull-right on ul.dropdown-menu as the
alignment signal for the not-yet-ported menus, and the still-active
floats in interior form markup (btn-remove-property/btn-add rows,
typeahead metadata, the hover-overlay edit buttons) - those go when
their components get ported, and the bridge documents exactly that.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The legacy modal markup puts button.close FIRST and titles the dialog
with a <legend>; the bridge's flex header laid children out in DOM order
- close at the left, the title squeezed into a wrapping column, the
description eating the rest. The header is a two-column grid now: the
title column (legend/h2/h3 styled as the design's mhead title, any <p>
as a muted subtitle under it) and the close button pinned top-right,
regardless of source order.

Verified on the Request access modal: title left, description under it,
close at the right - matching the design's mhead anatomy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both bs2:Chart forms (the RDF and SPARQL-results variants, which the CSR
re-render also compiles in) swap their fieldset block-row for the design
system's chart-controls three-column grid with .field cells - mono
uppercase overline labels, full-width selects, the series multi-select
as a listbox. Select classes/ids (chart-type / chart-category /
chart-series, name=ou|ol) are untouched, so the onchange handlers and
the RDF/POST encoding are unaffected.

Fieldsets and legends lose their Bootstrap-era box look globally:
borderless sections with overline legends, which also fixes the
old-school group boxes in the access modal. Multi-selects inside the
controls grid keep their natural height.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full-DOM audit (Playwright crawl of every menu, modal, form and mode
against the design-system contracts) surfaced the components that lost
their faces when the sprite CSS died, plus one real regression risk:

- The constructor modal had an empty <legend> - it is titled with the
  class label now (ac:object-label in xsl:try; the bare call throws and
  killed the whole modal render, which the first fix attempt proved the
  hard way. The catch falls back to the URI local name.)
- Remove-statement and remove-resource buttons were empty invisible
  boxes in every editing form: .tb buttons with the close glyph now, in
  all three emitters (constructor.xsl - with the xsl:attribute reordered
  before the glyph span, imports/default.xsl, resource.xsl), handler
  tokens kept
- The search modal's submit button was invisible: search glyph
- Add-statement button swaps the HTML entity cross for the add glyph
- Graph HUD buttons gain fit_screen / fullscreen glyphs per OtherModes
- Dead tokens purged: wymupdate, dl-horizontal, the sprite-era class
  injection on block-title anchors

Verified in the browser: Create > Container opens titled with glyphed
remove buttons (4/4); the per-block edit button opens the inline editing
form with glyphed controls and footer actions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A faceless-button DOM sweep (every button with no text, glyph or image,
across edit mode, the create modal and the inline block edit form)
caught the emitters whose only face was a sprite class injected via the
ldh:logo mode: the subject-edit toggle in form legends (edit glyph), the
add-value button on property controls (add glyph) and the rdf:type
control group's remove button in imports/rdf.xsl, which now matches the
canonical tb btn-remove-property + close-glyph shape. Dead sprite
background-position rules and the border-triangle caret-reversed rule
(the double-arrow bug's last holdout) leave ldh.css. Re-swept after
deploying: no faceless buttons remain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…glish go with it

The facets answer to 'where did the filters go': out of the links
drawer, into a ldh-view-toolbar at the top of every view block, per the
ViewBlocks contract. Left zone: filter_alt lead + one dropdown pill per
facetable property - the pill is the old nav-header (button-shaped, same
hidden subject/predicate/object inputs), so the lazy value-load onclick
and the checkbox filter onchange run unchanged; the hydrated ul.nav now
doubles as the facet-pop popover card. Right zone: the inline-create
button, Total results, the order-by select + direction button, and the
mode dropdown (with a visible active-mode glyph from ldh:mode-icons).
The drawer group the facets vacated hides when empty; parallax stays in
the drawer. Verified in the browser: pill opens, values load with
counts, checking a value filters the results.

The last pull-left/pull-right tokens are purged along the way - header
menus right-align via a header-scoped rule, the form legend is
restructured (label first, subject controls in a right-hugging .actions
cluster, remove button last), remove/add groups trail their form rows
via flex order, the language badge follows its value, and the typeahead
metadata span is .meta. The bridge float shims are deleted.

Branch-introduced English strings move to translations.rdf (add-stmt,
remove-resource, remove-link, get-started, learn-more, footer titles
and links, export format names); the facet lead reuses filter-title.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The three serialization links (RDF/XML, Turtle, JSON-LD) move out of the
Actions menu into a new bs2:ExportList mode that bs2:ActionBarRight
renders after the mode switcher: an icon-only ghost button (download
glyph, titled from the existing export translation entry) opening the
same target=_blank links. Same signed-in guard and HTTP-error
suppression as before, same ldh-of-wrap anatomy, so the open-toggle and
close-on-pick wiring covers it unchanged. The Actions menu keeps
edit / access / save-as / delete, minus the now-stray second divider.

Verified in the browser: the button renders right of Content, its menu
lists the three formats with the correct accept hrefs, and the Actions
menu no longer carries them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The edge-hover flyout stopped appearing: the port stamped the sidebar
with the design's ldh-sidebar class, whose resting state is
translateX(-100%) waiting for an is-open toggle - but the CSR handlers
carry show/hide as inline display (mouse at x=0 sets block, mouseout
sets none). The handler fired, display flipped, and the panel stayed
288px off-screen. The bridge now neutralizes the slide transform so
display alone controls visibility, and starts the panel below the
sticky header instead of the design's full-height top:0.

Verified in the browser: mouse at the left edge slides out the panel
(search, document tree, class pills, other views) at x:0 under the
header; mousing out hides it again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ith it

Every form footer (modal and inline) now follows the design's footer
anatomy: secondaries (Cancel/Reset/Close) are ghost ldh-btns, the
primary (Save/Compare/Allow/Annotate) is a filled ldh-btn with a glyph,
and it sits last so the ghost buttons lead into it. Handler tokens
(btn-save, btn-cancel, btn-reset, btn-close, btn-save-chart, spo-action,
cancel-action) ride along unchanged, as do the two $button-class param
defaults that feed the ldh:logo class injection.

The Bootstrap-era form-actions class itself is gone: the design system
has no such band (its ldh-form-actions-* names mean the per-form actions
dropdown), so the footers take the design's ldh-block-foot token - all
emitters, the one CSR matcher that anchored on it, and the bridge rules
renamed together. modal-footer stays as the sticky-scroll token, its
hardcoded white swapped for the surface token. Also fixed en route: a
stray quote in ldh:Form's save button class, and the access form's
hardcoded Allow label now comes from a new translations entry.

Verified in the browser: the create modal footer renders ghost Reset +
filled glyphed Save on the ldh-block-foot band.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…column

The rdf:RDF form shell gains a show-close-button param (default false, so
the SSR EditMode page keeps its plain footer) emitting a ghost Close
button ahead of Reset; the generic modal.xsl close handler already
matches any button.btn-close inside a modal, so no new wiring. All six
modal flows through the shell opt in: document create, document edit,
add-instance, app settings, and both constraint-violation re-render
paths. The three modal footers that already had a Close (import
ontology, generate containers, request access) join the new convention:
ghost Close and Reset leading into the ldh-btn primary. Also swept up:
form-actions-class param values that still said 'form-actions' (the
class-literal rename missed them), and modal.xsl's button-class defaults
move off btn btn-primary.

Modals now span the content column - width matches .content-body's inner
width (max 1280px minus side padding) instead of the 880px cap.
Verified in the browser: create modal footer reads Close / Reset / Save,
inline form reads Cancel / Reset / Save, and the modal box is
pixel-identical in x and width to the content blocks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
namedgraph and others added 29 commits August 27, 2026 01:50
…rimaries

Content mode's XHTML/Object buttons and the row modes' Create picker
used to sit at the end of the document flow - on long documents the
main authoring affordance was a scroll away, and the content-mode pair
was ghost-styled while every other creation button was filled. Both
rows now render into a fixed ldh-create-dock at the bottom right, all
buttons unified as filled ldh-btn primaries with the add glyph, matching
the action bar's Create anatomy.

The dock's right edge is anchored to the drawer-width token (360px +
gutter), so it can never collide with the right-edge drawer slide-in -
and that boundary is where the action bar's timestamp control ends, so
they read as aligned (within 9px at 1440; exact element-tracking is not
expressible from position:fixed). The class-picker menu opens upward
from the dock, the dock hides while a modal is open (the dim overlay is
pointer-inert), and elevation comes from the shadow token. Legacy
create-action height caps and the create-resource margin rule leave
ldh.css.

Verified in the browser: content mode shows + XHTML / + Object floating
bottom-right; switching to Read mode re-renders the dock with the
+ Create picker in the same position.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The form-row remove/add buttons (tb btn-remove-property, tb btn-add,
tb btn-remove-resource) rendered as browser-default bordered boxes:
app.css styles .tb only inside .ldh-toolbar, .ldh-of-wrap and
.ldh-block-head .actions, and the form rows sit outside all three
scopes. The bridge now carries a zero-specificity :where(button.tb)
base - 28px quiet icon button, neutral hover, danger hover on the
remove tokens - so every scoped design recipe still wins unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… viewport

The dock switches from fixed to sticky: it still rides the viewport
bottom while content scrolls, but it lives in flow at the end of the
content, so reaching the end parks it above the footer instead of
floating over (or below) it. The right-edge clearance of the drawer
strip carries over as an auto/max margin pair, since sticky offsets
resolve against the content column rather than the viewport.

The page column is a flex stack now (#visible-body min-height 100vh,
#tab-body absorbs the slack), which pins the footer to the viewport
bottom on short pages. The legacy sizing that made empty pages look
broken is gone from ldh.css: the forced body height that painted the
page gradient below the footer, the stale fixed-navbar padding pair,
and content-body's min-height heuristic that the flex chain replaces.

Verified in the browser: on a long page the dock floats bottom-right
and parks 64px above the footer at full scroll; on a short page the
footer ends the document exactly (no painted void) with the dock in
flow above it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four-angle review (reuse, simplification, efficiency, altitude) of the
design-system port, applied:

The big catch is a real repair: bs2:ModeListItem emits bare a.mi items,
but every view-level handler still matched ul.view-mode-list/li[.active]
- dead XPaths, so view mode switching and the reads feeding sort, facet
filtering and parallax were silently broken. The view mode menu now uses
the document-level anatomy (ldh-mode wrap, modes-pop), all matchers and
active-mode reads key on a.mi.is-active (mode class extracted via
map:keys($class-modes)), activation toggles follow, and the redundant
legacy 'active' token is gone. Verified in the browser: mode switch
re-renders, facet checkbox filters.

Dedup and dead code: the unreachable bs2:Constructor template (~80
lines) and its orphaned with-label param are deleted; the Root/Container
/Item glyph choice lives once in an ldh:class-icons map; the Link-header
target parse is one ldh:link-targets function instead of six inline
copies; the RDF export menu is one for-each over accept/label pairs
under the pre-existing bs2:MediaTypeList mode name instead of a parallel
bs2:ExportList; the admin AddData override passes its menu item through
xsl:next-match instead of re-emitting the whole dropdown; the avatar
initials item is a shared template; ldh:BlockLinksDrawer is a match
template per the project idiom; double icon plumbing (sprite-era
ldh:logo class injection under msi glyphs) is gone from the footer
buttons; the dead va-danger token becomes alert-error; login buttons
drop the stacked legacy btn vocabulary.

State classes: the dropdown toggle stamps open + is-open together, so
app.css's native open-state rules apply and the outside-click closer
clears both (with an early exit when nothing is open).

CSS: the legacy Bootstrap tab skin that out-specified the bridge's
design-token tabs is gone from ldh.css along with dead rules
(collapsing-top-navbar, sprite btn-logo, doc-controls btn-edit) and the
51px-era fallbacks; the bridge gains shared tokens (--ldh-content-w,
--ldh-drawer-w in one :root), uses --r-pill / --shadow-lg / --shadow-xl
/ --border-default instead of nonexistent-token fallbacks and literal
shadows, deduplicates .btn-group positioning, and the action bar's left
zone gets its own ab-left class instead of overloading ldh-add-wrap.

Efficiency: diff-key serialization short-circuits on non-diff renders
(ldh:diff-class / ldh:value-diff-class guard on empty key sets); the
duplicate triples-map builds in the diff view are hoisted.

Deferred with notes: icon-font subsetting (5.4MB woff2), :has()-based
quiet-block detection vs stamping is-quiet at the failure emitters,
shared modal header/footer shell, map-based diff-key membership,
constructor-sync request grouping, package catalog caching, typelist as
ldhc-tag, caret convention unification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.content-body centers with auto side margins, which opt a flex item out
of cross-axis stretch - once .document-body became a flex column for
create-bar parking, the column shrink-wrapped to its widest child and
every block in Properties mode narrowed with it. An explicit width puts
the auto margins back to centering the max-width cap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KwvP2BwVb5rmXc7hWxqF4a
The bar's right padding was the floating cluster's drawer-strip
clearance, pushing the Create button 360px in from the edge. A
full-width row needs no clearance - the drawer overlays it when open -
so the padding is now gutter + column inset, putting the button flush
with the blocks' right edge. That orphaned the --ldh-drawer-w token,
whose only consumer this padding was.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KwvP2BwVb5rmXc7hWxqF4a
…imitives LDH still emits

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AnsTqafyvZJUN7CmDse2mn
…the footer

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AnsTqafyvZJUN7CmDse2mn
…thout editable content

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AnsTqafyvZJUN7CmDse2mn
…-aligns with the content column

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…g a second box inside the pill

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…tle with description, date, type badge

A document paired with its foaf:primaryTopic renders as one row carrying the topic's label, description and type.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
… Table chart is skinned in CSS instead

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…line row forms' transplant and constructor chain

The modal body hosts a div.block.ldh-block that ldh:replace-block-element (the
DOM-update tail shared with ldh:render-row-form) transplants the rendered form
onto, so the generic handlers - add-value property picker, constructor sync,
type typeahead - anchor on 'block' instead of 'block-row' and work in both
placements. The edit flow fetches constructed-doc/constructors/shapes in
parallel and folds shapes + constructed-doc via ldh:build-merged-constructor,
so SHACL-defined properties resolve the same as SPIN-defined ones; the
violation re-render rides the same load-pairs. The fieldset legend doubles as
the modal form's heading.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…er; the 3D graph sizes to its canvas height

Graphs nested inside view blocks keep their fixed height - the canvas is a
direct .content-body child only at document level.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…tops drifting

The bar heights size the bars rather than mirror their content, letting
stacked sticky offsets (.editor-bar) compose them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…pills and headings with the rows below

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…e the design's property groups

Offset box-shadow copies of the dt/dd cells fill the column gap and the card
padding; a hovered dd lights its (deduped) dt via :has(), up to four values
per property.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…ce the flattened modes-pop markup no longer carries it on the parent

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…results region instead of the block wrappers, and a card with an open menu wins the paint order

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…escription in an auto-fill grid aligned to the toolbar

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…ute inset is reserved for the fullbleed wrapper

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…cepting default-mode dispatches and view Properties mode renders again

Same class of bug as be7c4eb (normalize-rdfxml): an unnamed-mode
match="/" entry point in a late import outranks every root/document
template below it, so the otherwise branch of ldh:ViewModeChoice fed its
results document to the RDFa extractor, which found no RDFa in RDF/XML
and emitted an empty rdf:RDF element. Both call sites pass $doc
explicitly, and the RDFa-Editor's own CLAUDE.md already documents the
named-entry-only contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
… the extractor entry becomes rdfax:extract-rdfa, the canonicalization passes cm:canonical, cm:normalize and cm:demote, and the standalone entry rdfae:main

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…ar while an XHTML block is being edited

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
…, joining graph mode in the fullbleed treatment

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
@namedgraph namedgraph mentioned this pull request Aug 27, 2026
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