Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

531 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Glossarist Concept Browser

A statically deployable terminology browser. Install as an npm package, configure via YAML, build, deploy.

Quick Start

# 1. Create your project
mkdir my-dictionary && cd my-dictionary
npm init -y

# 2. Install
npm install @glossarist/concept-browser

# 3. Create site-config.yml
cat > site-config.yml << 'EOF'
id: my-dictionary
title: My Dictionary
uri_base: https://example.com
datasets:
  - id: my-vocab
    title: My Vocabulary
    local_path: datasets/my-vocab    # or gcr_package: https://...
EOF

# 4. Build
npx concept-browser build

# 5. Deploy
# Upload dist/ to any static host (GitHub Pages, Netlify, S3, etc.)

How It Works

Your project (CWD)                    Package (read-only)
├── site-config.yml          ──┐
├── datasets/                 ─┤
│   └── my-vocab/             ─┤     ┌──────────────────────────┐
│       └── concepts/*.yaml   ─┤     │ @glossarist/concept-browser │
├── public/                   ─┤ ──> │                          │
│   └── (logos, favicons)     ─┤     │ generate-data.ts         │
├── .cb-content/   ← generated │     │ bridge-to-astro.ts       │
└── dist/          ← output    │     │ astro build              │
                             ──┘     └──────────────────────────┘

The package reads your config and data, generates .cb-content/ (content collections) and dist/ (static site) in your project. The package itself (node_modules/) is never modified.

Data / Deployment Boundary

There is a strict separation between dataset authors and deployers:

Role Owns Does NOT touch
Dataset author YAML concept files, bibliography, figures/tables site-config.yml, deployment URLs, other datasets
Deployer site-config.yml, URI patterns, branding, fonts dataset content, inline references, concept IDs
concept-browser Resolves every citation at runtime

The same dataset YAML works in any deployment. A citation like {{cite:iso704}} resolves differently depending on which datasets are co-deployed — the deployer's site-config.yml:uriPatterns decides.

Inline Content Syntax

All inline references in concept text use the unified {{kind:target}} notation:

Kind Syntax Example Description
cite {{cite:sourceId}} {{cite:iso7301}} Cite a ConceptSource from this concept's sources[]
cite+label {{cite:id, label}} {{cite:iso7301, rice}} Same, with explicit display label
urn {{urn:URN}} {{urn:iso:std:iso:704}} Reference via URN routing
fig {{fig:id}} {{fig:diagram_3}} Reference a figure entity
table {{table:id}} {{table:unit_list}} Reference a table entity
formula {{formula:id}} {{formula:ohm_law}} Reference a formula entity
bib {{bib:id}} {{bib:ref_1}} Bibliography entry (no underlying concept)
link {{link:URL}} {{link:https://example.com}} External URL
link+label {{link:URL, label}} {{link:https://example.com, click here}} External URL with label
image {{image:src}} {{image:diagram.png}} Inline image embed
image+alt {{image:src, alt}} {{image:diagram.png, The diagram}} Image with alt text
designation {{designation}} {{measurement unit}} Reference by designation text
numeric {{numeric_id}} {{112-01-10}} Reference by numeric ID

The deprecated <<ref,title>> AsciiDoc xref syntax still works but emits a console warning. Migrate to the unified syntax.

Citation Resolution Cascade

When concept-browser encounters {{cite:sourceId}}, it walks this cascade:

  1. uriPatterns — Is there a co-deployed dataset matching this URI? → internal link
  2. routing[] — Is there a routing entry in site-config.yml? → external link
  3. citation.link — Does the source have a canonical link? → flat bibliography record
  4. unresolved — No match → plain text

Configuration

Everything is in site-config.yml. See site-config.example.yml for all options.

Dataset location

Each dataset specifies where its source data lives. Three options:

datasets:
  # Option A: Local directory with concepts/*.yaml
  - id: my-vocab
    local_path: datasets/my-vocab        # relative to project root

  # Option B: GCR package (auto-downloaded at build time)
  - id: iev
    gcr_package: https://github.com/org/repo/releases/download/v1.0/my-vocab.gcr

  # Option C: Git repository (cloned at build time)
  - id: iso-terms
    source_repo: https://github.com/org/iso-terms

Colors

Each dataset can have a color — single hex or light/dark pair:

datasets:
  - id: my-vocab
    color: "#2563eb"                      # single color (both modes)

  - id: legacy-vocab
    color:
      light: "#004996"                    # light mode
      dark: "#3B82F6"                     # dark mode

Colors appear in the sidebar (color dot per dataset), sphere cards (top bar + tint), badges, and relationship edges. If omitted, a palette color is assigned automatically.

Group colors work the same way:

dataset_groups:
  - id: my-vocab-series
    color: "#d97706"

Logo

Place logo SVGs/PNGs in public/ and reference them in branding:

branding:
  logo:
    alt: My Dictionary
    light: /images/logo-light.svg         # shown in light mode
    dark: /images/logo-dark.svg           # shown in dark mode

If no logo is configured, the Glossarist logo is shown by default. The footer always shows the Glossarist logo ("Powered by Glossarist").

Fonts

Brand typography is fully slot-based and category-agnostic. There are four slotstitle, heading, body, mono — and each slot accepts any category (serif, sans-serif, monospace). Nothing dictates that "headings must be serif" or "body must be sans-serif". Pick whatever combination matches your brand.

Slots

Slot Applies to Default family Default category
title The single most-prominent text on each page (home hero, concept name on detail page, dataset/group title) DM Serif Display serif
heading h2–h6 section headings DM Serif Display serif
body Paragraph text, lists, table cells DM Sans sans-serif
mono Code blocks, inline code, kbd JetBrains Mono monospace

The defaults preserve the Glossarist visual identity — override any of them to match your brand.

Per-slot category override

Set category on any slot to control the fallback chain (the browser uses it when the primary family fails to load or is still loading):

branding:
  fonts:
    title:
      family: Inter
      source: google
      category: sans-serif           # sans-serif title (was serif by default)
      weights: [400, 600, 700]
    heading:
      family: Inter
      source: google
      category: sans-serif
      weights: [600, 700]
    body:
      family: Merriweather
      source: google
      category: serif                # serif body (was sans-serif by default)
      weights: [400, 700]
    mono:
      family: Fira Code
      source: google
      category: monospace
      weights: [400, 500]

This produces:

Slot CSS variable Stack
title --font-title 'Inter', system-ui, sans-serif
heading --font-heading (and --font-header for backward compat) 'Inter', system-ui, sans-serif
body --font-body 'Merriweather', Georgia, serif
mono --font-mono 'Fira Code', ui-monospace, "JetBrains Mono", Menlo, Monaco, monospace

Source options

source Behavior
google Build emits a Google Fonts CSS @import for the declared family + weights.
url Build emits a @font-face block loading from url.
local Consumer ships the font files in public/; no build-time fetch.

Backward compatibility

The legacy branding.fonts.header slot still works — it's a deprecated alias for branding.fonts.heading. Existing configs don't break. The CSS variable --font-header is still emitted (as an alias for --font-heading) so existing stylesheets that reference it continue to work.

Mixed-category example: all sans-serif

branding:
  fonts:
    title:   { family: Inter,        source: google, category: sans-serif }
    heading: { family: Inter,        source: google, category: sans-serif }
    body:    { family: Inter,        source: google, category: sans-serif }
    mono:    { family: JetBrains Mono, source: google, category: monospace }

Mixed-category example: traditional serif

branding:
  fonts:
    title:   { family: Playfair Display, source: google, category: serif }
    heading: { family: Lora,             source: google, category: serif }
    body:    { family: Source Sans Pro,  source: google, category: sans-serif }
    mono:    { family: Source Code Pro,  source: google, category: monospace }

Favicons

branding.favicon accepts two shapes — a legacy string form and an object form that lets consumers provide their own canonical favicon set without writing a post-build script.

String form (legacy, still supported)

Path to a single source SVG/PNG. The CLI generates the full variant set (favicon.ico, apple-touch-icon-*.png, etc.) from it using the favicons package:

branding:
  favicon: assets/my-brand.svg

Object form (recommended for branded deployments)

The object form lets you provide your own canonical favicon set (typically RealFaviconGenerator output) and have the CLI install it without a post-build script.

A canonical favicon set is multiple files — typically favicon.svg, favicon.ico, favicon-96x96.png, apple-touch-icon.png, web-app-manifest-192x192.png, web-app-manifest-512x512.png, and site.webmanifest. Put all of them in a directory (e.g. assets/favicons/) and reference it via source_dir:

my-deployment/
├── site-config.yml
└── assets/
    └── favicons/                  ← branding.favicon.source_dir points here
        ├── favicon.svg
        ├── favicon.ico
        ├── favicon-96x96.png
        ├── apple-touch-icon.png
        ├── web-app-manifest-192x192.png
        ├── web-app-manifest-512x512.png
        └── site.webmanifest       ← optional; CLI regenerates with BASE_PATH

Then in site-config.yml:

branding:
  favicon:
    base_path: /                              # URL prefix (BASE_PATH-aware; default '/')
    source_dir: assets/favicons               # canonical files (all of them, in one directory)
    icons:                                    # DATA — declare each icon, not HTML
      - rel: icon
        type: image/svg+xml
        href: favicon.svg
      - rel: icon
        type: image/png
        sizes: 96x96
        href: favicon-96x96.png
      - rel: shortcut icon
        href: favicon.ico
      - rel: apple-touch-icon
        sizes: 180x180
        href: apple-touch-icon.png
      - rel: manifest
        href: site.webmanifest

The CLI copies every file from source_dir/ into public/ (also removing the default cruft), then renders one <link> tag per icon entry. The href is a filename — the system applies the correct base_path prefix automatically. Absolute URLs (https://cdn.example.com/x.png) and root-relative paths (/x.png) are emitted unchanged.

Field Type Effect
source_dir string Path (relative to cwd) to a directory containing all canonical favicon files. The CLI copies every file in this directory into public/, overriding any defaults. Also removes the default-generated cruft (apple-touch-icon-57x57.png, favicon-16x16.png, etc.) so it doesn't linger.
icons FaviconIcon[] DATA — recommended. Array of { rel, href, type?, sizes? } entries. Each renders to one <link> tag with BASE_PATH-aware href. Replaces the default 16-link set.
skip_default_links boolean When true, the CLI does NOT call the favicons package and does NOT emit the default <link> tags. Pair with icons and source_dir for fully custom branding.
base_path string URL prefix prepended to every emitted link. Useful for BASE_PATH-scoped deployments (e.g. /vocab/).
links_html string @deprecated — use icons instead. Raw HTML emitted verbatim. Impossible to validate or safely BASE_PATH-rewrite; kept for backward compat with a console warning.

The object form exists so consumers with a RealFaviconGenerator favicon set (or any other canonical brand favicon bundle) can install it without a post-build script. Previously this required workarounds like glossarist/cie-eilv/scripts/install-favicons.mjs (149 lines) and glossarist/iala-vocab/scripts/install-favicons.mjs (175 lines) — both are now unnecessary.

If neither form is set, the Glossarist default favicon is used.

About pages

Place JSON files in public/pages/:

public/
└── pages/
    ├── about.json          → /about
    └── my-vocab-about.json → /my-vocab-about

Each file:

{
  "title": "About This Dictionary",
  "html": "<p>Content here. Supports full HTML.</p>"
}

The page appears in the sidebar navigation automatically.

Full example

See site-config.example.yml for all options including UI languages, fonts, features, and dataset groups.

Data/Deployment Boundary

Concept-browser enforces a strict separation between data (authored by dataset authors) and deployment (configured by deployers):

  • Dataset authors write concepts, sources, bibliography, and inline mentions. They don't know where their dataset will be deployed or what other datasets will be co-deployed.
  • Deployers write site-config.yml to register datasets, declare URI patterns, and optionally add routing entries for external datasets. They never edit dataset content.
  • Concept-browser resolves every cross-reference at runtime via a fixed cascade, making multiple datasets behave as one coherent whole.

The resolution cascade

Every {{cite:...}} and {{urn:...}} mention walks this cascade at render time:

  1. uriPatterns — is there a co-deployed dataset that matches? → internal link (case 1)
  2. routing[] — is there a routing entry for the URI? → external link (case 2)
  3. citation.link — does the source have a canonical link? → flat bib record (case 3)
  4. Unresolved — plain text

The same YAML renders differently in different deployments. The data never changes.

Inline Content Syntax

All inline references in concept text use the unified {{kind:target}} notation:

Kind Example What it does
cite {{cite:sourceId}} Cite a ConceptSource from this concept's sources[] — walks the full resolution cascade
urn {{urn:iso:std:iso:704}} Reference a concept via URN routing
fig {{fig:diagram_3}} Reference a figure entity in the same dataset
table {{table:units}} Reference a table entity
formula {{formula:ohm_law}} Reference a formula entity
bib {{bib:ref_1}} Reference a bibliography entry (case-3-only, no underlying concept)
link {{link:https://example.com}} External URL (canonical, deployment-independent)
image {{image:src, alt}} Inline image embed
(none) {{measurement unit}} Designation match in same dataset
(none) {{112-01-10}} Numeric ID match in same dataset

Each kind accepts an optional label: {{kind:target, label}}.

Deprecated: <<ref,title>> (AsciiDoc xref syntax) emits a deprecation warning. Migrate to {{kind:target}}.

See /learn/inline-content on any deployed site for a full interactive reference.

CLI Commands

npx concept-browser fetch      # Download datasets from GCR/repos
npx concept-browser generate   # Convert YAML → JSON-LD + RDF artifacts
npx concept-browser build      # Full pipeline: fetch + generate + build site

.gitignore for Your Project

Add these to your project's .gitignore:

.cb-content/
dist/
.datasets/
.gcr/
public/data/

Data Format

Each concept is a YAML file in datasets/{id}/concepts/:

---
data:
  identifier: "1-1-1"
  localized_concepts:
    eng: <uuid>
  sources:
    - type: authoritative
      origin:
        ref:
          source: "ISO 9000"
  definition:
    - content: "The concept definition text."
  terms:
    - type: expression
      normative_status: preferred
      designation: "quality"
  language_code: eng
---

Or use a Glossarist GCR package — set gcr_package in your dataset config and the CLI downloads it automatically.

Deployment

The build outputs a static site to dist/. Deploy it anywhere:

  • GitHub Pages: Push dist/ to gh-pages branch
  • Netlify/Vercel: Set build command to npx concept-browser build, publish directory to dist/
  • S3/CloudFront: aws s3 sync dist/ s3://my-bucket/

No server required. All data is pre-built into static JSON files.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages