@neovici/cosmoz-tabs
v10.3.0
Published
A multi views container element that allows navigation between the views using tabs.
Readme
<cosmoz-tabs>
Tabbed-views web components, styled with the Untitled UI
design system via @neovici/cosmoz-tokens.
Built with Pion.js + lit-html.
The package ships two tab families plus a card:
| Element / API | Use it when |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| cosmoz-tabs + cosmoz-tab | DOM-driven: author <cosmoz-tab> elements; the container renders the bar and switches panels. Selection works out of the box. |
| cosmoz-tabs-next + cosmoz-tab-next (+ useTabs/renderTabs/renderActivated) | Data-driven: drive tabs from a data array; a cosmoz-tab-next is only the clickable header — selection is owned by the consumer (hook or your own active wiring). |
| cosmoz-tab-card | A collapsible card, typically placed inside a cosmoz-tab. |
Both families share a single styling source of truth (src/styles.ts) and support three
Untitled UI variants via a variant attribute on the container: brand (default),
underline and segmented. Tabs spread to fill the bar by default (as the legacy tabs
did); add the compact-width attribute to size them to their content.
size picks how much box the tabs carry. Omit it for the default; size="sm" trims the
item's padding on both axes and thins the segmented track's ring to match, which is what
a control sitting next to a heading usually wants — the type is left alone, so the labels
stay as readable as they were.
Styling comes from
@neovici/cosmoz-tokens(--cz-*), with light/dark mode via:root.dark-mode. It ships as a dependency — see Install for loading it.
Install
npm i @neovici/cosmoz-tabs@neovici/cosmoz-tokens ships as a dependency, so it is installed for you. The host app
just needs to load it once for the --cz-* token values:
import '@neovici/cosmoz-tokens';Usage
Legacy family (cosmoz-tabs)
import '@neovici/cosmoz-tabs';<cosmoz-tabs selected="overview">
<cosmoz-tab name="overview" heading="Overview">…</cosmoz-tab>
<cosmoz-tab name="rows" heading="Invoice rows" badge="5">…</cosmoz-tab>
<cosmoz-tab name="history" heading="History">…</cosmoz-tab>
</cosmoz-tabs>Bind selection to the URL with hash-param. Tabs spread by default; size them to their
content with compact-width:
<cosmoz-tabs hash-param="tab" compact-width>…</cosmoz-tabs>Icons are passed as a lit-html template (e.g. from
@neovici/cosmoz-icons) via the .icon property:
import { receiptIcon } from '@neovici/cosmoz-icons/untitled';
html`<cosmoz-tab heading="Overview" name="overview" .icon=${receiptIcon()}
>…</cosmoz-tab
>`;Next family (cosmoz-tabs-next)
The recommended, data-driven pattern using the hook API:
import {
useTabs,
renderTabs,
renderActivated,
} from '@neovici/cosmoz-tabs/next';
const tabs = [
{ name: 'overview', title: 'Overview', render: renderOverview },
{ name: 'rows', title: 'Invoice rows', badge: '5', render: renderRows },
];
const Component = () => {
const model = useTabs(tabs, { hashParam: 'tab' });
return html`
<cosmoz-tabs-next variant="brand"> ${renderTabs(model)} </cosmoz-tabs-next>
${renderActivated(model, (tab) =>
tab.isActive ? html`<div>${tab.render()}</div>` : '',
)}
`;
};render above is a consumer-defined field, not part of the Tab/RenderTab type — the
hook and renderActivated/renderTabs are generic over your tab shape and carry your extra
fields through, so tab.render() is type-safe.
For the next family the container reflects variant/size/compact-width onto each
cosmoz-tab-next as plain attributes of the same names (CSS cannot cross the shadow
boundary); when both renderTabs(...) and the container set them, the container wins. Slot
an icon with the icon template's slot option: ${receiptIcon({ slot: 'icon' })}.
API
The custom-element API (attributes, properties, slots, CSS parts) is described in
custom-elements.json and in the JSDoc/Storybook stories.
Highlights:
cosmoz-tabs— attrsselected,hash-param,no-resize,variant,size,compact-width; partstabs,tab,content; eventstab-first-select,tab-select.cosmoz-tab— attrsheading,badge,disabled,hidden; prop.icon.cosmoz-tabs-next— attrsvariant,size,compact-width.cosmoz-tab-next— attrsactive,badge,href,disabled,size;iconslot.cosmoz-tab-card— attrsheading,collapsable,collapsed; partsheader,heading,collapse-icon,content. Themable via the--cosmoz-tab-card-*custom properties (which default to--cz-*tokens).
Development
npm run storybook:start
npm test
npm run test:watch
npm run lint
npm run buildReleases are managed with changesets in CI.
Stories double as tests: see stories/*.stories.js (demos) and stories/*.test.stories.ts
(behavioral tests run by @storybook/addon-vitest).
