Config authoring: everything needed to write a viewer config # Tutorial > Go from an empty page to a custom, themed protein viewer in four steps: add the component, point it at an accession, add your own track from a CSV, and style it. This is the guided, end-to-end path: start from nothing and finish with a working viewer that shows the full UniProt annotation for a protein **plus your own track**, recoloured to match your site. It’s one continuous example on a single protein: [`P05067`](https://www.uniprot.org/uniprotkb/P05067), amyloid precursor protein, the reference accession used throughout these docs. Each step ends with a **Try it live** link that opens the exact setup in the [playground](/protvista/playground/), so you can see it render and edit it without leaving the browser. When you want the full detail behind a step, follow the links to the how-to guides. The quickest way to follow along is the **[Starter Kit](https://github.com/ebi-webcomponents/protvista-starter-kit)**: a no-build template repository you can copy and publish with GitHub Pages, or serve from your own computer with one command (its README has the steps for Windows, macOS and Linux). Opening its `index.html` by double-clicking doesn’t work: browsers block a page opened from disk from reading the config. You can also work in your own page served by any static file server or your app’s dev server, or use the [playground](/protvista/playground/) for the config-only steps. No build tooling is required. Note The configuration surface below (`rows:`, `kind:`, `extends:`) is v5. The stable 4.x release does not understand it, so use the version Step 1 loads. ## Step 1: Add the component and point it at an accession `` is a **web component**: a custom element that works in any page with no bundler or framework. Load it once and drop the tag in with an accession: Paste the following HTML snippet into the `` of your HTML page. This snippet is not YAML or JSON, so do not paste it into the playground’s config editor. To try the viewer without editing an HTML file, use the “Try it live” link below. ```html ``` That address serves the published v5 beta straight from npm, so there is nothing to install. Pin the exact version, as above: `@beta` and `@5` would move under you as the beta develops. To build it yourself instead, clone the repository and copy the **contents** of `dist/` next to your page. Copy the whole folder, not just the one file: the build is split up, so `protvista-uniprot.mjs` loads `errors.js` and `filter-config.js` from the same place (and `format.js` / `js-yaml.js` when it needs them), and will not run on its own. ```sh git clone --branch next https://github.com/ebi-webcomponents/protvista cd protvista pnpm install pnpm build ``` Then copy the contents of `dist/` next to your HTML page. The `--branch next` matters: the repository’s default branch, `main`, is the 4.x line. Either way, the `accession` attribute on its own gives you the **full default UniProt viewer** for that protein: domains, variants, binding sites, structure coverage, AlphaFold confidence, and more. No config required. ![The default UniProt viewer for P05067, showing collapsed track groups for molecule processing, sequence information, topology, domains, sites, PTMs, epitopes, antigens, mutagenesis, variation, proteomics, structure coverage, AlphaFold confidence and AlphaMissense pathogenicity, aligned to the protein’s sequence.](/protvista/_astro/tutorial-default-viewer.DEnx5K9Z_1Tc2mB.webp) *The full default UniProt viewer, from one accession and no configuration.* [Embed the viewer](/protvista/embed) covers both ways to load the component and every attribute (`config-src`, `notooltip`, `nostructure`, …). ## Step 2: Add your own track from a CSV Now bring your own annotations. A `features` track can read a **CSV** file directly: point `data:` at the file and the `.csv` extension picks the parser for you: no `adapter:` needed. Say your lab has flagged some hotspot regions. Put them in a CSV with the feature-record columns `type,start,end,description,score`: ```csv type,start,end,description,score DOMAIN,18,289,Extracellular domain (custom re-annotation),0.95 BINDING,132,140,Predicted heparin-binding site,0.87 REGION,290,340,Acidic-rich linker region,0.6 MUTAGEN,614,614,Lab-observed loss-of-function point mutation,0.75 ``` Then describe a viewer that shows just that file as one standalone track (a `rows:` entry with `data:` and no `tracks:` needs no group wrapper): ```yaml accession: P05067 rows: - id: hotspots label: Hotspots kind: features data: ./hotspots.csv description: Hotspot regions identified by our lab's pipeline ``` Save the config as `my-config.yaml` next to `hotspots.csv` and point the element at it: ```html ``` Caution `data: ./hotspots.csv` is fetched **relative to the hosting page**, not the config file. If a file-backed track renders empty, this is the usual cause: serve the page from the same directory as the data, or use an absolute URL. ![A ProtVista viewer showing a single track named Hotspots, with four feature blocks positioned along the amino-acid sequence of P05067.](/protvista/_astro/tutorial-standalone-csv.Ceog0Rrq_Z2j1fRq.webp) *A standalone track loaded from a CSV file.* [Load your own data](/protvista/your-data) covers the full feature record and the TSV, JSON, and BED formats. `type` drives the colour and shape you see above — [Feature type and shape vocabulary](/protvista/type-and-shape-vocabulary) lists every recognised value and what an unrecognised one like `HOTSPOT` would look like instead. ## Step 3: Layer it onto the full UniProt viewer A standalone track is a viewer of its own. More often you want your track *on top of* everything UniProt already provides. Instead of rebuilding the default viewer, **`extends`** it: you inherit all its sources, groups, and themes, and declare only your addition. The custom data can be simpler here — the default viewer supplies the rest: ```csv type,start,end,description,score DOMAIN,18,289,Custom re-annotation of the extracellular domain,0.9 BINDING,614,614,Lab-observed candidate binding residue,0.72 ``` ```yaml accession: P05067 extends: /src/default-config.yaml rows: - id: MY_LAB label: My lab tracks: - id: hotspots kind: features data: ./hotspots.csv description: Hotspot regions identified by our lab's pipeline, layered on top of the canonical UniProt viewer ``` That’s the whole config: the full canonical viewer, with a **My lab** group added at the end. Caution `extends: /src/default-config.yaml` resolves only when the page is served from this repo’s root; a deployed site must point `extends` at a hosted copy instead — either your own, or the published package on a CDN: `https://cdn.jsdelivr.net/npm/protvista-uniprot@5.0.0-beta.5/dist/default-config.yaml`. See [Author a config](/protvista/configure#reuse-the-default-with-extends) for the merge rules and the full caveat. ![The full default UniProt viewer for P05067 with an additional group labelled My lab at the bottom, containing the lab’s own Hotspots track.](/protvista/_astro/tutorial-extended.C8OVsyD8_1bauXl.webp) *A lab’s own group layered on top of the canonical UniProt viewer with `extends:`.* ## Step 4: Style it Finally, make it yours. There are two levers, no rebuild required. **No code: from the config.** A `theme:` block recolours the viewer chrome directly, so an author who doesn’t write CSS can still brand the viewer. `labelColor` tints the row-label panel — group headers get the colour, track labels a lighter tint of it, so the group/track hierarchy stays visible; the optional `accentColor` sets focus rings and the datatable’s active-row marker. Dark or light, the label text, caret and hover state are derived from your colour, so a dark brand colour stays readable rather than leaving near-black text on a near-black cell. (One caveat: mid-greys around `#808080` sit too close to both black and white for *any* text to reach full AA contrast — the viewer picks the better of the two, but a clearly light or dark colour is the safer choice.) ```yaml theme: labelColor: '#e8f5e9' accentColor: '#2e7d32' ``` **From your page’s CSS.** For full control, `` exposes `--protvista-*` design tokens, and its datatable exposes `::part` hooks — set them in ordinary CSS on the page: ```css protvista-uniprot { --protvista-color-accent: #7b2d8e; --protvista-group-label-bg: #efe6f5; } ``` Caution The two levers are not additive: a config `theme:` **wins**. `labelColor` and `accentColor` are applied as inline styles on the element, so they override page CSS targeting the same tokens — including both tokens in the example above. Pick one lever, or force the CSS with `!important`. See [No-code theming from the config](/protvista/theming#no-code-theming-from-the-config). [Theme the viewer](/protvista/theming) lists every token and the datatable `::part` hooks. ![A ProtVista viewer whose row labels are tinted pale green, showing a track of features supplied inline in the configuration.](/protvista/_astro/tutorial-themed.Dt8tdqcW_1xCctm.webp) *The `inline-data` preset’s `theme:` block: `labelColor` tints the row-label panel.* ## You’re done You went from an empty page to a viewer that shows the full UniProt annotation for a protein, adds your own track from a CSV, and matches your site’s colours. Where to go next: * [Author a config](/protvista/configure) — every field a config can hold. * [Load your own data](/protvista/your-data) — TSV, JSON, BED, and inline data. * [Theme the viewer](/protvista/theming) — the full token and `::part` reference. * [Configuration vs data](/protvista/configuration-vs-data) — what your config controls versus what a data provider supplies. * [Escape hatches](/protvista/escape-hatches) — custom parsers, kinds, and themes when the built-ins aren’t enough. * [Playground](/protvista/playground/) — edit any config live and share it by link. * [Starter Kit](https://github.com/ebi-webcomponents/protvista-starter-kit) — a no-build template repository to copy and go. * [Runnable examples](https://github.com/ebi-webcomponents/protvista/tree/next/examples) — the CI-validated configs this tutorial draws from. *Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).* # Author a config > Write the short YAML or JSON file that tells the viewer which tracks to show, and where their data comes from. A **config** is a small document that describes what the viewer should show: the rows and tracks, where each track’s data comes from, and how it’s drawn. You provide it in one of two ways: point `config-src` at a **YAML or JSON** file (recommended), or assign it to the element’s **`viewerConfig`** property (a parsed object, or a YAML/JSON string). You never name internal components or adapters — you describe intent with high-level concepts. ## The minimal config ```yaml accession: P05067 sources: features: https://www.ebi.ac.uk/proteins/api/features/{accession} rows: - id: DOMAINS tracks: - id: domain kind: features filter: DOMAIN data: features ``` This renders one collapsible group, **Domains** (the label is title-cased from the `id`), containing one track, **Domain**, populated from the `features` URL (with `{accession}` substituted at fetch time) and narrowed to items whose type is `DOMAIN`. No `version`, no explicit component, no `label` — minimal configs collapse to the minimum. ## The building blocks ### `accession` The UniProt entry to display. Required unless [`sequence`](#sequence) is set — the viewer fetches the sequence for this accession before loading any track. Set one or the other, never both. With neither, the viewer shows a `missing-protein` error instead of mounting blank. ### `sequence` Your own protein, for one that isn’t in UniProt: use it instead of `accession`. It takes raw residues, inline FASTA, or a path or URL to a one-record FASTA file: ```yaml sequence: ./my-protein.fasta # fetched relative to the page, like data files ``` ```yaml sequence: | # `|`, not `>`: `>` folds the lines into one >my construct v2 MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQAPILSRVGDGTQDNLSGAEKAVQVKVKALPDAQ ``` The viewer then makes no request of its own (no UniProt entry, no structure panel) and shows the FASTA header wherever it would show the accession. Only tracks that read your own data work; see [Proteins outside UniProt](/protvista/sequence-only). ### `sources` A map of named URL templates. Use `{accession}` (or any other [variable](#variables)) as a placeholder; it’s filled in at fetch time. Tracks refer to a source by its key, so you write the URL once and reuse it: ```yaml sources: features: https://www.ebi.ac.uk/proteins/api/features/{accession} variation: https://www.ebi.ac.uk/proteins/api/variation/{accession} ``` #### The viewer’s own requests Besides track data, the viewer makes a few requests of its own: the entry it reads the sequence from, and the three lookups behind the 3D structure panel. Each goes to EBI unless your `sources` has a key of the same name, for example to point the viewer at a mirror: | Key | What it fetches | Default | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `sequenceEntry` | The entry the sequence is read from. It must answer with [Proteins API](https://www.ebi.ac.uk/proteins/api/doc/) entry JSON. | `https://www.ebi.ac.uk/proteins/api/proteins/{accession}` | | `structureUniprot` | The UniProtKB entry the structure panel reads PDB structures from. | `https://rest.uniprot.org/uniprotkb/{accession}` | | `structureAlphafold` | The AlphaFold DB predictions the structure panel lists. | `https://alphafold.ebi.ac.uk/api/prediction/{accession}?include_complexes=true` | | `structureBeacons` | The [3D-Beacons](https://www.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/) summary the structure panel lists models from. | `https://www.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/{accession}.json?exclude_provider=pdbe` | ```yaml sources: sequenceEntry: https://proteins.mirror.example.org/api/proteins/{accession} ``` Their `{tokens}` are filled like any data URL’s, from the [variables](#variables) below. A replacement must answer with the same JSON as the default it replaces. A [`sequence:`](#sequence) config makes none of these requests. A page that uses `` on its own sets these as the `uniprot-url`, `alphafold-url` and `beacons-url` attributes instead (see [Embed the viewer](/protvista/embed)). Given a `checksum` in place of an accession, that element makes a single 3D-Beacons lookup by sequence MD5, which its `beacons-checksum-url` attribute replaces. Its default is `https://www.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/v2/sequence/summary?id={checksum}&type=md5`, with `{checksum}` filled in. ### `variables` Baseline values for other `{tokens}` in your data URLs. The page can override each one with a `data-*` attribute on the element, so one config can serve several species, builds or datasets: ```yaml variables: species: human sources: features: https://api.example.org/{species}/features/{accession} ``` ```html ``` * Precedence, lowest first: `variables:`, then `data-*` attributes, then the `accession` attribute. `{accession}` always comes from `accession`. * Multi-word attributes are camelCased: `data-dataset-id` fills `{datasetId}`. * Values are URL-encoded. A value of exactly `.` or `..`, or one with malformed Unicode, is refused. * A token defined nowhere is a `missing-variable` warning. If nothing supplies it when the data loads, that URL isn’t fetched (a console warning says why) and its track is empty. * Changing a `data-*` attribute on the live element reloads the data. ### `rows` The list of things to show, top to bottom. A row is **either**: * a **group** — has a `tracks:` list, and renders as a collapsible section; or * a **standalone track** — has `data:` directly (no `tracks:`), for a single lane with no group wrapper. ```yaml rows: # A group of two tracks - id: MOLECULE_PROCESSING label: Molecule processing tracks: - id: signal kind: features filter: SIGNAL data: features - id: chain kind: features filter: CHAIN data: features # A standalone track (no group) - id: hotspots label: Hotspots kind: features data: ./hotspots.csv ``` ### `tracks` Each track needs an `id` and a `kind`, and a `data` source. Common fields: | Field | Purpose | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Unique within its group. Also the fallback label. | | `kind` | The track type — `features`, `variants`, `alphafold-confidence`, … See [Built-in track kinds](/protvista/track-kinds). | | `data` | Where the data comes from: a `sources` key, a URL, a file path, or inline. See [Load your own data](/protvista/your-data). | | `filter` | Keep only records of one `type` (e.g. `DOMAIN`). A convenience shortcut. | | `label` | Human-readable track title. Supports rich inline text. | | `description` | Longer text shown alongside the track. | | `rendering` | Visual settings — `color`, `shape`, `height`, `layout`, `colorScale`. See [`rendering`](#rendering). | | `hidden` | `true` ships the track hidden: a visitor can reveal it from **Customize**. Also valid on a group. See [Customize the layout](/protvista/customize-layout#authoring-a-row-or-track-hidden-hidden). | ### `kind` The `kind` is a **domain concept**, not a component name. `kind: features` draws feature regions; `kind: variants` draws single-residue variants; `kind: alphafold-confidence` draws an AlphaFold confidence ramp. Each kind knows which internal component and data adapter to use, so you don’t. See the full list in [Built-in track kinds](/protvista/track-kinds). ### `data` Points a track at its data. It can be: * a **`sources` key** — `data: features`; * a **URL** — `data: https://example.org/regions.csv`; * a **file path** — `data: ./hotspots.csv` (the format is inferred from the extension); or * **inline** — the records written directly in the config. The [Load your own data](/protvista/your-data) guide covers each in detail. ### `rendering` How a track looks. Set it on a track, on a group (its tracks inherit it), or under `defaults:` for every track. Each field is resolved separately, and the first of these that sets it wins: the track, its `kind`, its group, then `defaults:`. The `kind` step matters for `colorScale`: `alphafold-confidence`, `alphamissense-pathogenicity` and `alphamissense-heatmap` bring their own ramp, which beats a group’s or `defaults:` `colorScale`. A `colorScale` on such a group colours only the group’s collapsed row; to recolour the track itself, set it on the track. | Field | What it does | Where it has an effect | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `color` | The colour of every feature the track draws. A feature’s own `color` column wins. | Feature tracks | | `shape` | The glyph of every feature the track draws, one of the [shape names](/protvista/type-and-shape-vocabulary). A feature’s own `shape` column wins. | Feature tracks | | `height` | The track’s height in pixels. Unset, a feature track is 40 px tall, a line graph 50, a coloured sequence 13, a heatmap 300 and the variant plot 500. On a group, it also sizes the collapsed row. | Every track | | `layout` | `non-overlapping` (the default) gives overlapping features a row each; `default` draws them all on one row. | Feature tracks | | `colorScale` | A colour gradient: a `theme` (`alphafold-ramp`, `alphamissense-ramp`) or explicit `stops`. | Coloured-sequence tracks (`alphafold-confidence`, `alphamissense-pathogenicity`) and heatmaps (`alphamissense-heatmap`) | The rows of a `non-overlapping` track share its height, so ten overlapping features (isoforms, say) get thin rows at the default 40 px. Give them room: ```yaml - id: isoforms label: Isoforms kind: features data: ./isoforms.csv rendering: height: 120 ``` `height` under `defaults:` sizes every track, whatever its kind. `color`, `shape`, `layout` or `colorScale` on a track that can’t use it does nothing, and the validator says so with a `rendering-field-ignored` warning. In particular, a feature track has no colour scale: to colour features by score, give each one a `color` column ([Style and annotate each feature from your file](/protvista/your-data#style-and-annotate-each-feature-from-your-file)). ## Editor autocomplete Point your YAML/JSON editor at the published schema for validation and autocomplete as you write: ```yaml # yaml-language-server: $schema=https://ebi-webcomponents.github.io/protvista/schema/v1/config.schema.json ``` ## Reuse the default with `extends` To keep the entire canonical UniProt viewer and just add something of your own, `extends` it — you inherit all its sources, groups, and themes, and only declare your additions: ```yaml accession: P05067 extends: /src/default-config.yaml rows: - id: MY_LAB label: My lab tracks: - id: hotspots kind: features data: ./hotspots.csv ``` Caution `/src/default-config.yaml` resolves only when the page is served from the repository root (e.g. the dev server, `pnpm start`). A deployed site does not serve `src/`, so an embedder who copies this verbatim gets a 404. For anything beyond local development, point `extends` at your own hosted copy of the config, or at the published package on a CDN: ```yaml extends: https://cdn.jsdelivr.net/npm/protvista-uniprot@5.0.0-beta.5/dist/default-config.yaml ``` That path is served straight from the npm tarball, so pin an exact version — a config written against one release is not guaranteed to merge cleanly into another. ## Where to go next * [Load your own data](/protvista/your-data) — the `data` descriptor in full. * [Built-in track kinds](/protvista/track-kinds) — every `kind` and what it draws. * [Configuration vs data](/protvista/configuration-vs-data) — what your config controls versus what a data provider must supply. * The normative, field-by-field reference is [`specs/config-approach.md`](https://github.com/ebi-webcomponents/protvista/blob/next/specs/config-approach.md). *Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).* # Load your own data > How to load your own data into ProtVista: choose a format (CSV, TSV, JSON or BED) and give your data the shape a track needs. ProtVista isn’t limited to UniProt. Most tracks can read your own annotations from a **CSV, TSV, JSON, or BED** file, from a URL, or written straight into the config. This page shows the shape your data needs and how to point a track at it. You keep the same `kind:` either way. `kind: variants` draws variants whether the records come from the UniProt API or from your spreadsheet — the kind picks the renderer, the tooltips, and the colours; your file only has to carry the records. Which records depends on what the track draws, and there are three shapes in all: | Shape | Required columns | Used by | | ------------------------------------------------ | ---------------------- | --------------------------------------------------------------------------------- | | [Feature record](#the-feature-record) | `type`, `start`, `end` | `features`, `interpro-features`, `peptides`, `peptides-ptm`, `structure-coverage` | | [Point record](#a-line-graph-of-your-own-values) | `position`, `value` | `linegraph`, `variant-counts`, `rna-editing-counts` | | [Variation record](#your-own-variants) | `position`, `variant` | `variants`, `rna-editing` | Kinds named for a provider — `alphafold-confidence`, `alphamissense-pathogenicity`, `alphamissense-heatmap` — read that provider’s feed only; see [Built-in track kinds](/protvista/track-kinds). Your protein isn’t in UniProt? Give the config a `sequence:` instead of an `accession:` — see [Proteins outside UniProt](/protvista/sequence-only). ## The feature record A `features` track draws a list of **feature records**. Each record has: | Field | Required | Meaning | | ------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | yes | A label/category for the feature (e.g. `DOMAIN`, `BINDING`). Also what `filter:` matches on. | | `start` | yes | 1-based start position (inclusive), a whole number. | | `end` | yes | 1-based end position (inclusive), a whole number no less than `start`. | | `description` | CSV/TSV: yes | Free text shown on hover/click. A CSV or TSV file must have a `description` **column** in its header row, though its cells may be empty; a file whose header is only `type,start,end` is rejected. Optional in JSON. | | `score` | no | A number, typically 0–1, for quality or confidence. | | `color` | no | This feature’s colour — any CSS colour (`#1f77b4`, `steelblue`, `rgb(…)`). Wins over the track’s `rendering.color`. | | `shape` | no | This feature’s glyph, one of the [shape names](/protvista/type-and-shape-vocabulary). Wins over the track’s `rendering.shape`. A value that names a JavaScript built-in (`valueOf`, `constructor`, …) is dropped with a `track-data` warning, so the feature takes the track’s or type’s shape. | | `fill` | no | This feature’s fill colour, when it should differ from `color`. | | `opacity` | no | A number from 0 to 1 (default 0.9). | Any other column is kept on the record for `dataTooltip` — see [Style and annotate each feature from your file](#style-and-annotate-each-feature-from-your-file). BED files carry only the first five fields. A machine-readable version is published as [`feature-record.schema.json`](https://ebi-webcomponents.github.io/protvista/schema/v1/feature-record.schema.json). The [Adapter reference](/protvista/adapter-reference) lists every record shape, which kinds draw it, and which encodings can carry it. `type` isn’t free text once it’s drawn: ProtVista recognises 45 type names (UniProt feature types plus a few for peptides, epitopes and structure coverage), each with its own default colour and shape, and anything else renders as a black rectangle. See [Feature type and shape vocabulary](/protvista/type-and-shape-vocabulary) for the full list, what an unrecognised type looks like, and the 22 `rendering.shape` values. ## Pick a format — the extension chooses the parser Point `data:` at a file and the **file extension says how it is encoded**, while the track’s `kind` says what the records are. Between them there is nothing left to configure. The extension always chooses a parser your track’s `kind` can use: `.csv` on a `kind: features` track reads feature records, `.csv` on a `kind: linegraph` track reads `position,value` points. The `kind` says what the track *is*; the extension says what your file *looks like*. Pair a kind with a format that can’t carry its records — `kind: variants` at a `./x.bed`, since BED only holds feature records — and the config is rejected with a message naming both sides, rather than the track quietly coming up empty. ### CSV A header row plus one row per feature. This example is a single standalone track (one `rows:` entry with `data:` and no group): ```yaml accession: P05067 rows: - id: hotspots label: Hotspots kind: features data: ./hotspots.csv description: Hotspot regions identified by our lab's pipeline ``` ```csv type,start,end,description,score DOMAIN,18,289,Extracellular domain (custom re-annotation),0.95 BINDING,132,140,Predicted heparin-binding site,0.87 REGION,290,340,Acidic-rich linker region,0.6 ``` ### TSV Same columns, tab-separated. Use a `.tsv` extension: ```yaml rows: - id: MY_LAB label: My lab tracks: - id: hotspots kind: features data: ./hotspots.tsv ``` #### Spreadsheet exports ProtVista reads commas (`.csv`) and tabs (`.tsv`) only, and the format alone decides which — it never guesses from the content. Excel’s “CSV” export in many European locales writes semicolons instead, so save the sheet as **Text (Tab delimited)** and read it as TSV (if your text has accented letters, see the encoding note below). Excel names that file `.txt`, so say the encoding outright with `format:`, which works whatever the name: ```yaml data: url: ./hotspots.txt format: tsv ``` Renaming the file to `.tsv` works too. A few more things to check when the file comes from Excel or was edited on Windows: * **Accented text.** Excel’s plain “CSV” and “Text (Tab delimited)” exports use the computer’s legacy encoding, so accented letters and symbols in `description` come out garbled. Choose **CSV UTF-8 (Comma delimited)** instead. If your Excel writes semicolons even then (common where the decimal separator is a comma), export the sheet as CSV or TSV from Google Sheets or LibreOffice instead. “Unicode Text” (UTF-16) can’t be read at all: it fails as if the header were missing. * **Decimal points, not commas.** In a CSV, `0,95` splits into two cells and the row fails as ragged (`expected 5 columns, got 6`); in a TSV it fails with `expected a number`. Use `0.95`. * **Check the real file name.** Windows hides extensions by default, so a file saved from Notepad as `hotspots.csv` may really be `hotspots.csv.txt`. Turn on **View → File name extensions** in File Explorer. * **Match the case of the name exactly.** Windows treats `Hotspots.csv` and `hotspots.csv` as the same file; a web server such as GitHub Pages does not. If a file’s header looks like it uses a different separator from the one its format implies, the missing-column error also names the separator the header seems to use and the fix: ```plaintext ./hotspots.csv (parsed as CSV): missing required header column "type". Header must contain type, start, end, description[, score]. The header looks semicolon-separated, which ProtVista does not read. … ``` The same locales also write decimal commas (`0,5`). Those still fail as `expected a number, got "0,5"`, so change them to `0.5`. ### JSON An array of feature-record objects. Use a `.json` extension: ```yaml rows: - id: MY_LAB label: My lab tracks: - id: hotspots kind: features data: ./hotspots.json ``` ```json [ { "type": "DOMAIN", "start": 18, "end": 289, "description": "Extracellular domain" }, { "type": "BINDING", "start": 132, "end": 140, "score": 0.87 } ] ``` ### BED Standard tab-delimited [BED](https://en.wikipedia.org/wiki/BED_\(file_format\)) — a common genomics format for interval/region data. Use a `.bed` extension: ```yaml rows: - id: MY_LAB label: My lab tracks: - id: hotspots kind: features data: ./regions.bed rendering: color: '#2e86c1' shape: roundRectangle ``` BED has no type column, so every record gets `type: BED`. That isn’t one of the [recognised types](/protvista/type-and-shape-vocabulary), so without the `rendering` block above every region draws as a black rectangle. ## Style and annotate each feature from your file Add a `color` column and each feature is painted on its own, so `DOMAIN` rows can be blue and `BINDING` rows red in the same track. Add any other column — a reference, a gene name, a link — and a [`dataTooltip`](/protvista/data-tooltip) can show it: ```yaml accession: P05067 rows: - id: MY_LAB label: My lab tracks: - id: hits label: Styled hits kind: features data: ./hits.csv rendering: color: '#7f7f7f' dataTooltip: kind: markdown template: | **{% $description %}** ({% $type %}, {% $start %}–{% $end %}) PMID {% $pmid %} · {% link href=$url %}PubMed{% /link %} ``` ```csv type,start,end,description,color,pmid,url DOMAIN,18,189,E1 domain,#1f77b4,12345678,https://pubmed.ncbi.nlm.nih.gov/12345678/ BINDING,132,140,Predicted heparin-binding site,#d62728,23456789,https://pubmed.ncbi.nlm.nih.gov/23456789/ REGION,290,340,Acidic-rich linker region,,, ``` (The PubMed IDs are placeholders.) For a runnable variant, see [`examples/csv-styled/`](https://github.com/ebi-webcomponents/protvista/tree/next/examples/csv-styled): the same track and pattern, with a lab-notebook `ref` column in place of `pmid`, a “Read more” link, and one more `DOMAIN` row. How it works: * **Which colour wins.** A feature’s own `color` / `shape` wins over the track’s `rendering:`, which wins over the default for its `type`. A blank cell leaves the field off, so the `REGION` row above is drawn in the track’s grey. The same goes for `fill` and `opacity`. * **Colours must be valid CSS colours.** The canvas cannot paint a typo like `bleu` or `#catFace`, and draws that feature in the *previous* feature’s colour instead. The viewer keeps the value but reports a `track-data` warning naming it (in the console, on the [`protvista-error` event](/protvista/troubleshooting), and in the playground). The check doesn’t know every modern CSS colour, so `oklch(…)` also warns, though it paints fine. * **`opacity` must be a number from 0 to 1.** Anything else fails the track, naming the row. * **Every other column is kept as written**, as text — a blank cell is an empty string — so a template can use it as `{% $pmid %}` and a `fields` list as `path: pmid`. `{% link href=$url %}…{% /link %}` turns a URL column into a link; a row whose URL is empty, or not an `http(s):` / `mailto:` / `/…` / `#…` / `?…` URL, shows the text without one. A protocol-relative `//host/…` value counts as `/…` and links to that host. Name columns like identifiers (`gene_name`, `p-value`): a template cannot reference a name with a space in it, a `fields` path cannot reach one with a dot, and `$ctx` is reserved for the tooltip context. * **A few names cannot come from a file.** `tooltipContent`, `locations`, `residuesToHighlight`, and names JavaScript reserves (`toString`, `constructor`, `__proto__`, …) are dropped from CSV/TSV/JSON files and from inline text read with `format:`, with a `track-data` warning naming them. Records written straight into the config with `from: inline` (and `setTrackData()` arrays) are trusted and may still set them. * **JSON files work the same way**: `color`, `shape` and `fill` must be strings and `opacity` a number, and any other key is kept as it is, nested objects included. * **BED files can’t carry any of this.** Their columns are positional; use the track’s `rendering:` instead. Column names are matched exactly: `Color` is just another column, not a colour. ## Your data next to public data Files and URLs mix freely. Here a live UniProt track sits above your own file: ```yaml accession: P05067 sources: features: https://www.ebi.ac.uk/proteins/api/features/{accession} rows: - id: UNIPROT_DOMAINS label: UniProt domains tracks: - id: domain kind: features filter: DOMAIN data: features - id: MY_LAB label: My lab tracks: - id: hotspots kind: features data: ./hotspots.json ``` ## Inline data — no file at all For small or generated data, write records directly in the config with `from: inline`: ```yaml rows: - id: MY_ANNOTATIONS label: My custom annotations tracks: - id: binding_sites label: Predicted binding sites kind: features data: from: inline inlineData: - { type: BINDING, start: 45, end: 52, description: ATP binding } - { type: BINDING, start: 120, end: 128, description: Mg2+ binding } rendering: color: '#e74c3c' shape: diamond ``` `shape` is one of 22 shape names (19 of which the canvas track draws), and this `rendering` block applies to every feature in the `binding_sites` track — see [Feature type and shape vocabulary](/protvista/type-and-shape-vocabulary) for the full set, and for how this replaces `BINDING`’s own default colour and shape. A record’s own `color` / `shape` (say `{ type: BINDING, start: 45, end: 52, color: '#2e86c1' }`) wins over `rendering` for that one feature, exactly as a `color` column does in a file. ## A line graph of your own values The `kind: linegraph` setting draws a line graph from a JSON array of `{ position, value }` records: `position` is a whole number, `value` any number. ```yaml accession: P05067 rows: - id: depth label: Read depth kind: linegraph data: https://my-lab.example/api/depth/{accession} description: Per-residue read depth from our pipeline ``` ```json [ { "position": 1, "value": 12 }, { "position": 2, "value": 15 }, { "position": 3, "value": 9 } ] ``` Points are drawn in the order you supply them, so sort your records by `position` before serving them — nothing sorts them for you or rejects duplicates. A malformed row fails with your own filename, the reading applied to it, and the offending row and column: ```plaintext ./depth.csv (parsed as CSV): row 3, column "value": expected a number, got "abc". ``` The same record shape works inline — `from: inline` with `inlineData:` — so a graph can render with no fetch at all. See [`examples/linegraph/`](https://github.com/ebi-webcomponents/protvista/tree/next/examples/linegraph), or open **Your own line graph (inline values)** in the [playground](/protvista/playground/). The `kind` decides what the records are, so `data: ./depth.json` on a `kind: linegraph` track is read as line-graph points rather than generic features — nothing to name, nothing to configure. Where an extension can’t tell us — a URL with no filename, or records pasted straight into the config — say the encoding outright: ```yaml data: url: https://my-lab.example/api/depth/{accession} format: json ``` The same records work as delimited text, which is usually what falls out of a spreadsheet or an analysis script. On a `kind: linegraph` track the extension picks the parser — `.csv` and `.tsv` read a `position,value` header row and produce exactly the graph the JSON form does: ```yaml - id: depth label: Read depth kind: linegraph data: ./depth.csv ``` ```csv position,value 1,412 30,688 60,905 ``` Columns may be in either order, extra columns are ignored (graph points have no per-point tooltip to show them in, unlike feature records), and a malformed cell fails naming your file, the reading, the row and the column (`./depth.csv (parsed as CSV): row 3, column "value": expected a number, got "abc"`). Note the records come from the `kind`: the same `./x.csv` on a `kind: features` track is read as feature records instead. (A track needs a `kind` or a `component`; one with neither is rejected as `missing-track-renderer`.) `kind: variant-counts` and `kind: rna-editing-counts` read the same `position,value` records, so a count you computed yourself renders on the same track the UniProt counts would. ### Per-residue conservation A conservation score per residue is a natural line graph. The [`examples/conservation/`](https://github.com/ebi-webcomponents/protvista/tree/next/examples/conservation) example, also the [conservation preset](/protvista/playground/#preset=conservation) in the playground, draws real scores for rubredoxin (P24297, 54 residues) from the Pfam PF00301 family alignment. It shows them beside a `SITE` track of the most conserved residues and UniProt’s own binding sites, and UniProt’s four iron-binding cysteines have the four highest scores. The scores are generated by a script in the repository, and [`PROVENANCE.md`](https://github.com/ebi-webcomponents/protvista/tree/next/examples/conservation/PROVENANCE.md) records the method, the releases and the checksums. When you read them: * **They are within one family.** They measure conservation across the Pfam domain family, not a whole-protein evolutionary rate. * **Rare residues score higher.** The score (Jensen–Shannon divergence from a background distribution) rewards rare residues, so an invariant cysteine or tryptophan outscores an invariant leucine. * **The ends have no score.** Residues outside the Pfam domain (1–3 and 50–54 here) get no point, and the line covers only the scored stretch. * **The y-axis is fitted to the data**, not to 0–1. * **The line has no hover.** Graph points have no tooltip, so the example puts each top residue’s score in its `SITE` feature’s `score` column and shows it with a [`dataTooltip`](/protvista/data-tooltip): a `kind: features` track’s own tooltip lists only type, description, start and end. ## Your own variants `kind: variants` is the kind the UniProt viewer uses for its variation track. Point it at a file and it reads your calls instead — same track, same hover detail, same filter widget. ```yaml accession: P05067 rows: - id: cohort_variants label: Cohort variants kind: variants data: ./my-variants.csv description: Missense variants called in our patient cohort ``` ```csv position,wildType,variant,description,consequence 672,D,N,Observed in 3 of 48 cohort samples,missense 692,K,N,Recurrent in early-onset subgroup,missense 723,T,*,Premature stop in one carrier,stop_gained ``` | Field | Required | Meaning | | ------------- | -------- | ------------------------------------------------------------------ | | `position` | yes | 1-based position of the changed residue, a whole number. | | `variant` | yes | The residue it changes to. `*` for a stop, `-` for a deletion. | | `wildType` | no | The original residue. Shown on hover and used to label the change. | | `description` | no | Free text shown on hover/click. | | `consequence` | no | Your own consequence label (e.g. `missense`), shown on hover. | The same records work as `.tsv`, or as `.json` with one object per change: ```json [ { "position": 672, "wildType": "D", "variant": "N", "description": "3 of 48 samples" }, { "position": 723, "wildType": "T", "variant": "*", "consequence": "stop_gained" } ] ``` Your file doesn’t carry the protein sequence — the viewer already fetched it for `accession:` and supplies it, which is what lets the track lay out one row per residue. With a [`sequence:`](/protvista/sequence-only) config, the residues come from your `sequence:` instead. `kind: rna-editing` reads exactly the same shape. ## Add to the default UniProt viewer To layer your track on top of the full canonical viewer instead of building from scratch, `extends` the default — see the `extends` section in [Author a config](/protvista/configure#reuse-the-default-with-extends). (Note the caveat there: the built-in `/src/default-config.yaml` path only resolves during local development — host your own copy for a deployed site.) ## A path gotcha to know A track’s `data: ./hotspots.csv` is fetched **relative to the hosting page**, not relative to the config file. If a file-backed track renders empty, this is the usual cause: the browser looked for the file next to your HTML page. Serve the page from the same directory as the data (or use an absolute URL). The runnable [`examples/`](https://github.com/ebi-webcomponents/protvista/tree/next/examples) each carry their data file beside the config for exactly this reason — see [`examples/README.md`](https://github.com/ebi-webcomponents/protvista/blob/next/examples/README.md). ## Try your file in the playground You don’t have to host a file to see it render. In the [playground](/protvista/playground/), press **Load data file…** (or drop the file onto the config editor) and pick a CSV, TSV, JSON or BED file (or a FASTA file, to set `sequence:`). It is read in your browser and never uploaded. Only its *name* goes into the config, and so into any link you share: the playground adds a track with `data: ./hits.csv`, exactly as a config next to the real file would say it. * A file whose extension doesn’t say how to read it (`hits.txt`, `export.tab`) asks you to choose a format, and the config gets `format:` to match: `data: { url: ./hits.txt, format: csv }`. A `.csv` whose header is tab-separated is offered as `tsv` the same way, and the new track’s `kind` follows the header’s columns (`position,value` makes a line graph). * In a config that `extends:` another, a new track’s id ends in `-local` (`PTM.csv` becomes `PTM-local`), so it can’t replace a base row with the same id. * If the config already names the file — say you pasted a Starter Kit config with `data: ./data/hits.csv` — just load `hits.csv`. It renders in that track with no edit to the config. * A `.fasta`, `.fa`, `.faa` or `.fas` file — or any other file, bar a CSV, TSV, JSON or BED one, whose first non-blank line starts with `>` — becomes the config’s `sequence:` instead of a track (see [Try your FASTA in the playground](/protvista/sequence-only#try-your-fasta-in-the-playground)). So does any file the config’s `sequence:` already names. Otherwise raw residues with no `>` line are read as data, so give them a `.fasta` name or a header line. * Problems with the file are listed under the editor, naming it: a parse error (`./hits.csv (parsed as CSV): row 3, column "start": …`) with the same advice a hosted viewer gives, and, once it renders, any rows that fall outside the protein. The file stays loaded until you reload the page. A shared link carries only the name, so whoever opens it is asked to load the file themselves. After fixing the file on disk, load it again to pick up the change. ## Custom columns or formats If your file doesn’t match the feature-record columns — different headings, a bespoke format — you can register your own parser with `registerAdapter` and name it on the track. See [Escape hatches](/protvista/escape-hatches). ## Ask an AI to write your config Give the assistant your protein accession, the tracks you want, your data file name and its column headers, and any tooltip or colour requirements. Attach the [Config authoring documentation](https://ebi-webcomponents.github.io/protvista/_llms-txt/config-authoring.txt) so it can read the instructions and examples directly. You can also provide the [documentation index](https://ebi-webcomponents.github.io/protvista/llms.txt) and the [config schema](https://ebi-webcomponents.github.io/protvista/schema/v1/config.schema.json). Providing the config schema does not guarantee a working config. For example, an AI may use an unresolved preset name such as `default` in `extends`, omit required fields, or reference an undeclared source. Provide the full Config authoring documentation alongside your request so the assistant has the instructions and examples available. Treat any AI-generated config as a draft: results vary by prompt and assistant, so always verify the output in the playground. Always paste the config into the [playground](https://ebi-webcomponents.github.io/protvista/playground/) and load your sample file with **Load data file…**. Check the listed errors and warnings, confirm that the requested tracks appear in the right order, and test any tooltips, links and visibility controls before using it on your page. ## Start from your field * [Structural biology and drug discovery](/protvista/playground/#preset=community-structure-egfr) — PDBe co-crystal drug binding contacts for 7 approved TKIs on human EGFR (`P00533`), PDB 3D structure coverage, and AlphaFold confidence. ## Where to go next * [Configuration vs data](/protvista/configuration-vs-data) — the boundary this page sits on. * [Adapter reference](/protvista/adapter-reference) — exact payload shapes. * [Feature type and shape vocabulary](/protvista/type-and-shape-vocabulary) — what each `type` looks like, and every `shape`. * [Proteins outside UniProt](/protvista/sequence-only) — your own sequence instead of an accession. * [Troubleshoot errors](/protvista/troubleshooting) — when a track won’t load. *Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).* # Built-in track kinds > Describing each track's 'kind' (what it is in domain terms), colour scales and how to use a kind with your own data. A track’s `kind` says *what it is* in domain terms — features, variants, a confidence ramp — and ProtVista maps it to the right renderer and data adapter for you. Write `kind: features`, not a component name. This page lists every built-in kind. For the exact data shape each one consumes, see the [Adapter reference](/protvista/adapter-reference); for the boundary between what your config controls and what the data must provide, see [Configuration vs data](/protvista/configuration-vs-data). ## The kinds A kind’s name tells you whether you can bring your own data to it: * **A plain domain word** — `features`, `variants`, `peptides` — takes a file from you as readily as it takes a provider API. Point `data:` at your CSV, TSV, or JSON and the kind reads it. * **A name that starts with a provider** — `alphafold-confidence`, `alphamissense-heatmap` — reads that provider’s feed and nothing else. Those tracks are assembled from two API responses plus a further fetch, which no single file can stand in for. | `kind` | What it draws | Your own data | Typical provider | | ----------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------- | -------------------------- | | `features` | Sequence feature regions (domains, chains, binding sites, signal peptides…) drawn as shapes along the sequence. | `.csv` `.tsv` `.json` `.bed` | UniProt features API | | `interpro-features` | InterPro domain and family predictions. | `.csv` `.tsv` `.json` `.bed` | InterPro entries API | | `variants` | Single-residue variants (amino-acid changes), with disease association. | `.csv` `.tsv` `.json` | UniProt variation API | | `variant-counts` | A line graph of variant frequency along the sequence. | `.csv` `.tsv` `.json` | UniProt variation counts | | `rna-editing` | RNA-editing sites, drawn like variants. | `.csv` `.tsv` `.json` | UniProt RNA-editing API | | `rna-editing-counts` | A line graph of RNA-editing frequency. | `.csv` `.tsv` `.json` | UniProt RNA-editing counts | | `linegraph` | A line graph of numeric values you supply, one per position. | `.csv` `.tsv` `.json`, or inline | — (yours by design) | | `peptides` | Mass-spectrometry-detected peptides. | `.csv` `.tsv` `.json` `.bed` | UniProt proteomics API | | `peptides-ptm` | Post-translational modifications detected by MS. | `.csv` `.tsv` `.json` `.bed` | UniProt proteomics PTM API | | `structure-coverage` | Which stretches of the sequence are covered by known 3D structures. | `.csv` `.tsv` `.json` `.bed` | UniProt/PDBe coverage | | `alphafold-confidence` | AlphaFold per-residue confidence (pLDDT), drawn as a colour gradient over the sequence. | — | AlphaFold prediction | | `alphamissense-pathogenicity` | AlphaMissense average pathogenicity per residue, as a colour gradient. | — | AlphaMissense average | | `alphamissense-heatmap` | AlphaMissense pathogenicity as a full heatmap (every substitution × position). | — | AlphaMissense full matrix | Three record shapes cover every “your own data” cell above — features (`type,start,end`, plus a `description` column in CSV/TSV), points (`position,value`), and residue changes (`position,variant`). Which one a kind reads follows from what it draws, so `variants` and `rna-editing` share one, and every feature kind shares another. ## Colour scales The score kinds (`alphafold-confidence`, `alphamissense-pathogenicity`) come with colour ramps built in — AlphaFold’s confidence ramp (orange = low confidence, through to blue = very high) and AlphaMissense’s benign-to-pathogenic scale (blue = benign, through to red = pathogenic). Their colour-blind and contrast results are on [Accessibility and known limitations](/protvista/accessibility). You can define and apply your own ramps; see [Theme the viewer](/protvista/theming) and [Escape hatches](/protvista/escape-hatches) (`registerTheme`). ## Using a kind with your own data Swap the `data:` and keep everything else. The kind still decides the renderer, the tooltips, and the colours — only the parser changes: ```yaml # The UniProt variation API… - id: variants kind: variants data: variation # …and your own calls, same kind, same track. - id: variants kind: variants data: ./my-variants.csv ``` Pair a kind with a format it has no parser for — `kind: alphafold-confidence` at a `./x.csv` — and the config is rejected before anything loads, with a message naming the kinds that *do* read that format. See [Load your own data](/protvista/your-data) for each shape’s columns. ## Isoform rows A `features` track can also draw one row per isoform of a reviewed UniProt entry, on the canonical sequence’s coordinates. Point it at the entry and name the built-in adapter `uniprot-isoforms-json`: ```yaml sources: uniprotEntry: https://rest.uniprot.org/uniprotkb/{accession}.json?fields=sequence,ft_var_seq,cc_alternative_products rows: - id: isoforms label: Isoforms kind: features data: source: uniprotEntry adapter: uniprot-isoforms-json rendering: height: 160 ``` Each isoform UniProt lists gets a row: the canonical in blue, the others in grey. A gap in a row is a stretch of the canonical sequence that the isoform lacks. A darker mark is a canonical residue the isoform replaces with different ones; at an insertion, it marks the residue the new ones are added next to. Hover a row to see its changes, e.g. `P05067-4 (APP695): 289: E → V; 290-364: missing`. An External isoform, which UniProt describes in another entry, gets no row, and neither does one whose sequence UniProt doesn’t know (“Not described”). The canonical row’s tooltip names them. Use the canonical accession, such as `P10636`. An isoform’s own entry (`P10636-8`) has no alternative sequences, so the track stays empty. Ask for at least the fields above: the adapter needs the sequence, the alternative sequences (`ft_var_seq`) and the “Alternative products” comment (`cc_alternative_products`). UniProt rejects an unknown field name, such as `features`, with an error. The rows share the track’s height, 40 px unless you set one, so give the track about 15 px per isoform. Leave `layout` unset: the default, `non-overlapping`, keeps each isoform on its own row. The track runs only when a config names the adapter, so the default UniProt view makes no extra request. The `isoforms-app` example in the repository’s `examples/` folder is this config for APP (P05067), and the playground offers it as **APP isoforms** under **Community views**. *Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).* # Built-in adapter reference ProtVista *configuration* (rows, tracks, sources, rendering) is validated by the [config JSON Schema](https://ebi-webcomponents.github.io/protvista/schema/v1/config.schema.json); *track payloads* — the shapes adapters consume — are not. This page fills that gap: the expected input shape for every built-in adapter. For the config-vs-payload boundary see [Configuration vs data](/protvista/configuration-vs-data); for the normative shape-and-format contract see [specs/config-approach.md](https://github.com/ebi-webcomponents/protvista/blob/next/specs/config-approach.md#shape-and-format-normative). This reference is generated from the adapter code and kept in sync by drift tests, so it cannot silently diverge. It is a reference aid, not a normative schema. ## Bring your own data Two independent facts decide how a source is read. Your track’s `kind` says **which records** it needs; the file says **how they are encoded**. You never name an adapter for either: point a track at `./hits.csv` and the kind supplies the first half while the extension supplies the second. Use `format:` only when nothing can infer it — inline text, or a URL with no recognised extension. A malformed file fails with your own filename, the reading applied to it, and the offending row and column: `./depth.csv (parsed as CSV): row 3, column "value": expected a number, got "abc"`. ### The formats | Format | Extension | Fetched as | Records it can carry | | ------ | --------- | ---------- | --------------------------------------- | | `csv` | `.csv` | text | whatever the track’s kind draws | | `tsv` | `.tsv` | text | whatever the track’s kind draws | | `json` | `.json` | json | whatever the track’s kind draws | | `bed` | `.bed` | text | feature records (type, start, end) only | Only BED constrains what it can carry: it encodes feature semantics (0-based half-open, converted on read), so pairing it with a kind that draws anything else is a config error naming both sides. ## The record shapes Three shapes cover every kind that accepts your data. A machine-readable schema for the feature record is served at [`feature-record.schema.json`](https://ebi-webcomponents.github.io/protvista/schema/v1/feature-record.schema.json). ### feature records (type, start, end) Written by: `kind: features`, `kind: interpro-features`, `kind: peptides`, `kind: peptides-ptm`, `kind: structure-coverage`. Readable as: `csv`, `tsv`, `json`, `bed` — by file extension, or with an explicit `format:`. | Field | Type | Required | Notes | | ------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | string | Yes | Feature category label (e.g. DOMAIN, BINDING). Drives `filter:` and colour grouping. | | `start` | integer | Yes | 1-based start position (inclusive). | | `end` | integer | Yes | 1-based end position (inclusive). Must not be less than `start`. | | `description` | string | No | Free text shown in the default tooltip. CSV/TSV: the `description` header column is required (its cells may be empty); optional in JSON, and BED reads it from the `name` column. Omitted when empty. | | `score` | number | No | Optional numeric score. Omitted when empty. | | `color` | string | No | Per-feature colour (any CSS colour) for the outline, and the fill unless `fill` is set; wins over the track’s `rendering.color`. CSV/TSV/JSON only — BED never sets it. Omitted when empty. | | `shape` | string | No | Per-feature glyph (see the type and shape vocabulary); wins over the track’s `rendering.shape`. CSV/TSV/JSON only. Omitted when empty, and dropped with a warning when it names an Object.prototype property (`valueOf`, `constructor`, …). | | `fill` | string | No | Per-feature fill colour; defaults to `color`. CSV/TSV/JSON only. Omitted when empty. | | `opacity` | number | No | Per-feature opacity, from 0 to 1 (the canvas default is 0.9). CSV/TSV/JSON only. Omitted when empty; any other value is an error. | Any other column of a CSV/TSV file, or key of a JSON record, is kept on the record as written, so a `dataTooltip` can show it. `tooltipContent`, `locations`, `residuesToHighlight` and names JavaScript reserves (`__proto__`, `toString`, …) are dropped with a `track-data` warning. BED records carry only the fields above that BED can express — never the render fields. ### point records (position, value) Written by: `kind: linegraph`, `kind: rna-editing-counts`, `kind: variant-counts`. Readable as: `csv`, `tsv`, `json` — by file extension, or with an explicit `format:`. | Field | Type | Required | Notes | | ---------- | ------- | -------- | ------------------------------------------------------ | | `position` | integer | Yes | 1-based residue position. | | `value` | number | Yes | The number plotted at that position. Any finite value. | ### variation records (position, variant) Written by: `kind: rna-editing`, `kind: variants`. Readable as: `csv`, `tsv`, `json` — by file extension, or with an explicit `format:`. | Field | Type | Required | Notes | | ------------- | ------- | -------- | -------------------------------------------------------------- | | `position` | integer | Yes | 1-based position of the changed residue. | | `variant` | string | Yes | The residue it changes to. `*` for a stop, `-` for a deletion. | | `wildType` | string | No | The original residue. Shown on hover. | | `description` | string | No | Free text shown on hover/click. | | `consequence` | string | No | Your own consequence label, e.g. `missense`. | ## Built-in track adapters (provider-supplied) These back the kinds named for their provider. Their input is a response from an EBI API (or equivalent) — you do **not** author these payloads, and no file can stand in for one: each takes two responses plus a further fetch. The shapes below are informational, not a contract you must produce. | Semantic kind | Adapter | Renders with | Inputs | Input shape | | ----------------------------- | --------------------------------- | ------------------------------ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `features` | `uniprot-features-json` | `nightingale-track-canvas` | 1 | UniProt Proteins API features response — `{ features: [...] }`, each feature carrying `type`, `begin`, `end`, and evidence. | | `interpro-features` | `interpro-entries-json` | `nightingale-track-canvas` | 1 | InterPro protein-entries response — `{ results: [{ metadata, proteins: [{ entry_protein_locations }] }] }`. Representative-domain fragments are flattened into features. | | `variants` | `uniprot-variation-json` | `nightingale-variation-canvas` | 1 | UniProt Proteins API variation response — `{ sequence, features: [...] }` with per-variant genomic location, alternative sequence and predictions. | | `variant-counts` | `uniprot-variation-counts-json` | `nightingale-linegraph-track` | 1 | Same variation response as `uniprot-variation-json`; aggregated into per-position total and disease-causing variant counts for the line graph. | | `rna-editing` | `uniprot-rna-editing-json` | `nightingale-variation-canvas` | 1 | UniProt Proteins API RNA-editing response — `{ sequence, features: [{ locationType, variantType }] }`. | | `rna-editing-counts` | `uniprot-rna-editing-counts-json` | `nightingale-linegraph-track` | 1 | Same RNA-editing response as `uniprot-rna-editing-json`; aggregated into per-position missense counts for the line graph. | | `peptides` | `uniprot-proteomics-json` | `nightingale-track-canvas` | 1 | UniProt Proteomics API response — `{ features: [{ unique, ptms }] }`; PTMs are lifted onto each peptide as residues to highlight. | | `peptides-ptm` | `uniprot-proteomics-ptm-json` | `nightingale-track-canvas` | 1 | PTMeXchange proteomics-PTM response — `{ features: [{ begin, peptide, ptms: [{ name, position, dbReferences }] }] }`; emitted as per-residue MOD_RES markers coloured by confidence. | | `structure-coverage` | `uniprot-proteins-pdb-json` | `nightingale-track-canvas` | 1 | UniProt Proteins API entry — `{ dbReferences: [{ type: "PDB", properties: { chains } }] }`; PDB chain ranges are parsed and overlapping intervals merged. | | `alphafold-confidence` | `alphafold-prediction-json` | `nightingale-colored-sequence` | 2 (+ fetches a further URL) | AlphaFold prediction list (matched to the protein sequence) plus the UniProt entry. The adapter then fetches the per-residue confidence JSON and returns pLDDT categories. | | `alphamissense-pathogenicity` | `alphamissense-average-csv` | `nightingale-colored-sequence` | 2 (+ fetches a further URL) | AlphaFold prediction list (with an AlphaMissense annotations URL) plus the UniProt entry. The adapter fetches the annotations CSV and returns per-position average pathogenicity codes. | | `alphamissense-heatmap` | `alphamissense-full-csv` | `nightingale-sequence-heatmap` | 2 (+ fetches a further URL) | Same AlphaMissense annotations as `alphamissense-average-csv`, but returns the full per-mutation `{ xValue, yValue, score }` matrix for the heatmap. | ### `uniprot-proteomics-json` output Each item is the API’s peptide feature with every field passed through under its own name and value (`begin`, `end`, `peptide`, `unique`, `ptms`, `evidences`, `xrefs`, …), plus the fields below. Only `type` is overwritten; its API value is kept as `sourceType`. | Field | Notes | | --------------------- | -------------------------------------------------------------------------------------------------------- | | `type` | `'unique'` or `'non_unique'` (from the API’s `unique` flag), so `filter:` can split the two tracks. | | `sourceType` | The API feature’s own `type` (`PROTEOMICS` or `PROTEOMICS_PTM`), which `type` replaces. | | `ptms` | The API feature’s `ptms`, unchanged. Each `position` is relative to the peptide (1 = its first residue). | | `taxid` | The response’s top-level `taxid`, copied onto each feature. | | `start` | The API `begin`, which is also kept. | | `residuesToHighlight` | The peptide’s `ptms` as `{ name, position, sources, dbReferences }`, for highlighting. | | `category` | Always `PROTEOMICS`. | ### `uniprot-proteomics-ptm-json` output Each item is a marker the adapter builds, one per modification per residue (`start` = `end` = the absolute position). The API’s own `ptms` entries behind it pass through unchanged. The modified residue is not repeated on the marker; read it from the entry sequence at `start`. | Field | Notes | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | Always `MOD_RES_LS`. | | `ptms` | The API’s `ptms` entries for this modification at this residue, unchanged, across every peptide that reports it. Each `position` is relative to its own peptide. | | `confidenceScore` | The value the marker’s `color` is computed from: the `Confidence score` property shared by every entry’s `dbReferences` (`Gold`, `Silver` or `Bronze`). `null` when the entries report no score, or a mixture of scores (a mixture also logs a warning). | | `color` | The colour for `confidenceScore`, or `black` when it is `null`. | ## Related * [Configuration vs data](/protvista/configuration-vs-data) — what config controls vs what providers supply. * [specs/config-approach.md](https://github.com/ebi-webcomponents/protvista/blob/next/specs/config-approach.md) — normative Intent/Representation split. * [specs/config-approach.md — Shape and format](https://github.com/ebi-webcomponents/protvista/blob/next/specs/config-approach.md#shape-and-format-normative) — normative shape-and-format contract. * [examples/](https://github.com/ebi-webcomponents/protvista/tree/next/examples) — runnable, CI-validated config + data pairs. *Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).* # Authoring dataTooltip > 'dataTooltip' configures per-datapoint tooltips using three forms: a 'bare-string' for simple one-line templates, a 'fields' form for a flat property sheet of labelled rows, or a 'markdown' form for full templates with prose and conditional logic, while falling back to a default when a record has none of the fields. `dataTooltip` controls the per-datapoint tooltip shown when a user clicks a feature on a track. It has three authoring forms, listed here from least to most expressive. Pick the simplest one that works — the rendering pipeline is the same for all three. All three forms run through the same renderer: every field value is HTML-escaped at the leaf, link `href`s are routed through a scheme allowlist (`http:`, `https:`, `mailto:`, and values starting with `/`, `#` or `?` (a protocol-relative `//host/…` one links to that host); `javascript:`, `data:` and bare relative paths are dropped), and rich / interactive tooltips are not a config concern. If you need a custom React panel, evidence badges, taxonomy lookups, or any other stateful UI, listen for the Nightingale `change` event on the element and mount your own overlay, setting the `notooltip` attribute on `` to suppress the built-in popover. When a track has no `dataTooltip` at all, the resolver falls back to a per-kind default if one exists, and otherwise synthesizes a compact Markdoc tooltip from adapted payload fields such as `type`, `description`, position, variant details, significance, score, xrefs, evidences, and remaining scalar fields. Configs that don’t author a tooltip therefore still get a useful safety-net tooltip out of the box. ## Bare-string form A one-line Markdoc template. The YAML value is a string, so no quoting with nested maps needed. Shorthand for `{ kind: markdown, template: "…" }`. Fields on the datapoint are in scope as `$field`. ```yaml tracks: - id: signal label: Signal peptide kind: features filter: SIGNAL data: features dataTooltip: '**Signal peptide** {% $begin %}–{% $end %}' ``` ## `kind: fields` form A declarative list of labelled rows. Each entry renders as `
label

