npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@design.estate/dees-catalog

v23.0.0

Published

A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.

Readme

@design.estate/dees-catalog

Lit and TypeScript web components for application shells, forms, tables, charts, storage, media, and agent workspaces. Shared theme tokens and controls keep these surfaces consistent.

Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.

Find the right component

Start with the task below, then open the component's API and source in the component index. Search by both the UI pattern and the element tag before implementing a custom control. For example, segmented control, view switcher, view-toggle, and single-selection toggle all lead to dees-input-multitoggle.

| Task or UI pattern | Existing component | Selection guidance | | --- | --- | --- | | Switch between a few views, modes, or time periods; segmented control | dees-input-multitoggle | One selected value in a shared track. Use keyed options when labels differ from stored values. | | Choose one form answer from visible radio options | dees-input-radiogroup | Radio indicators, required validation, optional payload values. | | Turn one setting on or off | dees-input-toggle, dees-input-checkbox | Boolean setting or checkable form choice. Multitoggle's boolean mode is useful when both alternatives need explicit labels. | | Toggle an independent toolbar option | dees-button with pressed, dees-input-toggle | A toolbar or app-bar command that stays on uses a button with pressed; a labelled setting uses the switch. A set of independent options is not a single-selection multitoggle. | | Choose from a longer menu of values | dees-input-dropdown | Searchable single selection; enableSearch defaults to true. | | Group related commands | dees-button-group, dees-actionbar | Layout for actions. A button group does not own a selected value. | | Navigate open content panels, with optional close controls | dees-appui-tabs | Tab selection, close actions and keyboard focus. Use multitoggle for changing a display mode within a panel. | | Build an application shell with views and menus | dees-appui | Main/secondary navigation, content tabs, bars and view lifecycle. Routes by hash, or by path beside the host's server routes. | | Give a view its page header: title, summary, actions, tabs | dees-appui-page | One max-width and padding for every view; the primary action at the end of the title row. Do not hand-code headers. | | Head a view with its title, summary and actions above content that scrolls on its own | dees-appui-contentheading, or contentHeading in the view definition | A title with its badges, a line of facts joined with " · ", the view's tools and actions at the end of the row and a hairline across the content area; it stays while the view scrolls. dees-appui-page draws its header with it. | | Report a page's warnings and errors from its header instead of stacking hints above its content | dees-statusbutton | One button beside the page's actions shows the worst status and a count; its speech-bubble panel lists the statuses, and an error or a severe warning opens it by itself. Keep dees-hint for a note next to the thing it explains. | | Say that a tile, panel or list is loading, empty or failed | dees-state | One pattern for all three: icon or spinner, sentence, the way on, Retry and folded technical detail. A dees-table says the same through loading, error and 'empty' actions. | | Show, paste or share a link to the current app screen; address/location bar | dees-appui-location | Clickable ancestors, URL editing and copy feedback. Enable it in App UI; route state belongs to the shell. | | Reach any screen, menu item or host command by name | dees-appui-commandpalette | The shell opens it with the platform modifier and K and with the app bar's search button. Host commands come from IAppConfig.commands / setCommands(). | | Build a simpler grouped dashboard | dees-simple-appdash | Grouped dashboard navigation without configuring the full App UI shell. | | Offer sign-in methods and reveal an authenticated app | dees-simple-login | Passkeys, providers and password form; the application owns authentication. | | Show products with quantities or select a kit | dees-shopping-productcard | Independent quantity and selection events; the application owns the cart and inventory. | | See components composed into one working application | Integrated App UI demo | Shared state, layout presets, navigation, service tabs, charts, files, settings and activity. Start here before assembling a shell. | | Collect and submit form values | dees-form, dees-form-submit | Give each input a stable key; use the form's collection and validation flow. | | Choose, photograph or drop files, and show each file's upload | dees-input-fileupload, dees-dropzone-overlay | The field selects files (capture opens a phone camera) and shows the upload state the host reports with setFileState(); the overlay receives files dropped anywhere in the window. | | Edit multiline plain text | dees-input-textarea | Notes and descriptions as strings, with validation and form reset; no rich-text editor. | | Track epics, tasks and subtasks across workflow columns | dees-kanban | Board, ordering, projects, filters, ticket detail with links, source and history, discussion, live refresh and stable ticket references. | | Show a conversation about a record: comments by people and agents, what happened, a reply | dees-thread | Timeline of comments and events, Markdown composer with close/reopen actions, pending and refused posts, read-only and failed states. The host owns posting and adds the posted entry. | | Show who wrote, did or owns something: a picture, else initials; an agent apart from a person | dees-avatar | Round for a person, a rounded square with its own glyph for an agent or the system; a picture that fails falls back to the initials. decorative beside a visible name. | | Compose comments with searchable inline references | dees-input-richtext searchReferences, dees-input-reference | In Markdown, # at the start of a word searches the host's records at the caret and links the one chosen; dees-input-reference writes plain text with references, at #_. Both keep stable reference IDs. | | Search, filter, sort, edit and act on records | dees-table | Use its columns, selection and data actions before building a table toolbar or row-action layer. | | Offer an action about what a tile shows: open its details, export it, add to it | dees-tile actions | Footer or header actions, drawn and held while they run exactly like a table's header and footer actions. Never a footer bar of buttons built by hand; actions on a table's rows or on the table itself are the table's own. See Where actions go. | | Filter a view's rows: an account, a month, "Include private" | dees-table filters | Behind the filter button at the end of the table's search field, which counts the active filters and opens them in the speech bubble; each active one is a removable chip in the bar above the column headers. Never a row of dropdowns and checkboxes in the header. See Where actions go. | | Put a table's search field in a page header, beside the page's own buttons | dees-table-search | The table's own field and filter button, linked to the table; the table hides its built-in field. Do not wire a plain input to setSearchQuery(). | | Show metrics, health and operational context | dees-statsgrid, dees-dataview-statusobject | Metric tiles versus structured status details and actions. | | Plot a time series, compare categories, show proportions, capacity or profiles | Chart components | Area, bar, donut, gauge and radar have distinct data contracts. | | Browse object storage with folders, files and preview | dees-storage-browser | Owns columns/list switching, provider operations and responsive preview. | | Preview a file or display source/diffs | dees-preview, dees-dataview-codebox | Preview chooses the viewer; codebox owns source and inline/split diffs. | | Edit project files with terminals | dees-workspace | Monaco, file tree, terminal and diff workspace. | | Run an agent conversation with tools and decisions | dees-harness-chat | Compose the harness family; use its resource list to arrange conversations and resources, its session list for a read-only overview of sessions. | | Ask for a focused decision or show contextual commands | dees-modal, dees-contextmenu | Modal task flow versus contextual action menu. Use existing overlay ownership. | | Put the actions of a dialog, a speech bubble or a card of your own at its bottom edge | dees-actionfooter | Divider, tinted strip, the primary rightmost, one action at a time. A dialog's menuOptions and a bubble's actions are drawn by it already; never a row of buttons built by hand. Not the countdown question bar, which is dees-actionbar. | | Show completion, progress or transient feedback | dees-stepper, dees-progressbar, dees-toast | Workflow stages, measurable progress, or a brief notification. |

Before adding a control

  1. Find the task above and search the tag index below. Read the linked API and the actual implementation; similarly named controls have different contracts.
  2. Open its catalog demo with pnpm run watch (the running workspace uses port 3002). Check selection, events, form participation, keyboard behavior and sizing.
  3. Compose the existing component. If it lacks a needed reusable capability, extend its owner and verify existing consumers instead of copying its markup and styles into each view.
  4. When adding or extending a component, update its task entry, tag/source index, API example and demo together. Keep internal anchor and source links valid.

Documentation map

Installation

The examples below import DeesElement directly, so install both packages:

pnpm add @design.estate/dees-catalog @design.estate/dees-element@^4.0.1

The catalog runs on @design.estate/dees-element 4 and @design.estate/dees-domtools 5. A host that depends on either package itself uses those majors (^4.0.1 and ^5.0.0); an older major puts a second copy in its bundle.

Quick Start

import { html, DeesElement, customElement } from '@design.estate/dees-element';
import '@design.estate/dees-catalog';

@customElement('my-app')
class MyApp extends DeesElement {
  render() {
    return html`
      <dees-button type="accent" @click=${() => alert('Hello!')}>
        Click me!
      </dees-button>
    `;
  }
}

Entry points

| Import | What it is | | --- | --- | | @design.estate/dees-catalog | Every component, registered on import, with the types of their properties and events. | | @design.estate/dees-catalog/icon | dees-icon alone (see DeesIcon). | | @design.estate/dees-catalog/cdn | Where the catalog loads Monaco from, as constants, and monacoVsDirectoryUrl() for a server that serves Monaco itself. Imports no component, Lit or DOM API, so it runs in plain Node. | | @design.estate/dees-catalog/richtext-markdown | The richtext editor's Markdown format without the editor: richtextMarkdownToDoc, richtextDocToMarkdown and richtextMarkdownRenderOptions, and its references: richtextReferenceHref, richtextReferenceId, richtextMarkdownToReferences and richtextReferencesToMarkdown. Imports no component, Lit or DOM API, so it runs in plain Node. |

Name a component's event and property types from the package root, never from a dist_ts_web/... path:

import type { ICellEditDetail } from '@design.estate/dees-catalog';

table.addEventListener('cellEdit', (event: CustomEvent<ICellEditDetail<IInvoiceRow>>) => {
  save(event.detail.row, event.detail.key, event.detail.newValue);
});

The prebuilt bundle

dist_bundle/bundle.js is the whole catalog as one ES module, for a page that loads it without a bundler of its own. It imports its shared code, and on first use ECharts, xterm.js, highlight.js, Lightweight Charts, the rich-text editor and the PDF.js worker, from dist_bundle/chunks/ by relative URL, so a server publishes the whole dist_bundle/ directory, chunks/ beside bundle.js; bundle.js alone fails at its first import. The chunks come from the same origin, so script-src 'self' covers them.

Content Security Policy

One library still loads from jsdelivr by default when it is first needed, through DeesServiceLibLoader: Monaco, for the workspace editors. Everything else ships inside the catalog: ECharts for the ECharts-based charts (with only the series and components they draw), xterm.js and its addons for terminals, Lightweight Charts, and highlight.js with the languages its code views name. Charts, terminals and code views therefore work under script-src 'self'. A server whose CSP lists Monaco's CDN takes the URL from the cdn entry, which follows the catalog's pin, instead of writing it by hand:

import { CDN_MONACO_BASE_URL } from '@design.estate/dees-catalog/cdn';