value

`. Use this when the tooltip is a flat property sheet without prose or conditional content. `path` is a dotted path against the item (e.g. `association.0.name`). Missing or empty values drop out silently rather than rendering an empty row. The value at `path` is coerced to string, HTML-escaped, and wrapped in `

` at the leaf. There is no per-field render hook; when you need rich, interactive, or stateful tooltips (xref badges, evidence icons, taxonomy lookups, React components, …), own the overlay in the host instead — see [React host integration](/protvista/react-integration). ```yaml tracks: - id: compbias label: Compositional bias kind: features filter: COMPBIAS data: features dataTooltip: kind: fields fields: - { path: type, label: Type } - { path: description, label: Description } - { path: begin, label: Start } - { path: end, label: End } ``` ## `kind: markdown` form A full Markdoc template. Use this when the tooltip needs prose or conditional fragments. Field interpolation uses `{% $field %}`; flow control uses Markdoc’s `{% if %}` / `{% else %}` / `{% /if %}`. ```yaml tracks: - id: domain label: Domain kind: features filter: DOMAIN data: features dataTooltip: kind: markdown template: | ### {% $description %} **Position:** {% $begin %}–{% $end %} {% if $score %}**Score:** {% $score %}{% /if %} ``` ## Links from a field Markdoc cannot put a variable into an ordinary link’s destination, so a field that holds a URL — a `url` column in your own file, say — becomes a link with the `{% link %}` tag: ```yaml dataTooltip: kind: markdown template: | PMID {% $pmid %}: {% link href=$url %}read on PubMed{% /link %} ``` The self-closing form, `{% link href=$url /%}`, uses the URL itself as the link text. The URL goes through the same allowlist as every other link: only an absolute `http:` / `https:` / `mailto:` URL, or one starting with `/`, `#` or `?`, becomes a link. A value starting with `//` is protocol-relative, not root-relative: `//example.org/x` links to another site, as an `https:` URL would. Anything else — `javascript:`, a bare relative path like `docs/x.html`, or a feature whose `url` is empty or missing — renders the text alone, with no link. Links open in the same tab. ## Fields from your own file Any column of your own CSV or TSV file, or any key of your JSON records, is in scope as `$column` in a template and as a `path` in a `fields` list — not only the documented feature fields. See [Style and annotate each feature from your file](/protvista/your-data#style-and-annotate-each-feature-from-your-file). Name columns like identifiers (`gene_name`, `p-value`): a template cannot reference a name with a space in it, a `fields` path cannot reach one with a dot in it, and `$ctx` always means the tooltip context, never a column called `ctx`. ## When a field is missing A field a record does not have renders as nothing: in the `fields` form its row drops out, and in a template `{% $field %}` renders empty. That is expected when only some records carry the field (to drop the text around it too, see [Guard a field some records lack](#guard-a-field-some-records-lack)), and a record with none of the fields shows the default tooltip (see [A record with none of the fields](#a-record-with-none-of-the-fields)). When **no** record on the track carries it, the name is almost certainly wrong, so the viewer says so — once per track each time the data loads, naming every such field: ```plaintext [protvista-uniprot] Track domains/hits: dataTooltip references unknown fields: pvalue, Gene ``` A row you add on its own, outside a group, is named by its track id alone (`Track hits: …`). The same text fires a `tooltip-field-miss` [`protvista-error` event](/protvista/troubleshooting#phases) with `severity: 'warning'` and the names in `context.fields`, and the playground lists it as a warning. The track itself renders as usual: there is no `⚠` badge and no alert panel, even with `strict` on. The names to check against are the record’s own: the fields a provider adapter outputs, or the column headers of your file (see [Fields from your own file](#fields-from-your-own-file)). A few details: * Every field the template names counts, including one inside `{% if $field %}`, a function such as `equals($field, "x")`, or `{% link href=$field %}`, and one in a fenced code block, which Markdoc fills in too. Inline code (single backticks) is printed literally, so a name there is not a reference. * A field that is present but empty (`''`, a blank cell, or `null` in a JSON key of your own) is not missing. The exceptions are a few built-in columns, which are left off a record that has no value for them, so a column with no value on any row reads as missing: * In a feature file, `description`, `score`, `color`, `shape`, `fill` and `opacity`: a blank CSV or TSV cell, or `null` in JSON. In JSON, `""` is left off too for `description` and the four render fields, but not for `score`: `"score": ""` is not a number, so it fails the track. * In a variation CSV or TSV file, `wildType`, `description` and `consequence`: a blank cell. * A variation file’s records carry `start` and `end` (not `position`), plus `variant` and `consequenceType`, and `wildType`, `description` and `consequence` when they have a value; its other columns are dropped. * For a dotted path such as `variant.wildType`, a record where `variant` is `null` counts as having it, so it does not warn. In the `fields` form that row just drops out. A template (`{% $variant.wildType %}`) currently fails the whole track on such a record, so guard it with `{% if $variant %}` or use the `fields` form. * `$ctx.accession`, `$ctx.trackId` and `$ctx.kind`, and any key you supply under the template’s `variables:`, are checked against those values rather than the records. * Only a `dataTooltip` you write is checked, and only one you write falls back to the default (below). A track using its kind’s built-in default, or the automatic tooltip, never warns, and neither do line-graph, coloured-sequence and heatmap tracks, which have no per-feature tooltip for `dataTooltip` to template (see [Line graphs](#line-graphs)). ### A record with none of the fields A record shows the track’s default tooltip instead of yours when both of these hold: * it has no value for any field the template names: each one is missing, `null`, or a blank cell (`''`); * what the template renders for it has no letter or digit: only punctuation, such as the `·` that `{% $gene %} · {% link href=$url /%}` leaves, or nothing at all. The default is what the track would show with no `dataTooltip`: its kind’s built-in tooltip (Type, Description, Start and End for `features`), or the automatic tooltip for a track with no `kind`. This is decided record by record, so on one track a record with a `url` shows your template and a record with neither field shows the default. It works the same in the `fields` form, where such a record would otherwise have no tooltip at all. Your template stays in charge whenever either condition fails: * A record with a value for at least one of the fields gets your template, whatever is left of it. * A template that has its own words for the case is kept: `{% if $gene %}{% $gene %}{% else /%}No gene recorded{% /if %}` shows “No gene recorded”, and `Lab hit {% $gene %}` shows “Lab hit”. * `$ctx.…` and keys you supply under `variables:` are not fields of the record, so a template that names only those never falls back. The fallback is silent. A field that no record on the track carries is still reported, as above. ### Guard a field some records lack To drop the text around a field that only some records have, wrap both in Markdoc’s built-in `{% if %}`: ```yaml dataTooltip: kind: markdown template: | {% if $gene %}{% $gene %} · {% /if %}{% link href=$url /%} ``` A record with both fields shows `HBA1 · https://…`, and one with a `url` but no `gene` (missing or `null`) shows the link alone. One with neither renders nothing, so it shows the default tooltip. A blank `gene` cell is different: see below. Markdoc’s `if` is false only for a missing field, `null` and `false`. A blank CSV or TSV cell is an empty string, which counts as true, so the template above still shows `·` before the link for a row whose `gene` cell is empty. To leave out blank cells too, test for them: ```yaml dataTooltip: kind: markdown template: | {% if and($gene, not(equals($gene, ""))) %}{% $gene %} · {% /if %}{% link href=$url /%} ``` ## When to leave `dataTooltip` off For every track in the default config, no `dataTooltip` is set. Each semantic `kind` carries a sensible tooltip default — authors who just want the canonical UniProt look get it for free. Only set `dataTooltip` when you want a track-specific override, or when you’re authoring a track that doesn’t match an existing kind’s default. ## Line graphs A line graph has no per-feature datapoint to template, so `dataTooltip` does not apply to it. Clicking a line graph opens a fixed tooltip instead: the clicked position, then each series’ value there. *Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).* # Proteins outside UniProt > You can use ProtVista without a UniProt accession by providing your own custom protein sequence—via a FASTA file, inline text, or raw residues—to visualize custom annotation tracks entirely independently of UniProt. ProtVista normally starts from a UniProt accession: it fetches the entry’s sequence, then draws tracks against it. A predicted protein, a synthetic construct, a patient-specific variant or an unreviewed assembly has no accession — so give the viewer the sequence itself instead. ```yaml sequence: ./my-protein.fasta rows: - id: hotspots label: Hotspots on {accession} kind: features data: ./hotspots.csv ``` With `sequence:` set, the viewer draws the navigation, the sequence and every track that reads your own data. It makes **no request of its own**: no UniProt entry, no structure panel. The runnable [`examples/sequence-only/`](https://github.com/ebi-webcomponents/protvista/tree/next/examples/sequence-only) is exactly this config, with its FASTA file and data beside it. ## When to use it Use `sequence:` when the protein you want to annotate is not a UniProt entry, or when the viewer must not reach UniProt at all. If your protein *is* in UniProt, use `accession:` — you get the sequence, the structure panel and every UniProt track for free, and you can still [add your own tracks](/protvista/your-data). It suits a short protein too. The [small-peptide preset](/protvista/playground/#preset=small-peptide) shows Trp-cage, a designed 20-residue miniprotein, from its own sequence with its secondary structure from a CSV file. `accession:` and `sequence:` are alternatives: set one, never both. Pairing your own sequence with UniProt annotations would place UniProt’s coordinates on a sequence they don’t describe. ## Three ways to give the sequence **A FASTA file** — a path or URL to a file with one record: ```yaml sequence: ./my-protein.fasta ``` The path is fetched relative to the hosting page, like a [data file](/protvista/your-data#a-path-gotcha-to-know), when the config loads. A bare name with a FASTA extension (`.fasta`, `.fa`, `.faa`, `.fas`) works too. **Inline FASTA** — written as a YAML literal block, with `|`: ```yaml sequence: | >my construct v2 MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQAPILSRVGDGTQDNLSGAEKAVQVKVKALPDAQ ``` Use `|`, not `>`. YAML’s folded `>` joins the lines into one, so the residues end up inside the header and the viewer reports that the sequence “has no residues”. The [own-sequence preset](/protvista/playground/#preset=own-sequence) is this form, with its track’s records written inline too, so it makes no request at all. Paste your own FASTA over the block, or load a `.fasta` file (below). **Raw residues** — just the letters: ```yaml sequence: MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQAPILSRVGDGTQDNLSGAEKAVQVKVKALPDAQ ``` In every form, whitespace and line breaks are ignored, letters are uppercased, and one trailing `*` (a stop) is dropped. Residues are the one-letter codes A–Z. A FASTA file must hold exactly one record. The FASTA header is the protein’s name: the viewer shows it wherever it would show an accession — `{accession}` in a label, the “No feature data available for …” message, and warnings about your data. A header longer than 80 characters is cut short; with no header, the name is “your sequence”. ## Which tracks work Every track that reads your own data: * a file or URL of your own — CSV, TSV, JSON or BED, on any kind with a record shape (`features`, `linegraph`, `variants`, …); see [Load your own data](/protvista/your-data); * inline data written into the config; * a `from: custom` source filled by `setTrackData()`. A `kind: variants` track takes its residues from your `sequence:`, just as it takes them from the entry in accession mode. These need UniProt, and are rejected when the config loads: * any data URL that uses `{accession}` — including every source in the default UniProt config; * the AlphaFold and AlphaMissense kinds (`alphafold-confidence`, `alphamissense-pathogenicity`, `alphamissense-heatmap`), or an explicit `adapter:` that reads those feeds; * a label that *links* to an `{accession}` URL, such as `[AlphaFold](https://alphafold.ebi.ac.uk/entry/{accession})`. `{accession}` in plain label text is fine: it becomes your FASTA header. To show per-residue scores you computed yourself — pLDDT from your own structure prediction, say — use [`kind: linegraph`](/protvista/your-data#a-line-graph-of-your-own-values) with a `position,value` file. ## Start from a blank config `extends:` the default UniProt config won’t work: nearly every track in it needs UniProt. Write the rows you want from scratch. If a config does extend it, every inherited track that needs UniProt is listed, followed by a summary saying to start from a blank config instead. ## The error messages Every problem with a `sequence:` config is a config error. It shows in the viewer’s error panel, logs one console line, and fires a `protvista-error` event with `phase: 'config'`, `severity: 'error'` and each problem in `detail.issues` (see [Troubleshoot errors](/protvista/troubleshooting)): | `code` | Message | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `accession-and-sequence` | `This config sets both 'accession:' and 'sequence:'. Use 'accession:' to show a UniProt entry, or 'sequence:' to show your own protein — not both.` When the accession comes from the element’s `accession` attribute, the message says so. | | `needs-accession` | `Track hotspots needs UniProt data: its data URL uses {accession}. With 'sequence:' only file, inline and custom sources work.` The reason may also be `kind 'alphafold-confidence' reads AlphaFold DB data for a UniProt entry` or `its label links to a UniProt-keyed URL ({accession})`. | | `invalid-sequence` | `./my-protein.fasta (parsed as FASTA): contains 2 records; the viewer shows one protein. …` — or an invalid character with its residue position, or no residues at all. An inline value that looks like a file name (`protein.txt`) is told to write it as `./protein.txt`. | | `cannot-resolve-sequence` | `Could not load the sequence file './my-protein.fasta': HTTP 404 Not Found.` | | `missing-protein` | `Nothing to show: set 'accession:' (a UniProt entry) or 'sequence:' (your own protein), or the element's accession attribute.` | The [playground](/protvista/playground/) checks a `sequence:` config as you type, inline sequence included, and disables its accession box while the config sets `sequence:`, itself or through an `extends:` base. ## Try your FASTA in the playground Press **Load data file…** in the [playground](/protvista/playground/) (or drop the file onto the config editor) and pick a one-record FASTA file: `.fasta`, `.fa`, `.faa` or `.fas`, or any other file whose first non-blank line starts with `>` (a `.csv`, `.tsv`, `.json` or `.bed` file is read as data, unless the config’s `sequence:` names it). It is read in your browser and never uploaded. Only its *name* goes into the config — `sequence: ./my-protein.fasta` — so neither the residues nor the header reach a link you share. If the config already names the file, as [`examples/sequence-only/`](https://github.com/ebi-webcomponents/protvista/tree/next/examples/sequence-only) does, it just loads — whatever its name, so a `seq.txt` of raw residues works too. Otherwise you choose where it goes: * **This config** sets its `sequence:` (replacing an inline one) and removes `accession:`, keeping your tracks. Tracks that need UniProt are then listed as `needs-accession` errors. * **A new sequence-only config** replaces the editor text with one that names only your file. Press Ctrl/Cmd+Z in the editor to get the old config back. A file the viewer would reject — two records, a character outside A–Z, no residues — is not loaded, and the playground shows the `invalid-sequence` message a hosted viewer would. A file over 2 MB, the most a hosted viewer fetches, is refused too. Someone who opens a shared link is asked to load the file themselves: until they do, the playground lists `local-file-missing`, and the preview pane, which shows no preview, says which file to load. If your header is a UniProt one (`>sp|P01542|…`), the protein is in UniProt: `accession: P01542` gives you its UniProt tracks as well. ## Your coordinates are still checked Rows that fall outside your sequence — a start below 1, or an end past the last residue — load and render as written, with a `track-data` warning on the console and the `protvista-error` event that names your FASTA header: ```plaintext ./hotspots.csv (parsed as CSV): 1 of 5 rows fall outside my construct v2 (240 residues); first: row 4, end 812. … ``` See [Common coordinate mistakes](/protvista/troubleshooting#common-coordinate-mistakes). ## Offline and air-gapped use With `sequence:` the viewer itself contacts no server: no UniProt, no AlphaFold, no structure lookups. The only requests are for the files your config names — the FASTA file and your data files, fetched relative to the page — so a page and its files on an internal server, or on disk behind a local web server, need no internet access. Inline sequence and inline data need no requests at all. For fully offline use, also host the viewer yourself rather than loading it from a CDN: serve the package’s `dist/` folder, which holds the component and the lazily loaded YAML parser chunk it fetches for a YAML config. ## Limits These are out of scope for `sequence:`: * isoforms; * more than one sequence in a viewer; * structures: no prediction, and no structure panel, for a custom sequence; * mapping your sequence to a UniProt entry automatically; * nucleotide sequences — residues are protein one-letter codes. *Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).* # Troubleshoot errors > Explaining the 'protvista-error' event and what each detail and phase means, describing common problems and solutions. When something doesn’t load, ProtVista tells you in three places: on screen (an alert panel or a ⚠ badge), in the browser console, and through a single `protvista-error` event you can listen for. This page covers all three, plus the most common causes. The full list of events the viewer dispatches, including `protvista-error`’s neighbours `protvista-event` and `protvista-layout-change`, is on the [Events](/protvista/events) page. Start with the messages on screen. If the whole viewer cannot load, read the alert panel. If only one track fails, check the ⚠ badge on that track. In the playground, also check the listed errors and warnings. These messages can help you identify the problem before using the browser console or writing an event listener. See [Where a failure shows up](#where-a-failure-shows-up) for details. ## The `protvista-error` event The element emits one bubbling `protvista-error` event for every problem, so a single listener covers all of them. Switch on `detail.phase`: ```js const viewer = document.querySelector('protvista-uniprot'); viewer.addEventListener('protvista-error', (event) => { const { phase, severity, message, source, context } = event.detail; // `message` is the text the badge or panel shows, so the simplest useful // listener is one line and needs no `switch` at all. console.warn(`[protvista] ${phase}: ${message}`, source ?? ''); if (phase === 'config' && severity === 'error') { console.error('The config was rejected:', event.detail.issues); } else if (phase === 'track-fetch') { // `trackId` is absent when a collapsed group's combined view failed. const where = context.trackId ? `${context.groupId}/${context.trackId}` : context.groupId; console.warn(`Track ${where} failed`); } }); ``` ### `detail` | Field | What it holds | | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `phase` | Which part of the pipeline failed — see the table below. | | `severity` | `'error'` or `'warning'` — see [Where a failure shows up](#where-a-failure-shows-up). A warning names something that loaded, but not as written: a config warning, a `theme:` colour that could not be resolved, a component with no renderer. | | `message` | One line: the text the `⚠` badge or the alert panel shows, without the `[protvista]` tag the console line starts with. For a malformed file or a track that could not draw its data, this is what the decoder or the component threw, naming your file and the offending row. | | `source` | The URL or path the failure came from, when it had one. Absent for an inline, `custom` or `setTrackData()` source. | | `issues` | For `config`, the `ValidationIssue[]`. For `track-data` and `tooltip-field-miss`, one issue with `severity: 'warning'` whose `code` says what was found and whose `path` names the track. `[]` otherwise. | | `context` | Identifiers for the failure — see [The `context` object](#the-context-object). | ### Phases | `phase` | Fires when | Useful `context` | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | `config` | The config fails to parse or validate, or it loads with warnings. `detail.severity` says which. `detail.issues` lists what’s wrong when the validator raised it, and a warning’s issue has `severity: 'warning'`; a warning raised after validation (an unresolvable `theme:` colour, a component with no renderer) has empty `issues`. | — | | `sequence` | No usable sequence was found for the accession. Never fires for a [`sequence:`](/protvista/sequence-only) config, which fetches no entry; a problem with its sequence is a `config` error. | `accession`, plus (on a fetch failure) `errorKind` / `status` / `url` | | `track-fetch` | A track’s data failed in a way that breaks it — a network error, a 5xx response, an unparseable body, a malformed file the decoder rejected, a 4xx on a path or URL to your own data, or a payload the track could not draw. A 4xx from a *provider endpoint* is treated as “missing, not broken” and does *not* fire this event. | `groupId`, `trackId`, `url`, `status`, `errorKind` | | `set-track-data` | Misuse of the `setTrackData()` programmatic API. | `groupId`, `trackId` | | `track-data` | A bring-your-own-data track loaded and renders as written, but something in its data is worth knowing: rows whose coordinates fall outside the protein’s sequence (the UniProt entry’s, or your `sequence:`) — a start below 1, or a position past the last residue — or, for a feature file, columns that were ignored (`tooltipContent`, `locations`, `residuesToHighlight`, names like `toString`), `shape` values that were ignored because they name a JavaScript built-in (`valueOf`, `constructor`, …), or `color` / `fill` values the canvas cannot paint. `detail.issues` holds one issue with `severity: 'warning'` and `code` set to `coordinate-out-of-range`, `data-field-ignored` or `unpaintable-color`. | `groupId`, `trackId`, `url` (file and URL tracks) | | `tooltip-field-miss` | A track’s authored `dataTooltip` references a field that none of the track’s records carries, so that part of every tooltip is blank. The track still renders. Fires once per track per load, listing every such field. `detail.issues` holds one issue with `code: 'tooltip-field-miss'` and `severity: 'warning'`. | `groupId`, `trackId`, `fields` | ### Where a failure shows up Every failure in the viewer — a config that won’t validate, a sequence that won’t load, a file that won’t parse — is described by two facts and routed by them: how **severe** it is, and whether it is scoped to the whole **viewer** or to one **track**. A row may also name a **phase**, and a phase row may be narrowed again by a **code** saying what was found: for `track-data`, the `code` of the event’s issue; on the viewer, a URL template left unfetched (`url-variable-unresolved`), a component with no renderer (`unrendered-component`) or a `theme:` colour that didn’t resolve (`theme-color-ignored`). The table below is the whole rule. It lives as data in `src/errors/router.ts` and is drift-tested against this page, so the two cannot disagree. | Severity | Scope | Phase | Code | Console | `protvista-error` | Alert panel | Row badge | Visitor notice | Author mode | | --------- | ------ | -------------------- | ------------------------- | ------- | ----------------- | -------------- | --------- | -------------- | ----------- | | `error` | viewer | any | any | yes | yes | always | — | no | yes | | `error` | track | any | any | yes | yes | under `strict` | yes | no | yes | | `warning` | viewer | `set-track-data` | any | yes | yes | under `strict` | — | no | yes | | `warning` | viewer | `config` | `unrendered-component` | yes | yes | never | — | viewer | yes | | `warning` | viewer | `config` | `theme-color-ignored` | yes | yes | never | — | no | yes | | `warning` | viewer | `track-fetch` | `url-variable-unresolved` | yes | yes | never | — | viewer | yes | | `warning` | viewer | any | any | yes | yes | never | — | no | yes | | `warning` | track | `track-data` | `coordinate-out-of-range` | yes | yes | never | no | track | yes | | `warning` | track | `track-data` | `unpaintable-color` | yes | yes | never | no | track | yes | | `warning` | track | `track-data` | `data-field-ignored` | yes | yes | never | no | no | yes | | `warning` | track | `track-data` | any | yes | yes | never | no | no | yes | | `warning` | track | `tooltip-field-miss` | any | yes | yes | never | no | no | yes | | `warning` | track | any | any | yes | yes | under `strict` | yes | no | yes | | `info` | viewer | any | any | yes | no | never | — | no | no | | `info` | track | any | any | yes | no | never | no | no | no | Reading it: * A **viewer-scoped error** leaves nothing to render past it — no config, or no sequence — so the panel replaces the viewer whether or not `strict` is set. * A **track-scoped** failure leaves the rest of the viewer working, so the row carries it as a `⚠` badge. `strict` promotes the batch to a single aggregated panel; without `strict` the badges stand alone. * A **viewer-scoped warning** names something legal that loaded as written — a config warning, or a `theme:` field that could not be resolved. It never raises the panel, even under `strict`: hiding a working viewer behind a notice about something that worked is not a louder failure, just a less useful one. Two of them change what is on screen, so they also carry a **visitor notice** on the viewer: a track whose data URL was never fetched (an undefined `{variable}`), and a component with no renderer. A `theme:` colour that didn’t resolve leaves the default colours in place and nothing missing, so it has none. * The one exception is a rejected **`setTrackData()`** call, which `strict` does promote. The rows above are read most-specific-first, so a row naming a `phase` wins over the `any` row for the same severity and scope. A config warning describes something that happened as written; an ignored API call describes something the caller asked for that did not happen, which is what `strict` exists to make loud. * A **`track-data`** warning — rows whose coordinates fall outside the sequence, a column of your file that was ignored, a colour the canvas cannot paint — goes the other way for a track. The rows loaded and render as written, so it takes neither the `⚠` badge nor the panel: a badge would mark a working row as broken. Where the visitor would otherwise be misled — features not drawn in full, colours not painted — the track gets a quieter **visitor notice** instead. An ignored column changes nothing drawn, so it has none. * A **`tooltip-field-miss`** warning is the same for the same reason: every record renders, and only the track’s tooltip template names a field the data never has. No badge and no panel; the event and the console line carry it. * **`info`** is an expected absence, not a failure: a provider endpoint answering 404 for an entity with no data of this kind, or a `from: custom` track nobody injected data into. It gets a console line and no user surface. * **Author mode** lists everything the `protvista-error` event carries — every error and every warning, never `info` — so its column is the event’s. See [What visitors and authors see](#what-visitors-and-authors-see). A **Retry** control appears on whichever surface carried the failure, and only when retrying could plausibly change the answer: a network error or a 5xx may be transient, and a path or URL to your own data that 404s is something *you* can fix — correct it or drop the file into place, then Retry reloads that one track instead of the whole page. A built-in provider adapter that throws gets a Retry too: some make requests of their own (the AlphaFold confidence and AlphaMissense tracks each fetch a second file), so their failures can be as temporary as a 5xx. A malformed file, a malformed `setTrackData()` payload, an unregistered adapter name and a payload the track could not draw all get no Retry: they would fail the same way again with no action available in between. ### What visitors and authors see The console and the `protvista-error` event reach you; a visitor to a page that embeds the viewer sees neither. So the viewer follows one rule: **tell visitors when what they’re looking at is incomplete or misleading, and tell authors everything.** **Visitor notices.** A warning whose row in the table above has a visitor notice puts a quiet ⓘ on the affected track’s label. It opens a short, plain-language note — “2 features extend beyond this sequence, so they aren’t shown in full.”, “Some colours in the data couldn’t be shown, so some features may be in the wrong colour.” — that never names a file, a URL, a field or a variable. A viewer-level notice (““Partner data” isn’t shown: its data couldn’t be loaded.”, ““Mine” can’t be displayed in this viewer.”) goes on an ⓘ in the top bar, beside **Customize**. So does a track notice whose label isn’t on screen — a track inside a collapsed group, a track with no data to draw, a customize-mode placeholder, or the “No feature data available” view — prefixed with the track’s name. A track you hid from the layout tells visitors nothing until Customize shows it again. The notes are announced once to screen readers, politely, and the ⓘ is a keyboard-reachable button: Escape, a click elsewhere or moving focus on closes its note. A note too long for the room beside its button scrolls. Visitor notices are on by default. The **`quiet-notices`** attribute turns them off: ```html ``` The `protvista-error` event is unchanged either way, so an embedder that wants its own UI can keep listening to it. **Author mode.** The **`show-warnings`** attribute, or `showWarnings: true` in the config, turns author mode on; either one is enough. It is off by default, so visitors never see authoring notes. Each track with a warning, and the top bar, then carries a muted ⚠ with a count instead of the ⓘ. Its list holds every warning in full: the same text the console, the event and the playground’s diagnostics show, with its phase, code and config path, the file or URL it came from, and the note visitors see for it (unless `quiet-notices` is set). A rejected `setTrackData()` call made three times is listed once, marked ×3. A red `⚠` badge works like the ⓘ: select it and a note opens with what went wrong — the same text the console and the event carry, such as “./hits.csv could not be found (HTTP 404) — check the path is relative to the page.” Unlike a notice, an error says which file or URL failed, because the person reading it is often the one who can fix it. Retry stays beside it. Author mode shows errors in full too. A track’s red `⚠` badge lists the error’s whole text and its source, then the track’s warnings. An error on a track whose label isn’t drawn — inside a collapsed group, which shows only the group’s count badge — is listed beside Customize under the track’s name. The alert panel adds the console’s text under its summary. Setting or removing either attribute on a live element takes effect at once, with no reload. In the [playground](/protvista/playground/), the preview keeps visitor notices on and author mode off, so it shows what your visitors will see; the diagnostics list beside the editor is the author view there. `showWarnings: true` in the config turns author mode on in the preview too, but it names a loaded file by the `blob:` URL the preview fetched, where the diagnostics list says `./name`. ### The `context` object Every field is optional; the reporter fills in what’s relevant to the phase. `accession` is always set when the element has one. For `track-fetch`, `errorKind` is one of: * `network` — unreachable (offline, blocked, DNS, CORS, or timeout); * `http` — the server answered with a 5xx, or a path or URL to your own data answered 4xx (`status` is set). A 4xx from a provider endpoint is treated as “missing, not broken”, so it does not fire a `track-fetch` event; * `parse` — a successful response whose body couldn’t be parsed; * `adapter` — the body arrived, but the decoder, the shape validator or the named adapter threw on it. For your own file, the badge and the event carry the thrown message verbatim, which names your file and the offending row; for a provider adapter, they name the URL the data came from; * `render` — the payload was built, and the track itself rejected it on handover (a line graph handed feature records, say). `trackId` is absent when the rejected payload was a collapsed group’s combined view rather than one track’s. ## Common problems ### Nothing renders at all The viewer needs a protein: an `accession` (the attribute, or `accession:` in the config) for a UniProt entry, or a [`sequence:`](/protvista/sequence-only) for your own. With neither, it shows the config panel with one issue, `missing-protein`: “Nothing to show: set ‘accession:’ (a UniProt entry) or ‘sequence:’ (your own protein), or the element’s accession attribute.” Set one of them. A wrong accession shows the “No UniProt entry found” panel instead. While the first load is in flight you see a spinner, and screen readers hear “Loading protein data…” — so a region that stays *blank* is not a slow load. Check that `suspend` is not still on the element. **Opened the page by double-clicking it?** A page opened from disk (a `file://` address) can’t load its config or any data file: browsers block it from reading other local files. Serve the folder with a local web server instead and open `http://localhost:8000/`. On Windows run `py -m http.server 8000 --bind 127.0.0.1` in that folder (or `python` if `py` isn’t found); on macOS or Linux, `python3 -m http.server 8000 --bind 127.0.0.1`. The [Starter Kit](https://github.com/ebi-webcomponents/protvista-starter-kit) detects this case and says so on the page. **Self-hosting `dist/` with Python on Windows?** Python’s server takes file types from the Windows registry, which on some machines maps `.mjs` or `.js` to `text/plain`. The browser then refuses to run the module, and the console says it was blocked because of a disallowed MIME type. Load the component from the CDN instead (see [Embed the viewer](/protvista/embed)), or use a different static server such as `npx serve`. ### A track shows up empty Almost always a **path** issue with a file-backed track. `data: ./hotspots.csv` is resolved relative to the **hosting page**, not the config file — so the browser may be looking in the wrong place. Serve the page from the same directory as the data, or use an absolute URL. See the path note in [Load your own data](/protvista/your-data#a-path-gotcha-to-know). The viewer says so on screen. A relative path that 404s shows a `⚠` badge reading “`./hotspots.csv` could not be found (HTTP 404) — check the path is relative to the page.” An absolute URL, or the `{ url: … }` form, that 404s reads “… could not be found (HTTP 404) — check the URL.” A JSON file whose top level is not an array — `{ "features": [...] }` rather than `[...]` — shows a badge too. So a track that stays empty with no badge really did load and really is empty. ### A track is empty but its data looks right If `errorKind` is `render`, the data loaded and parsed — the track just could not draw it. That is a shape mismatch rather than a content problem: a line graph handed feature records, or a `setTrackData()` payload that does not match the record contract for the track’s `kind`. The badge carries what the component threw, and the other tracks are unaffected. See [Load your own data](/protvista/your-data) for the shape each kind expects. ### A data file is malformed A file that loads but can’t be decoded shows a `⚠` badge carrying the decoder’s own message — `./hits.csv (parsed as CSV): row 3, column "start": expected a number, got "abc"` — and fires a `track-fetch` event with `errorKind: 'adapter'` and the identical text. The rest of the viewer keeps working; only that one track degrades. No Retry is offered, because re-running the same decoder over the same bytes gives the same answer: fix the file. To check a file before you host it, load it in the [playground](/protvista/playground/) with **Load data file…**. The same message is listed there, naming your file. When the header is the problem because the file uses a different separator from the one its format implies — a semicolon export from Excel, a tab file named `.csv`, a comma file named `.tsv` — the message names the separator the header seems to use and the fix: ```plaintext ./hits.csv (parsed as CSV): missing required header column "type". Header must contain type, start, end, description[, score]. The header looks tab-separated — read it as TSV: set `format: tsv` (or rename the file to .tsv). ``` `format:` always works, because it wins over the extension; a rename is offered only when the file’s extension matches the format it was read as (it does not help if an explicit `format:` also pins that reading). No `format:` reads semicolons, so for those save the sheet from Excel as “Text (Tab delimited)” and read it as TSV, or re-export it comma-separated and read it as CSV. The viewer never switches separator by itself: only the message changes. ### A tooltip is missing rows or shows blanks A field a tooltip names that a record does not have renders as nothing: a `fields` row drops out, and a `{% $field %}` in a template renders empty. That is normal for a field only some records carry. When **no** record on the track has it, the name is almost always wrong — a typo, a different capitalisation, or a column your file calls something else — so the console gets one line per track naming every such field: ```plaintext [protvista-uniprot] Track domains/hits: dataTooltip references unknown fields: pvalue, Gene ``` The same text fires a `tooltip-field-miss` event (`context.fields` lists the names), and the [playground](/protvista/playground/) lists it as a warning. The names a template can use are the record’s own: the provider adapter’s output, or the column headers of your file — see [Fields from your own file](/protvista/data-tooltip#fields-from-your-own-file). A field that is present but empty — a blank cell in a column of your own — is not missing; see [When a field is missing](/protvista/data-tooltip#when-a-field-is-missing) for the few columns where it is. A record that has none of the fields a template names, and for which the template renders only punctuation (the `·` of `{% $gene %} · {% $url %}`) or nothing, shows the track’s default tooltip instead, with no warning of its own: see [A record with none of the fields](/protvista/data-tooltip#a-record-with-none-of-the-fields). To drop the text around a field only some records have, wrap it in `{% if %}`: see [Guard a field some records lack](/protvista/data-tooltip#guard-a-field-some-records-lack). ### Common coordinate mistakes ProtVista expects 1-based, inclusive positions on the entry’s canonical sequence. Three mistakes account for most wrong-looking tracks: * **0-based coordinates** (BED habits, Python ranges). A `start` of 0 is the giveaway, and every feature is shifted by one. ProtVista reports this as a `track-data` warning but never shifts your data for you. BED files are the exception: they are 0-based by definition and converted automatically. * **Isoform numbering.** Positions from another isoform can run past the canonical sequence’s last residue. This is also reported as a `track-data` warning, naming the first row that falls outside. * **Inverted intervals** (`end` before `start`) and fractional coordinates (`18.5`). The file is rejected when it loads, as a [malformed file](#a-data-file-is-malformed): the track renders empty and its `⚠` badge names the row, for example `./x.csv (parsed as CSV): row 3: end (4) is before start (5).` A `track-data` warning reads like this: ```plaintext ./hits.csv (parsed as CSV): 12 of 340 rows fall outside P05067 (770 residues); first: row 7, end 812. Coordinates must be 1-based positions on this protein's canonical sequence — check for 0-based coordinates (start 0) or isoform numbering. ``` With a [`sequence:`](/protvista/sequence-only) config the check runs against your sequence, and the message names its FASTA header (or “your sequence”) where the accession would be: `… fall outside my construct v2 (240 residues) …`. ### Features are all black, or show a ? A feature whose `type` ProtVista doesn’t recognise — `HOTSPOT`, or `BED` for every record in a `.bed` file — draws as a black rectangle, with nothing but a console log to say why. A `shape` the canvas track can’t draw, misspelt or not, draws as a question mark. Set `rendering.color` and `rendering.shape` on the track; [Feature type and shape vocabulary](/protvista/type-and-shape-vocabulary) lists the recognised types and draws every valid shape. ### The config is rejected A `config`-phase error carries `detail.issues` describing each problem. Validate your config as you write it in the [playground](/protvista/playground/) (and check your data files there with **Load data file…**), or point your editor at the schema for inline checking — see [Author a config](/protvista/configure#editor-autocomplete). ### “needs UniProt data”, or “both ‘accession:’ and ‘sequence:’” These come from a [`sequence:`](/protvista/sequence-only) config, which shows your own protein with no UniProt entry behind it. * `needs-accession` — a track can’t work without a UniProt entry: a data URL with `{accession}`, an AlphaFold or AlphaMissense kind, or a label that links to an `{accession}` URL. Point the track at your own file, inline data or a `from: custom` source, or remove it. A config that `extends:` the UniProt default gets one of these per inherited track, plus a summary: start from a blank config instead. * `accession-and-sequence` — the config sets both, or the element’s `accession` attribute is set on a `sequence:` config. Keep one. * `invalid-sequence` / `cannot-resolve-sequence` — the sequence itself is not one protein (several FASTA records, a character outside A–Z, no residues), or the FASTA file could not be fetched. The message names the file and, for a bad residue, its position. ### A URL track fails to load Look at `errorKind`: `network` usually means CORS or connectivity (the data server must allow cross-origin requests from your page); `http` with a `status` means the server returned a 5xx; `parse` means the body could not be parsed at all, and the message carries the parser’s own complaint (where in the body it gave up); `adapter` means the decoder rejected the records themselves. Confirm the URL in a browser tab, and check it returns the [shape the adapter expects](/protvista/adapter-reference). (A 4xx such as 404 from a *provider endpoint* is treated as “no data for this track” and is hidden rather than reported as an error. From a `from: file` source — with a format or an explicit `adapter:` — or any URL with a declared `format`, it is reported: there a 404 can only mean the path or URL is wrong.) ## Where to go next * [Load your own data](/protvista/your-data) — data shapes and the path gotcha. * [Escape hatches](/protvista/escape-hatches) — custom adapters and error handling. *Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).* # Feature type and shape vocabulary Every feature record has a `type` (see [Load your own data](/protvista/your-data)). The viewer uses it to pick the feature’s default colour and shape from a table built into Nightingale, the library that draws the tracks. This page lists that table as shipped in `@nightingale-elements/nightingale-track` 5.11.1, the version the viewer draws with. It is generated from that package and kept in sync by drift tests, so it cannot silently diverge. ## How a feature gets its colour and shape The first of these that applies wins: 1. A colour or shape carried on the feature record itself. Some built-in kinds set one (InterPro domains get a colour per entry; PTMeXchange peptides are triangles); records from your own CSV, TSV or JSON file can set one with a `color` / `shape` column (or key); BED files cannot. 2. The track’s `rendering.color` and `rendering.shape`, set on the track or inherited from its group or from `defaults:`. These apply to **every** feature in the track, whatever its type. 3. The default for the feature’s `type`, from [the table below](#recognised-types). 4. `black` and `rectangle`. Colour and shape are resolved separately, so a track can set one and keep the other. `type` is matched without regard to case, against either the type code (`ACT_SITE`) or the UniProt name (`active site`) listed in the table. ## Types ProtVista does not recognise A `type` outside the table is drawn as a black rectangle. The viewer shows no error or warning; Nightingale only logs the type name to the browser console. Every unrecognised type looks the same, so a track that mixes two of them cannot tell them apart. BED files have no type column, so every record read from a `.bed` file has `type: BED` and is drawn as a black rectangle unless its track sets `rendering`. ### Worked example: a custom type A lab marks its own hotspots with a `type` UniProt does not use: ```csv type,start,end,description,score HOTSPOT,132,140,Lab-defined mutational hotspot,0.87 HOTSPOT,601,612,Lab-defined mutational hotspot,0.64 ``` On its own, each row draws as a black rectangle. Give the track a `rendering` block to choose the look instead: ```yaml accession: P05067 rows: - id: hotspots label: Hotspots kind: features data: ./hotspots.csv rendering: color: '#e67e22' shape: roundRectangle ``` Because `rendering` applies to the whole track, it also overrides the defaults of any recognised types in the same file. Keep custom types in their own file and track so the recognised ones keep their colours. ## Recognised types 45 type codes, in the order Nightingale’s [type table](https://github.com/ebi-webcomponents/nightingale/blob/main/packages/nightingale-track/src/config.ts) lists them. | Type | UniProt name | Label | Default shape | Default colour | Meaning | | ------------ | ------------------------------------- | ----------------------------- | ------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CHAIN` | chain | Chain | `rectangle` | `#CC9933` | (aka mature region). This describes the extent of a polypeptide chain in the mature protein following processing | | `TRANSIT` | transit peptide | Transit peptide | `rectangle` | `#009966` | This describes the extent of a transit peptide | | `INIT_MET` | initiator methionine | Initiator methionine | `arrow` | `#996633` | This indicates that the initiator methionine is cleaved from the mature protein | | `PROPEP` | propeptide | Propeptide | `rectangle` | `#99CCCC` | Part of a protein that is cleaved during maturation or activation | | `PEPTIDE` | peptide | Peptide | `rectangle` | `#006699` | The position and length of an active peptide in the mature protein | | `SIGNAL` | signal peptide | Signal peptide | `rectangle` | `#CC0033` | N-terminal signal peptide | | `HELIX` | helix | Helix | `rectangle` | `#FF0066` | The positions of experimentally determined helical regions | | `STRAND` | strand | Beta strand | `rectangle` | `#FFCC00` | The positions of experimentally determined beta strands | | `TURN` | turn | Turn | `rectangle` | `#0571AF` | The positions of experimentally determined hydrogen-bonded turns | | `DISULFID` | disulfide bond | Disulfide bond | `bridge` | `#23B14D` | The positions of cysteine residues participating in disulphide bonds | | `CROSSLNK` | cross-link | Cross-link | `bridge` | `#FF6600` | Covalent linkages of various types formed between two proteins or between two parts of the same protein | | `REGION` | region of interest | Region of interest | `rectangle` | `#B33E00` | Regions in multifunctional enzymes or fusion proteins, or characteristics of a region, e.g., protein-protein interactions mediation | | `COILED` | coiled-coil region | Coiled-coil region | `rectangle` | `#006699` | Coiled coils are built by two or more alpha-helices that wind around each other to form a supercoil | | `MOTIF` | short sequence motif | Short sequence motif | `rectangle` | `#402060` | Short conserved sequence motif of biological significance | | `REPEAT` | repeat | Repeat | `rectangle` | `#9900FF` | Repeated sequence motifs or repeated domains within the protein | | `CA_BIND` | calcium-binding region | Calcium-binding region | `rectangle` | `#FF3399` | Calcium-binding regions, such as the EF-hand motif | | `DNA_BIND` | DNA-binding region | DNA binding | `rectangle` | `#009933` | DNA-binding domains such as AP2/ERF domain, the ETS domain, the Fork-Head domain, the HMG box and the Myb domain | | `DOMAIN` | other domain of interest | Other domain of interest | `rectangle` | `#9999FF` | Specific combination of secondary structures organized into a characteristic three-dimensional structure or fold | | `ZN_FING` | zinc finger region | Zinc finger | `rectangle` | `#990066` | Small, functional, independently folded domain that coordinates one or more zinc ions | | `NP_BIND` | nucleotide-binding region | Nucleotide-binding region | `rectangle` | `#FF9900` | (aka flavin-binding). Region in the protein which binds nucleotide phosphates | | `METAL` | metal ion-binding site | Metal ion-binding site | `diamond` | `#009900` | Binding site for a metal ion | | `SITE` | other site of interest | Other site of interest | `chevron` | `#660033` | Any interesting single amino acid site on the sequence | | `BINDING` | other binding site | Other binding site | `rectangle` | `#009999` | Binding site for any chemical group (co-enzyme, prosthetic group, etc.) | | `ACT_SITE` | active site | Active site | `circle` | `#FF6666` | Amino acid(s) directly involved in the activity of an enzyme | | `MOD_RES` | Post-translationally modified residue | Modified residue | `triangle` | `#000066` | Modified residues such as phosphorylation, acetylation, acylation, methylation | | `LIPID` | lipid moiety-binding region | Lipidation | `wave` | `#99CC33` | Covalently attached lipid group(s) | | `CARBOHYD` | glycosylation site | Glycosylation | `hexagon` | `#CC3366` | Covalently attached glycan group(s) | | `COMPBIAS` | compositionally biased region | Compositionally biased region | `rectangle` | `#FF3366` | Position of regions of compositional bias within the protein and the particular amino acids that are over-represented within those regions | | `CONFLICT` | sequence conflict | Sequence conflict | `rectangle` | `#6633CC` | Sequence discrepancies of unknown origin | | `NON_CONS` | non-consecutive residues | Non-adjacent residues | `doubleBar` | `#FF0033` | Indicates that two residues in a sequence are not consecutive and that there is an undetermined number of unsequenced residues between them | | `NON_TER` | non-terminal residue | Non-terminal residue | `doubleBar` | `#339933` | The sequence is incomplete. The residue is not the terminal residue of the complete protein | | `UNSURE` | unsure residue | Sequence uncertainty | `rectangle` | `#33FF00` | Regions of a sequence for which the authors are unsure about the sequence assignment | | `NON_STD` | non-standard amino acid | Non-standard residue | `pentagon` | `#330066` | Non-standard amino acids (selenocysteine and pyrrolysine) | | `MUTAGEN` | mutagenesis site | Mutagenesis | `rectangle` | `#FF9900` | Site which has been experimentally altered by mutagenesis | | `TOPO_DOM` | topological domain | Topological domain | `rectangle` | `#CC0000` | Location of non-membrane regions of membrane-spanning proteins | | `TRANSMEM` | transmembrane region | Transmembrane | `rectangle` | `#CC00CC` | Extent of a membrane-spanning region | | `INTRAMEM` | intramembrane region | Intramembrane | `rectangle` | `#0000CC` | Extent of a region located in a membrane without crossing it | | `VAR_SEQ` | splice variant | Splice variant | `rectangle` | `#CC11CC` | — | | `VARIANT` | sequence variant | Natural variant | `circle` | `black` | Natural variant of the protein, including polymorphisms, variations between strains, isolates or cultivars, disease-associated mutations and RNA editing events | | `UNIQUE` | unique | Unique peptide | `rectangle` | `#fc3133` | — | | `NON_UNIQUE` | non-unique | Non-unique peptide | `rectangle` | `#8585fc` | — | | `RESIDUE` | residue | PTM residue | `rectangle` | `#06038D` | — | | `EPITOPE` | epitope | Epitopes | `rectangle` | `#4ade33` | — | | `ANTIGEN` | antigen | Antibody binding sequences | `rectangle` | `#996699` | — | | `PDBE_COVER` | pdbe coverage | PDBe 3D structure coverage | `rectangle` | `#669966` | — | ## Shapes `rendering.shape` takes one of these 22 names. The spellings (`discontinuos`) are Nightingale’s. Each is drawn below by the canvas track’s own drawing code: shapes that stretch across several residues, glyphs on one. 3 of them (`leftEndedTag`, `rightEndedTag`, `doubleEndedTag`) are declared but not drawn by the canvas track, and show a question mark instead. So does any other name: the config accepts any string for `shape`, and a misspelt one only shows up as a one-time warning naming it in the browser console. | Looks like | Shape | Drawn as | Default for | | ---------- | ------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | `rectangle` | Stretches to cover the feature | `CHAIN`, `TRANSIT`, `PROPEP`, `PEPTIDE`, `SIGNAL`, `HELIX`, `STRAND`, `TURN`, `REGION`, `COILED`, `MOTIF`, `REPEAT`, `CA_BIND`, `DNA_BIND`, `DOMAIN`, `ZN_FING`, `NP_BIND`, `BINDING`, `COMPBIAS`, `CONFLICT`, `UNSURE`, `MUTAGEN`, `TOPO_DOM`, `TRANSMEM`, `INTRAMEM`, `VAR_SEQ`, `UNIQUE`, `NON_UNIQUE`, `RESIDUE`, `EPITOPE`, `ANTIGEN`, `PDBE_COVER` | | | `roundRectangle` | Stretches to cover the feature | — | | | `bridge` | Stretches to cover the feature | `DISULFID`, `CROSSLNK` | | | `line` | Stretches to cover the feature | — | | | `diamond` | Fixed-size glyph; across more than one residue a line joins its ends | `METAL` | | | `chevron` | Fixed-size glyph; across more than one residue a line joins its ends | `SITE` | | | `catFace` | Fixed-size glyph; across more than one residue a line joins its ends | — | | | `triangle` | Fixed-size glyph; across more than one residue a line joins its ends | `MOD_RES` | | | `wave` | Fixed-size glyph; across more than one residue a line joins its ends | `LIPID` | | | `hexagon` | Fixed-size glyph; across more than one residue a line joins its ends | `CARBOHYD` | | | `pentagon` | Fixed-size glyph; across more than one residue a line joins its ends | `NON_STD` | | | `circle` | Fixed-size glyph; across more than one residue a line joins its ends | `ACT_SITE`, `VARIANT` | | | `arrow` | Fixed-size glyph; across more than one residue a line joins its ends | `INIT_MET` | | | `doubleBar` | Fixed-size glyph; across more than one residue a line joins its ends | `NON_CONS`, `NON_TER` | | | `discontinuosStart` | Stretches to cover the feature | — | | | `discontinuos` | Stretches to cover the feature | — | | | `discontinuosEnd` | Stretches to cover the feature | — | | | `helix` | Stretches to cover the feature | — | | | `strand` | Stretches to cover the feature | — | | | `leftEndedTag` | Not drawn — shows a question mark | — | | | `rightEndedTag` | Not drawn — shows a question mark | — | | | `doubleEndedTag` | Not drawn — shows a question mark | — |