// a directory: the trailing slash makes each source match the files below it
const scriptSrc = ["'self'", `${CDN_MONACO_BASE_URL}/`].join(' ');
const styleSrc = ["'self'", `${CDN_MONACO_BASE_URL}/`].join(' ');
const connectSrc = ["'self'", `${CDN_MONACO_BASE_URL}/`].join(' ');
const workerSrc = 'blob:';

Monaco is not one module: its loader.js and the modules it requires load from below CDN_MONACO_BASE_URL (script-src). Its editor.main links editor/editor.main.css into the document head (style-src), and the catalog reads the same file with fetch() (connect-src) for the editors' shadow roots, once that link has loaded, from the browser's cache: the stylesheet loads once. Under a style-src that blocks the link the editors still get the stylesheet through fetch(), but Monaco's rules for the document itself (its screen-reader live region) are missing. Monaco starts its language workers from blob: URLs (worker-src), and each worker loads its script from below CDN_MONACO_BASE_URL under the page's script-src. Without worker-src blob: the browser blocks the workers: the editors still open, but without what the workers compute, such as JSON validation.

A host that serves Monaco from its own origin needs no CDN source at all; see Serving Monaco from your own origin.

CDN_SCRIPT_URLS and CDN_VERSIONS are deprecated, every entry of them: nothing loads those URLs any more (xterm, ECharts and highlight.js are bundled; the echarts pin still names the 5.6.0 file the catalog once loaded, while the bundled ECharts is 6.x), so a CSP needs none of them. They stay exported until the next major. A host that already ships xterm or highlight.js hands its own in with provideXtermModules or provideHighlightJs, and the catalog uses it instead of its bundled copy. The bundled highlight.js languages are Bash (also shell, console), C, C++, C#, CSS, diff, Dockerfile, Go, GraphQL, HTML/XML, INI/TOML, Java, JavaScript, JSON, Kotlin, Less, Markdown, PHP, Python, Ruby, Rust, SCSS, SQL, Swift, TypeScript and YAML, under highlight.js's names and aliases; an unlabelled fence in a harness message is detected among them, and code in another language is shown plain. The same constants are exported from the package root, beside CDN_BASE and CDN_VERSIONS.

Serving Monaco from your own origin

A host whose CSP forbids CDN scripts, or that must work offline, serves Monaco's min/vs directory itself and tells the catalog where, before the first editor mounts. The cdn entry locates that directory, from the monaco-editor the catalog itself depends on, so the copy matches the catalog's pin even when the host has no monaco-editor of its own:

// server, in Node
import { fileURLToPath } from 'node:url';
import { monacoVsDirectoryUrl } from '@design.estate/dees-catalog/cdn';

app.use('/assets/monaco/min/vs', express.static(fileURLToPath(monacoVsDirectoryUrl())));
// or copy it into the static files at build time:
// await fs.cp(monacoVsDirectoryUrl(), './public/assets/monaco/min/vs', { recursive: true });
// page, before the first editor mounts
import { DeesServiceLibLoader } from '@design.estate/dees-catalog';

DeesServiceLibLoader.getInstance().provideMonacoBaseUrl('/assets/monaco/min/vs');

monacoVsDirectoryUrl() is a file: URL ending in /. Monaco then loads its scripts, its worker scripts and its stylesheet from the page's own origin, so 'self' covers them in script-src, style-src and connect-src; its workers still start from blob: URLs, so the CSP keeps worker-src blob:. The CDN stays the default for now; the next major serves Monaco from the host's own origin by default.

Languages

The words the components say to a reader — button texts, names for assistive technology, tooltips, empty states — come from one typed @push.rocks/smarti18n catalog, exported as dcMessages (namespace @design.estate/dees-catalog). English is the source; German, with the formal "Sie", ships complete. A page provides its language once, through provideI18n from @design.estate/dees-element, and every component re-renders when it changes:

import { createI18n } from '@push.rocks/smarti18n';
import { provideI18n } from '@design.estate/dees-element';
import { dcMessages } from '@design.estate/dees-catalog';

const i18n = createI18n({ locale: 'de-DE', formats: { number: 'de-DE' } });
provideI18n(document.documentElement, i18n, { reflect: true }); // sets <html lang dir>
await i18n.setLocale('en-GB');

// a word of your own, for one language
i18n.override(dcMessages, 'de', { 'appui.skipLink': 'Direkt zum Inhalt' });
  • Without a provider, the components speak English in neutral formats: numbers ungrouped (5000), inside messages too, at most three fraction digits, file sizes in Intl units (2 kB), and weeks from Monday. A component whose provider goes away returns to this.

  • Modals, menus and toasts appended to document.body find the provider on <html>. An overlay opened from a subtree that speaks another language passes that language through the overlay helper's i18n option (DeesModal.createAndShow({ …, i18n }), DeesModal.confirm, DeesModal.prompt, DeesToast.show). The overlay's own words (its default buttons, help and close labels) speak it as well as the content it renders, and follow that instance rather than the page.

  • Numbers and dates follow the instance's formats (formats.number, formats.date), which a host sets apart from the language.

  • The app shell's labels (see dees-appui) still override the catalog's words in every language.

  • The date picker names months and weekdays in the date format's language and starts the week where that format does (Monday in de-DE, Sunday in en-US; Monday without a provider); its weekStartsOn, when set, wins.

  • A helper outside an element (the block editor's slash menu, the storage browser's formatSize and validateMove, the agent chat's harnessFormat* helpers, the PDF formatFileSize) takes the words or formats to speak in and defaults to English; dcSourceWords are the catalog's English source words.

  • The agent chat's tool descriptors get the showing element's words as the optional second argument of label, subtitle and summary, and custom body renderers find them as helpers.t, so a registered tool can speak the reader's language too.

  • Every component's words are in the catalog. scripts/check-literal-ratchet.cjs, part of pnpm test, counts the English literals left in component templates and lets the count only go down; the two it still counts are URL examples in the block editor's link and video fields, which no language changes.

  • An error a host throws for a component to show (a stepper action's refusal) can speak the reader's language after it was thrown: @push.rocks/smarti18n's LocalizedError (≥ 1.5.0) keeps a catalog key and its parameters with the words they come from, and its message says them in the language of the moment it is read. A component that renders again on a change of language, as the stepper's alert does, shows the new language. Its key and args are typed as t() types them:

    import { LocalizedError } from '@push.rocks/smarti18n';
    
    throw new LocalizedError(words, 'claim.amountInvalid', { amount }); // words = i18n.bind(catalog)
  • The pseudo-locales en-XA (longer, accented words) and ar-XB (right to left) show clipped text and direction faults: createI18n({ locale: 'en-XA', pseudo: true }).

Development Guide

For developers working on this library, please refer to the UI Components Playbook for comprehensive patterns, best practices, and architectural guidelines.

Repository development notes, including readme.plan.md and readme.hints.md, live in docs/development/. These notes are retained in Git and excluded from the published package.

Before publishing, verify the installed package as well as the source tests:

pnpm test
node scripts/check-packed-consumer.mjs

pnpm test rebuilds the package first. The consumer check installs its tarball in an isolated temporary project and checks the table, settings, modal, PDF viewer, PDF thumbnail, harness message-list, datepicker, textarea and composer declarations with TypeScript 5.9.3 and 6.0.3, strict: true and skipLibCheck: false. It also runs Chromium construction, reconnect, selection/copy and real keyboard checks against the installed modules and bundle. The packed tests also exercise strict calendar validation, multiline form values and attachment capabilities. Trusted keyboard entry inside a native dialog is checked in UTC, Los Angeles, Berlin and São Paulo. No source aliases or vendor declarations are used. Pass an existing release tarball as the script's argument to verify those exact bytes.

Test sets

pnpm test runs every test file and is the check before a release. While working on a change, run the test files that change can affect:

pnpm test:changed                  # what changed since origin/main, committed or not
pnpm test:changed --list           # which test files, and the change that picks each, without running them
pnpm test:set element dees-table   # the test files that cover one element
pnpm test:set group dataview       # ... or an element group; `pnpm test:set groups` lists them
pnpm test:set path ts_web/elements/00theme.ts
pnpm test:set why test/test.chips.chromium.ts ts_web/elements/00group-utility/dees-icon/dees-icon.ts

A set runs the ratchet checks pnpm test runs, builds the package when a picked test file reads the build, and then runs the picked files with the options of pnpm test. Arguments after -- go to tstest. Tests are bundled without a type check; pnpm run build checks the types.

scripts/test-set.mjs picks a test file when the change touches what the file covers: the modules it imports, followed through their own imports, and every element whose tag it or one of those modules uses in a template or creates by name. A barrel such as ts_web/index.ts is followed only for the names imported from it, and a component's demo only for a test file that imports or renders it. A change to package.json, pnpm-lock.yaml, pnpm-workspace.yaml, tsconfig.json or .smartconfig.json runs every test file; documentation runs none. What neither imports nor tags show, a test file names in a comment line, // test-set:covers <path> ... (the notices and entry-point tests do).

The full run starts four test files at a time (concurrency in the @git.zone/tstest section of .smartconfig.json), each in a Chromium or Node.js process of its own. A test file therefore keeps to its own process: a server it starts binds port 0 and reads the port it got, and it waits for an element's own events rather than for the clock. A file has 30 seconds (--timeout 30); one that needs more is split by subject.

Component index

Search this table by the actual HTML tag. It includes registered catalog elements and supporting subcomponents; the task guide identifies the usual composition entry points. API links lead to sections below; source links always lead to the owning implementation, including components without a dedicated reference section yet. Demo files live beside their components.

Run node scripts/check-component-docs.cjs to check index coverage and README links. After adding a component or API section, run the same command with --write-index to regenerate the tag table from registrations. Update the task guide and examples deliberately; the generated table does not replace them.

| Element | Family | API | Source | | --- | --- | --- | --- | | dees-actionbar | feedback | DeesActionbar | Source | | dees-actionfooter | button | DeesActionFooter | Source | | dees-appui | appui | DeesAppui | Source | | dees-appui-activitylog | appui | DeesAppuiActivitylog | Source | | dees-appui-appbar | appui | — | Source | | dees-appui-bottombar | appui | DeesAppuiBottombar | Source | | dees-appui-commandpalette | appui | DeesAppuiCommandpalette | Source | | dees-appui-contentheading | appui | DeesAppuiContentheading | Source | | dees-appui-location | appui | DeesAppuiLocation | Source | | dees-appui-maincontent | appui | DeesAppuiMaincontent | Source | | dees-appui-mainmenu | appui | DeesAppuiMainmenu | Source | | dees-appui-page | appui | DeesAppuiPage | Source | | dees-appui-profiledropdown | appui | — | Source | | dees-appui-secondarymenu | appui | DeesAppuiSecondarymenu | Source | | dees-appui-tabs | appui | DeesAppuiTabs | Source | | dees-appui-tray | appui | DeesAppuiTray | Source | | dees-audio-viewer | media | DeesAudioViewer | Source | | dees-avatar | dataview | DeesAvatar | Source | | dees-badge | feedback | DeesBadge | Source | | dees-button | button | DeesButton | Source | | dees-button-exit | button | DeesButtonExit | Source | | dees-button-group | button | DeesButtonGroup | Source | | dees-chart-area | chart | DeesChartArea | Source | | dees-chart-bar | chart | DeesChartBar | Source | | dees-chart-donut | chart | DeesChartDonut | Source | | dees-chart-gauge | chart | DeesChartGauge | Source | | dees-chart-line | chart | DeesChartLine | Source | | dees-chart-log | chart | DeesChartLog | Source | | dees-chart-radar | chart | DeesChartRadar | Source | | dees-chips | layout | DeesChips | Source | | dees-contextmenu | overlay | DeesContextmenu | Source | | dees-dashboardgrid | layout | DeesDashboardgrid | Source | | dees-dataview-codebox | dataview | DeesDataviewCodebox | Source | | dees-dataview-statusobject | dataview | DeesDataviewStatusobject | Source | | dees-dropzone-overlay | overlay | DeesDropzoneOverlay | Source | | dees-form | form | DeesForm | Source | | dees-form-section | form | DeesFormSection | Source | | dees-form-submit | form | DeesFormSubmit | Source | | dees-formatting-menu | input | — | Source | | dees-grouped-list | dataview | DeesGroupedList | Source | | dees-harness-chat | harness | DeesHarnessChat | Source | | dees-harness-composer | harness | DeesHarnessComposer | Source | | dees-harness-content-blocks | harness | DeesHarnessContentBlocks | Source | | dees-harness-conversation-picker | harness | DeesHarnessConversationPicker | Source | | dees-harness-message | harness | DeesHarnessMessage | Source | | dees-harness-message-list | harness | DeesHarnessMessageList | Source | | dees-harness-overflow-text | harness | DeesHarnessOverflowText | Source | | dees-harness-permission-card | harness | DeesHarnessPermissionCard | Source | | dees-harness-question-card | harness | DeesHarnessQuestionCard | Source | | dees-harness-reasoning | harness | DeesHarnessReasoning | Source | | dees-harness-resource-list | harness | DeesHarnessResourceList | Source | | dees-harness-session-list | harness | DeesHarnessSessionList | Source | | dees-harness-session-sidebar | harness | DeesHarnessSessionSidebar | Source | | dees-harness-sidebar | harness | DeesHarnessSidebar | Source | | dees-harness-status | harness | DeesHarnessStatus | Source | | dees-harness-thread-cut | harness | DeesHarnessThreadCut | Source | | dees-harness-todos | harness | DeesHarnessTodos | Source | | dees-harness-tool-card | harness | DeesHarnessToolCard | Source | | dees-harness-tool-fullscreen | harness | — | Source | | dees-harness-usage | harness | DeesHarnessUsage | Source | | dees-harness-work-run | harness | DeesHarnessWorkRun | Source | | dees-heading | layout | DeesHeading | Source | | dees-hint | feedback | DeesHint | Source | | dees-icon | utility | DeesIcon | Source | | dees-image-viewer | media | DeesImageViewer | Source | | dees-input-checkbox | input | DeesInputCheckbox | Source | | dees-input-code | input | DeesInputCode | Source | | dees-input-datepicker | input | DeesInputDatepicker | Source | | dees-input-datepicker-popup | input | — | Source | | dees-input-dropdown | input | DeesInputDropdown | Source | | dees-input-dropdown-popup | input | — | Source | | dees-input-fileupload | input | DeesInputFileupload | Source | | dees-input-iban | input | DeesInputIban | Source | | dees-input-list | input | DeesInputList | Source | | dees-input-multitoggle | input | DeesInputMultitoggle | Source | | dees-input-phone | input | DeesInputPhone | Source | | dees-input-profilepicture | input | DeesInputProfilePicture | Source | | dees-input-quantityselector | input | DeesInputQuantitySelector | Source | | dees-input-radiogroup | input | DeesInputRadiogroup | Source | | dees-input-reference | input | DeesInputReference | Source | | dees-input-richtext | input | DeesInputRichtext | Source | | dees-input-tags | input | DeesInputTags | Source | | dees-input-text | input | DeesInputText | Source | | dees-input-textarea | input | DeesInputTextarea | Source | | dees-input-toggle | input | DeesInputToggle | Source | | dees-input-typelist | input | DeesInputTypelist | Source | | dees-input-wysiwyg | input | DeesInputWysiwyg | Source | | dees-kanban | kanban | DeesKanban | Source | | dees-kanban-activity | kanban | DeesKanbanActivity | Source | | dees-kanban-card | kanban | DeesKanbanCard | Source | | dees-kanban-column | kanban | DeesKanbanColumn | Source | | dees-kanban-discussion | kanban | DeesKanbanDiscussion | Source | | dees-kanban-ticket-detail | kanban | DeesKanbanTicketDetail | Source | | dees-label | layout | DeesLabel | Source | | dees-legend | chart | DeesLegend | Source | | dees-modal | overlay | DeesModal | Source | | dees-mosaic | layout | DeesMosaic | Source | | dees-pagination | layout | DeesPagination | Source | | dees-panel | layout | DeesPanel | Source | | dees-pdf-viewer | media | DeesPdfViewer | Source | | dees-popover | overlay | DeesPopover | Source | | dees-preview | media | DeesPreview | Source | | dees-profilepicture-modal | input | — | Source | | dees-progressbar | feedback | DeesProgressbar | Source | | dees-resize-handle | layout | DeesResizeHandle | Source | | dees-searchbar | utility | DeesSearchbar | Source | | dees-settings | layout | — | Source | | dees-shopping-productcard | simple | DeesShoppingProductcard | Source | | dees-simple-appdash | simple | DeesSimpleAppDash | Source | | dees-simple-login | simple | DeesSimpleLogin | Source | | dees-slash-menu | input | — | Source | | dees-sparklebox | layout | — | Source | | dees-speechbubble | overlay | DeesSpeechbubble | Source | | dees-spinner | feedback | DeesSpinner | Source | | dees-state | feedback | DeesState | Source | | dees-statsgrid | dataview | DeesStatsGrid | Source | | dees-statusbutton | feedback | DeesStatusbutton | Source | | dees-stepper | layout | DeesStepper | Source | | dees-storage-browser | dataview | DeesStorageBrowser | Source | | dees-storage-columns | dataview | — | Source | | dees-storage-keys | dataview | — | Source | | dees-storage-preview | dataview | — | Source | | dees-table | dataview | DeesTable | Source | | dees-table-search | dataview | DeesTableSearch | Source | | dees-terminal-view | terminal | DeesTerminalView | Source | | dees-theme | utility | DeesTheme | Source | | dees-thread | dataview | DeesThread | Source | | dees-thumbnail-audio | media | DeesThumbnailAudio | Source | | dees-thumbnail-folder | media | DeesThumbnailFolder | Source | | dees-thumbnail-image | media | DeesThumbnailImage | Source | | dees-thumbnail-note | media | DeesThumbnailNote | Source | | dees-thumbnail-pdf | media | DeesThumbnailPdf | Source | | dees-thumbnail-video | media | DeesThumbnailVideo | Source | | dees-tile | layout | DeesTile | Source | | dees-toast | feedback | DeesToast | Source | | dees-updater | utility | DeesUpdater | Source | | dees-video-viewer | media | DeesVideoViewer | Source | | dees-windowcontrols | utility | DeesWindowControls | Source | | dees-windowlayer | overlay | DeesWindowLayer | Source | | dees-workspace | workspace | DeesWorkspace | Source | | dees-workspace-bottombar | workspace | DeesWorkspaceBottombar | Source | | dees-workspace-diff-editor | workspace | DeesWorkspaceDiffEditor | Source | | dees-workspace-filetree | workspace | DeesWorkspaceFiletree | Source | | dees-workspace-markdown | workspace | DeesWorkspaceMarkdown | Source | | dees-workspace-markdownoutlet | workspace | DeesWorkspaceMarkdownoutlet | Source | | dees-workspace-monaco | workspace | DeesWorkspaceMonaco | Source | | dees-workspace-terminal | workspace | DeesWorkspaceTerminal | Source | | dees-workspace-terminal-preview | workspace | DeesWorkspaceTerminalPreview | Source | | dees-wysiwyg-block | input | — | Source |

Component API

Reflected properties

A component's public configuration is one surface in markup, in devtools and in CSS: every public property of a string, number or boolean type reflects to its attribute, whether it was set from markup or through a property binding. A page can style around a configuration, such as dees-panel[variant='outline'] or dees-spinner[bnw], and a component's own :host([...]) rules hold however the value arrived. Attribute names are the property name in lower case unless the property declares another one, such as dees-table's show-column-filters.

Four kinds of property stay unreflected, each listed with its reason in scripts/reflect-allowlist.json: user data and state that changes per keystroke, pointer move, scroll or data tick (a field's value, a search query, progress; a toggle's on/off value reflects, since its own styles select it); objects, arrays and functions, which have no attribute; names that are global HTML or ARIA attributes with native behaviour on the host (title, aria-label); and large text or encoded data (a whole file, a diff, a data URL). pnpm test runs scripts/check-reflect-ratchet.cjs, which fails on a new public primitive property that neither reflects nor is listed there.

Failed actions

Components that run an application callback — dees-modal's menuOptions[].action, dees-table's dataActions[].actionFunc and onRetry, dees-tile's actions[].actionFunc, dees-appui-page's and dees-appui-contentheading's actions[].action, dees-state's actions[].action and onRetry, dees-popover's actions[].action and dees-actionfooter's actions[].action — await it. A rejected callback is never dropped: the component dispatches the cancelable dees-action-error event (bubbling and composed) and, unless a listener cancels it, shows the error through dees-toast. Successful callbacks behave exactly as before and dispatch nothing.

element.addEventListener('dees-action-error', (event: CustomEvent<IActionErrorDetail>) => {
  const { error, action, source } = event.detail;
  // source: 'modal-menu' | 'modal-dismiss' | 'stepper-cancel' | 'table-header' | 'table-footer' | 'table-empty'
  //       | 'table-row' | 'table-rowmenu' | 'table-contextmenu' | 'table-doubleclick' | 'table-rowclick'
  //       | 'table-keycombination'
  //       | 'tile-header' | 'tile-footer' | 'page-action' | 'heading-action' | 'state-action' | 'state-retry'
  //       | 'popover-action' | 'actionfooter' (a footer's own `source` when it sets one)
  event.preventDefault();      // render your own error surface instead of the toast
  reportToCrashService(error);
});

IActionErrorDetail, actionErrorEventName, actionErrorMessage, reportActionError and runAction are exported from the package root, so a component outside the catalog can announce failures the same way.

Where actions go

An action sits on the thing it acts on, in that thing's own place for actions, so every implementation offers its actions the same way. Choose the place by what the action is about:

| The action is about | Its place | For example | | --- | --- | --- | | A row of a table | the table's dataActions with type: ['inRow', 'contextmenu'] | "Open", "Approve" | | A table as a whole, or its selected rows | the table's dataActions with type: ['header'] or ['footer']; ['empty'] for the way on from no rows | "Import", "Export", "Approve selected", "New service" | | What a tile shows: a summary, a status, a chart, a list that is not a table | the tile's actions, type: ['footer'] for the way on from the content, ['header'] for one that adds to or refreshes it | "Open the audit trail", "Export CSV", "New invoice" | | A whole page or view | dees-appui-page actions, or the view's contentHeading actions | "New service" | | A decision a dialog asks for | dees-modal menuOptions | "Cancel", "Delete" | | What a speech bubble shows, such as its filters | the bubble's actions | "Clear filters" | | A place that loads, is empty or failed | dees-state actions | "Retry", "New service" | | Not an action: a standing explanation of a view — how a feature works, what a list holds | the table's or the tile's info: an info button (ⓘ) at the end of the footer bar, after the footer actions, that opens it in the speech bubble | "A GoBD year archive holds one fiscal year: …" | | Not an action: the view's filters | the table's filters, behind the filter button at the end of its search field; what is active shows as chips in the active-filter bar above the column headers | "Account", "Month", "Include private" |

  • Never build a bar of buttons into a tile's footer or header slot. Tile actions and a table's header and footer actions are one standard, drawn by one renderer: they share their size, type, spacing, wrapping, glyph, busy face, keyboard behaviour and accessible name.
  • A dialog's and a speech bubble's actions stand in their action footer, dees-actionfooter, at the bottom edge: a divider, a tinted strip, the primary rightmost. Never build a row of buttons into a dialog's or a bubble's content; a surface of your own that asks like a dialog, a card centred in a view, places the same footer.
  • A tile whose content is a table offers the actions about the table's rows as the table's own header or footer actions, never in a second footer under the table's: the table's footer already carries its count and its actions.
  • The slots are for content: a status line, a legend, statistics, a chip. Next to actions, slotted content takes the bar's leading side.
  • An action that applies but cannot be taken yet stays visible and disabled; one that never applies is not offered.
  • A standing explanation of a view never goes in a banner or a hint above its table or tile: it goes in the info of the table or tile it explains. The info button is the footer bar's last control, named in the reader's language ("About this list"; on a tile "About {heading}"), and shows even where the bar has no other actions; its words are the host's, in the host's language. Hints stay for what is true now (a missing statement), the status button for a page's statuses.
  • Where filters go: into the filter button. A view's filters — a dropdown, a checkbox, a day — are the table's filters, never a row of fields in the page or table header. The filter button is the sliders button at the end of the table's search field, in the table's header or in dees-table-search on the page header: one entry point, never a second filter control beside the field. It counts the active filters and opens them in the speech bubble, with "Filter by column" and where the search looks, and "Clear filters" in the bubble's action footer; a choice applies as it is made. Each filter declares its default, the value that means "not filtered" (every account, any month, private movements included): only a filter at another value is active, counts on the button, shows as a chip ("Account: Sparkasse", "Without private") in the active-filter bar between the header and the column headers, and "Clear all" puts it back to its default. The search counts in its own field, not on the button, and is a chip in the bar.

Frameless components

A component that draws its own box keeps it by default. Where the host already owns the surface — a modal, a preview stage, a transcript row, a table cell, a fullscreen view, a coloured panel — set frameless: the component drops its outer border, its corner radius and its surface, so the host's surface shows through its header, content and footer. Content, padding, separators, focus rings and behavior stay as they are, and so do the backgrounds that belong to what the component shows: controls, a terminal, a code editor, a PDF page, a video stage. A selected table row becomes a tint over the host's surface.

A part that content scrolls beneath — a table's sticky column header or sticky footer, a pinned action column — paints an opaque backdrop while content passes beneath it, in --dees-tile-surface (the catalog's content surface unless set). A host whose surface is not that one sets the property on the frameless component, so the backdrop matches:

<dees-table frameless fixed-height style="--dees-tile-surface: var(--my-panel-surface)"></dees-table>

frameless is a reflected boolean, so it works from markup, from a property binding, and from the host's own CSS through :host([frameless]).

// The modal owns the window, so the diff fills it without a second border.
const modal = await DeesModal.createAndShow({
  heading: 'config.ts',
  content: html`<dees-dataview-codebox frameless .unifiedDiff=${patch}></dees-dataview-codebox>`,
});

Supported by dees-chart-area, dees-chart-bar, dees-chart-donut, dees-chart-gauge, dees-chart-log, dees-chart-radar, dees-dataview-codebox, dees-dataview-statusobject, dees-input-code, dees-input-dropdown, dees-input-fileupload, dees-input-list, dees-input-richtext, dees-input-text, dees-input-wysiwyg, dees-kanban-ticket-detail, dees-pdf-viewer, dees-preview, dees-settings, dees-simple-login, dees-table, dees-tile, dees-video-viewer and dees-workspace-terminal-preview.

dees-tile owns the rule for every component built on it; a new boxed component joins the convention by forwarding .frameless=${this.frameless} to its tile and painting no surface of its own over it in frameless mode. On dees-input-text and dees-input-dropdown — what dees-table uses for inline cell editing — the same flag also hides the label and validation row, because the cell owns that chrome. dees-simple-login drops its card's surface and the page backdrop around it. On dees-video-viewer, frameless means filling the enclosing frame: the stage keeps its black letterbox and the controls bar its surface, because both sit over the video. dees-kanban-ticket-detail draws no frame to drop: frameless only says that its edges are its host's, so its toolbar, its main column and the outer side of its fields take the region inset; the board sets it in its modal. dees-dashboardgrid, dees-statsgrid and dees-stepper draw one card per item instead of a frame of their own, so they deliberately have no frameless.

A frameless component also takes the region inset at its edges.

Region inset

--dees-inset-inline is the inline inset of the region an element sits in, measured to that region's edges. It is how content lines up with the heading of the page or pane around it: a page whose heading sits 32px in declares 32px on its body, and a frameless table in that body puts its first and last content 32px in as well.

  • Who sets it. A container that gives its content its full width — a page frame with a flush body, a pane, a drawer or modal body that runs to its edges — sets it there, to its own content inset. The theme sets it nowhere: unset, no region inset is declared. In the catalog, dees-modal declares its heading's inset on a body with contentPadding: 0.
  • Who reads it. Components drawn frameless. Their edges are their host's edges, so the content at their inline edges — cells, header and footer bands, toolbars, notices, list rows, skeleton rows, labels — sits at var(--dees-inset-inline, <own inset>). Unset, nothing moves. A framed component sits inside its host's padding and keeps its own inset; taking the region inset as well would inset it twice.
  • No leaking. A component that reads the token reads it once, on its host, and resets it for everything it renders and slots, so a component nested in it — a table in a table cell, a component in a settings value — sees a region inset only when a nearer container declares one. Two pass it on instead, because their content runs to their own edges with no padding of its own: a frameless dees-tile to its slots, and a frameless dees-preview to the viewer in its content area. A tile's header or footer bar (tile actions) pads to the inset itself, so what it slots sees none.
// A page frame with a flush body: the heading's inset is the region inset of the body.
@customElement('my-page')
export class MyPage extends DeesElement {
  public static styles = [themeDefaultStyles, css`
    :host { display: flex; flex-direction: column; height: 100%; }
    h1 { margin: 0; padding: var(--dees-spacing-xl) var(--dees-spacing-2xl) var(--dees-spacing-md); }
    .body { --dees-inset-inline: var(--dees-spacing-2xl); flex: 1; min-height: 0; display: flex; flex-direction: column; }
  `];
  public render() {
    return html`<h1>Services</h1>
      <div class="body"><dees-table frameless fill .columns=${columns} .data=${services}></dees-table></div>`;
  }
}

Here the table's header band, its first and last cells, its footer, its skeleton rows and its notice sit 32px from the body's edges: a selection checkbox starts at 32px and the row actions end there. A fill or fixed-height table takes no room for its scrollbar, whose rail lies over the end of its rows while they scroll (see Scrollbars). A narrow page declares its narrow inset, such as var(--dees-spacing-lg), which the table takes in place of its own narrow values.

A few places keep part of their own inset, each for a reason:

  • an inline cell editor and a column filter field pad their text themselves, so their cell adds only the rest and the text stays where the cell showed it;
  • dees-input-wysiwyg keeps at least the 28px its block handles sit in;
  • ECharts charts (dees-chart-line, -bar, -donut, -gauge, -radar) inset their plot by 16px themselves, so the plot's frame moves in by what the region inset adds;
  • a split diff in dees-dataview-codebox and the thumbnail strip of dees-pdf-viewer take it on their outer sides only, not at the divider;
  • content centred in its box — an empty, loading or error state, the dees-simple-login card — has no edge to line up and keeps its padding.

A component outside the catalog joins the standard with insetInlineStyles, exported from the package root. It reads the token on the host into the component's own property while the host is [frameless], leaves that property unset otherwise, and resets the token for the shadow tree:

import { insetInlineStyles, themeDefaultStyles } from '@design.estate/dees-catalog';

public static styles = [
  themeDefaultStyles,
  insetInlineStyles('--panel-inset-inline'),
  css`.toolbar { padding-inline: var(--panel-inset-inline, 12px); }`,
];

Scroll edges

Where content scrolls beneath fixed chrome — a header row above it, a search field, pinned rows or a footer below it — the edge content lies past casts a shadow: scrolled down, the top edge casts one; with content below the fold, the bottom edge does; content that fits casts none. Each shadow is a 16px band of --dees-color-scroll-edge-shadow painted from the edge inward (a 35% stop at 6px), drawn over the content without taking its room or its pointer input, and