| name | web-components |
| description | Use when building UI with @toolcase/web-components — framework-free HTML5 Web Components (`tc-*` custom elements) with from-scratch toolcase styling and a Bootstrap-compatible class API. Covers layout (BasicLayout, DashboardLayout, DashboardContent, DashboardSidebar, Login, Container, Row, Col, Spacer, Stack), content (Accordion, AccordionItem, ActionHeader, ActionItems, ActionRowList, Alert, AnnouncementBar, ApiReferenceTable, AssetRow, AssetRowList, Avatar, Badge, BadgeRow, Banner, Brand, Build, BriefCard, BundleBar, CdnMap, CalloutQuote, Changelog, ChartContainer, Sparkline, TrendIndicator, Leaderboard, LeaderboardTrend, CodeLabelCell, CodeSnippet, CodeWithOutput, CommunityLinks, ConfigPreview, ContributorWall, CookbookGrid, CoolButton, ActivityCard, BasicCard, Button, ButtonGroup, Card, Carousel, CloseButton, Collapse, Divider, Dropdown, DownloadStats, EcosystemMap, EmptyState, GameShowcaseCard, GithubStarsCard, GoodFirstIssues, Group, Hero, HeroStatsBar, Heading, Image, InfiniteScroll, Kbd, ListCard, ListGroup, LogoCloud, MaintainerCard, Marquee, MetricTile, MetricGrid, MigrationGuide, PageFooter, Panel, PhaseGrid, Pipeline, PinnedFeatureShowcase, PluginGrid, PricingCard, File, UserPanel, QueuedFile, Placeholder, Progress, PulseIndicator, ScoringRules, ScoreDisplay, SectionCard, SectionFlag, Skeleton, Spinner, SprintChain, Stamp, MetricCard, StatCard, StateMachine, StatusCard, StatusDot, Stepper, Tag, DataList, TierLadder, Timeline, UsageSummaryPanel, WelcomeGuide, CommandReference, Comparator, CompatibilityMatrix, CountdownTimer, FAQList, FeatureMatrix, Text, VersionLabel, VisuallyHidden), navigation (Breadcrumb, CoolNav, Nav, Navbar, Pagination, Scrollspy, SocialLinks, Stepper), overlays & feedback (ContextMenu, DebugOverlay, Modal, Offcanvas, Popover, Toast, Tooltip), and forms (CardOptions, Check, CheckboxGroup, Chip, ChipGroup, ColorPicker, IconPicker, DatePicker, EarlySignupForm, EditableText, FloatingLabel, Form, HelperText, Input, InputGroup, InputGroupText, Label, MultiCardSelect, NewsletterSignup, Option, Radio, RadioGroup, Range, RangeSlider, DeadzoneSlider, Select, Switch, Textarea). Consumable from any stack — React, Vue, Svelte, or plain HTML. |
web-components — API Reference
Framework-free HTML5 Web Components with their own from-scratch toolcase styling — no Bootstrap dependency, Bootstrap-compatible markup/class API. No React, Vue, or Angular required — drop tc-* tags into any stack.
import { register } from '@toolcase/web-components'
import '@toolcase/web-components/style.css'
register()
SSR / Next.js / server-side rendering — dynamic import required. Every tc-* class body contains extends HTMLElement, which is evaluated at module load time. A static top-level import '@toolcase/web-components' in server-rendered code throws ReferenceError: HTMLElement is not defined. The package ships a node export condition that resolves to a no-op stub, so server-side imports are safe, but browser registration must use a dynamic import inside a client-only boundary:
'use client'
import { useEffect } from 'react'
useEffect(() => {
void import('@toolcase/web-components').then(m => m.register())
}, [])
Never call register() at module top-level in a file that Next.js (or any SSR framework) also renders on the server.
React className merging. Components that own their host class attribute — tc-modal, tc-alert, tc-card, tc-avatar, tc-toast, tc-carousel, tc-navbar, tc-list-group-item, tc-portrait, tc-offcanvas — merge consumer-authored classes into the classes they render instead of clobbering them: classes present on the tag before the first render (e.g. a React className) are captured and re-applied on every subsequent render.
After register() you can author markup directly:
<tc-button variant="primary">Save</tc-button>
<tc-modal title="Confirm">Are you sure?</tc-modal>
<tc-alert variant="success" dismissible>Saved successfully.</tc-alert>
Internationalization (configureMessages)
Every built-in user-visible default string a tc-* component renders (validation copy, placeholders, pagination summaries, aria labels) resolves through a global message registry at render time, so a single configureMessages() call at app bootstrap localises the whole component set. Defaults stay English. Exported from the package root:
configureMessages(overrides: Partial<ToolcaseMessages>) — merges the overrides into the active catalog (keys set to undefined keep their current value) and dispatches a tc-messages-changed CustomEvent on document.
resetMessages() — restores the built-in English defaults (also dispatches tc-messages-changed).
getMessages(): Readonly<ToolcaseMessages> — snapshot of the active catalog (for auditing coverage).
import { configureMessages } from '@toolcase/web-components'
configureMessages({
fieldRequired: 'Ова поле е задолжително',
paginationRange: ({ start, end, total }) => `${start}–${end} од ${total}`,
selectPlaceholder: 'Изберете…',
})
Message catalog
| Key | Default | Used by |
|---|
fieldRequired | This field is required | tc-form-input + the pickers/sliders/OTP — required field left empty |
fieldInvalid | Please provide a valid value. | tc-input, tc-textarea, tc-tag-input |
invalidValue | Invalid value | tc-form-input + the pickers/sliders/OTP — fallback when a validator returns no message |
selectionRequired | Please make a selection. | tc-select, tc-combo-box, tc-extended-select |
selectionInvalid | Please provide a valid selection. | tc-select, tc-combo-box, tc-extended-select |
selectionMinOne | Please select at least one option. | tc-checkbox-group |
invalidChoice | Please choose a valid value. | tc-slider |
invalidDate | Please provide a valid date. | date picker |
invalidTime | Please provide a valid time. | time picker |
invalidColor | Please select a valid color. | color picker |
invalidIcon | Please select a valid icon. | icon picker |
invalidCode | Please enter a valid code. | tc-otp-input |
selectPlaceholder | Select… | selects (tc-select, tc-combo-box, tc-extended-select) |
searchPlaceholder | Search… | searchable selects |
searchOptionsLabel | Search options | searchable selects (search box aria label) |
loading | Loading… | spinners, skeletons, charts, tables |
noData | No data | tc-table empty default + charts |
close | Close | close buttons (modal, offcanvas, toast, alert, drawer, …) |
clear | Clear | reserved |
filtersLabel | Filters | tc-advanced-table filter toolbar |
toggleSidebarLabel | Toggle sidebar | reserved |
paginationLabel | Page navigation | tc-pagination |
paginationPrevious | Previous | tc-pagination |
paginationNext | Next | tc-pagination |
paginationRange | {start}–{end} of {total} | tc-advanced-table pagination summary |
stepsComplete | {completed} of {total} complete | reserved formatter |
starsRating | {value} out of {max} stars | reserved formatter |
fileDropPrompt | Drag & drop files here or click to browse | tc-file-dropzone |
fileDropLabel | Upload files — drag and drop or click to browse | tc-file-dropzone |
fileSelectLabel | Select files to upload | tc-file-dropzone |
Notes:
- Parameterised keys (
paginationRange, stepsComplete, starsRating) accept either a {token} template string or a formatter function ((params) => string) — functions get full control over pluralisation and word order.
- Per-instance attributes (
placeholder, empty-message, error, required-message, …) always win over the registry for one-off cases.
- Call
configureMessages() before components render — components read the registry when they render. Changing messages later requires re-rendering the affected components; listen for tc-messages-changed on document to trigger that.
Table of Contents
Layout
tc-basic-layout
Two-section page layout: an optional brand header region followed by a full-height main content area. Flat structural surface — no shadows, no border-radius, slate neutrals only.
Tag: tc-basic-layout
Attributes
| Attribute | Type | Default | Description |
|---|
brand | string | — | Text content for the brand header. When set, renders a <header> with the attribute value. When omitted and no slot="brand" children are present, the header is hidden entirely. |
JS Properties
| Property | Type | Description |
|---|
brand | string | null | Reflects the brand attribute. |
Events
None. tc-basic-layout is a purely presentational layout element.
Slots
| Slot | Description |
|---|
| (default) | Main page content. Rendered inside <main class="tc-basic-layout-main">. |
brand | Rich brand header content (logos, nav, custom markup). Used when the brand attribute is absent. Rendered inside <header class="tc-basic-layout-brand">. |
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-basic-layout-brand-bg | var(--tc-surface) | Brand header background. |
--bs-basic-layout-brand-color | var(--tc-text) | Brand header text color. |
--bs-basic-layout-brand-border | var(--tc-border) | Color of the 1px hairline beneath the brand header. |
--bs-basic-layout-main-bg | transparent | Main content area background. |
--bs-basic-layout-main-color | var(--tc-text) | Main content area text color. |
<tc-basic-layout brand="My App">
<p>Page content here.</p>
</tc-basic-layout>
<tc-basic-layout>
<div slot="brand">
<img src="logo.svg" alt="My App" />
</div>
<p>Page content here.</p>
</tc-basic-layout>
<tc-basic-layout>
<p>Full-height main area, no brand header.</p>
</tc-basic-layout>
tc-dashboard-layout
Full-height dashboard shell: a full-height sidebar on the left, a glass navbar spanning the content column, and a scrollable content area. Responsive: below 992px the sidebar becomes a slide-in drawer over a dimmed backdrop (scoped to the component, not the viewport); at ≥992px it is pinned open as a static left rail and the toggle is hidden. Named slots cover the brand, menu, panel, and both navbar ends; unslotted children land in the main content. On mobile the drawer opens/closes via the toggle button, Ctrl+B (Cmd+B), backdrop tap, Escape, or the sidebar-open attribute. Dispatches tc-toggle-sidebar on every flip, plus a discrete tc-sidebar-open / tc-sidebar-close.
Tag: tc-dashboard-layout
Attributes
| Attribute | Type | Default | Description |
|---|
sidebar-open | boolean | (unset) | When present, the mobile drawer is open. Default closed; on desktop (≥992px) the rail is pinned open by CSS regardless of this attribute. Reflected by the sidebarOpen JS property. |
JS Properties
| Property | Type | Description |
|---|
sidebarOpen | boolean | Reflected from the sidebar-open boolean attribute. Set true/false to expand/collapse the sidebar programmatically. |
onToggleSidebar | ((open: boolean) => void) | null | Optional callback invoked on every toggle alongside the tc-toggle-sidebar CustomEvent. Default null. |
Events
| Event | Detail | Description |
|---|
tc-toggle-sidebar | { open: boolean } | Fired when the user toggles the sidebar via the button or keyboard shortcut. |
tc-sidebar-open | — | Fired alongside tc-toggle-sidebar when a user-driven toggle opens the sidebar. No detail — bind one direction without reading detail.open. |
tc-sidebar-close | — | Fired alongside tc-toggle-sidebar when a user-driven toggle closes the sidebar. No detail. |
Slots
| Slot | Description |
|---|
| (default) | Main content — rendered inside <main class="tc-dashboard-layout__content">. |
navbar-left | Content injected to the right of the sidebar-toggle button in the navbar. |
navbar-right | Content pushed to the far right of the navbar. |
brand | Logo / wordmark at the top of the sidebar, above the menu. |
sidebar-menu | Scrollable menu region (middle of the sidebar). |
sidebar-panel | Pinned panel at the bottom of the sidebar (user info, version badge, etc.). |
Accessibility
- The navbar is a
<nav role="navigation" aria-label="Application navigation">.
- The sidebar is an
<aside role="navigation" aria-label="Sidebar navigation">.
- The toggle
<button> carries aria-expanded (updates in-place on toggle) and aria-controls tied to the sidebar's id.
- The toggle is reachable by Tab; Enter/Space activate it natively.
- Ctrl+B (Cmd+B) toggles the sidebar and Escape closes it from anywhere on the page; the handler is removed in
disconnectedCallback.
- Tapping the dimmed backdrop closes the mobile drawer.
- A closed mobile drawer is
inert + aria-hidden and goes visibility: hidden after the slide-out transition completes — it leaves the tab order and the accessibility tree (a translated-off drawer would otherwise keep its buttons focusable). At ≥992px the pinned rail is never inert.
- Closing while focus sits inside the drawer hands focus back to the toggle button (instead of dropping it on
body).
- While the mobile drawer is open, Tab is trapped inside it — it is a modal surface over the dimmed content.
- Touch targets: the toggle is
44px × 44px under @media (pointer: coarse).
prefers-reduced-motion disables the sidebar slide transition.
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-dashboard-layout-navbar-height | 3.75rem | Height of the top navbar bar. |
--bs-dashboard-layout-navbar-bg | rgba(255,255,255,0.85) | Navbar background (translucent glass). |
--bs-dashboard-layout-navbar-border | var(--tc-border) | Hairline border below the navbar. |
--bs-dashboard-layout-navbar-color | var(--tc-text) | Text/icon color in the navbar. |
--bs-dashboard-layout-navbar-padding-x | 0.5rem | Horizontal padding on the navbar. |
--bs-dashboard-layout-sidebar-width | 15rem | Expanded width of the sidebar. |
--bs-dashboard-layout-sidebar-bg | var(--tc-surface) | Sidebar background. |
--bs-dashboard-layout-sidebar-border | var(--tc-border) | Hairline borders on sidebar regions. |
--bs-dashboard-layout-sidebar-brand-padding | 0.75rem 1rem | Padding around the brand slot. |
--bs-dashboard-layout-sidebar-panel-padding | 0.75rem 1rem | Padding around the panel slot. |
--bs-dashboard-layout-content-bg | var(--tc-surface-hover) | Background of the content rail. |
--bs-dashboard-layout-toggle-size | 2.25rem | Width and height of the toggle button. |
--bs-dashboard-layout-toggle-hover-bg | var(--tc-surface-muted) | Toggle button hover background. |
--bs-dashboard-layout-overlay-bg | rgba(0,0,0,0.5) | Dimmed backdrop behind the mobile drawer. |
--bs-dashboard-layout-transition | var(--tc-transition-base) | Sidebar slide + backdrop fade transition. |
<tc-dashboard-layout style="height: 100vh">
<div slot="brand">MyApp</div>
<nav slot="sidebar-menu">
<a href="/dashboard">Dashboard</a>
<a href="/settings">Settings</a>
</nav>
<div slot="sidebar-panel">user@example.com</div>
<span slot="navbar-left">/ Dashboard</span>
<div slot="navbar-right"><button>New</button></div>
<main style="padding:1.5rem">Page content here</main>
</tc-dashboard-layout>
<tc-dashboard-layout id="dl"></tc-dashboard-layout>
<script>
document.getElementById('dl').sidebarOpen = false
document.getElementById('dl').onToggleSidebar = open => console.log('sidebar open:', open)
</script>
<tc-dashboard-layout id="dl2" style="height:100vh">
<div slot="brand">App</div>
<nav slot="sidebar-menu"><a href="/">Home</a></nav>
<p style="padding:1rem">Content</p>
</tc-dashboard-layout>
<script>
document.getElementById('dl2').addEventListener('tc-toggle-sidebar', e => {
console.log('sidebar is now', e.detail.open ? 'open' : 'closed')
})
</script>
tc-dashboard-content
Scrollable main content area for the dashboard layout. A pure layout wrapper — no chrome, no elevation. Provides comfortable padding and overflow-y: auto for hosting cards, rows, and section headers inside a dashboard shell.
Tag: tc-dashboard-content
Attributes
None. tc-dashboard-content is a purely structural layout element with no configurable attributes.
JS Properties
None.
Events
None. tc-dashboard-content is a purely presentational layout element.
Slots
| Slot | Description |
|---|
| (default) | Main dashboard content (cards, rows, headers). Rendered inside <div class="tc-dashboard-content-inner"> which sits inside <main class="tc-dashboard-content">. |
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-dashboard-content-bg | var(--tc-surface) | Background of the scroll region. |
--bs-dashboard-content-color | var(--tc-text) | Text color of the content area. |
--bs-dashboard-content-padding | 1.5rem | Inner padding applied to the content container. |
--bs-dashboard-content-max-width | none | Optional max-width cap for the inner container. |
<tc-dashboard-content>
<h2>Overview</h2>
<tc-basic-card title="Revenue" value="$12,400"></tc-basic-card>
</tc-dashboard-content>
<tc-dashboard-content style="--bs-dashboard-content-max-width: 960px">
<p>Content centred within 960px.</p>
</tc-dashboard-content>
tc-dashboard-sidebar
Vertical sidebar shell for dashboard layouts. Arranges three named slot regions — brand (top), menu (scrollable middle), panel (pinned bottom) — in a fixed-width column separated from the content by a 1px hairline on its trailing edge. No shadow, no border-radius; slate neutrals carry the design.
Tag: tc-dashboard-sidebar
Attributes
None. tc-dashboard-sidebar is a purely structural layout element with no configurable attributes.
JS Properties
None.
Events
None. tc-dashboard-sidebar is a purely presentational layout element.
Slots
| Slot | Description |
|---|
brand | Top branding area (logo, wordmark, app name). Rendered inside <div class="tc-dashboard-sidebar-brand"> at the top of the sidebar with a bottom hairline. |
menu | Primary scrollable navigation list. Rendered inside <nav class="tc-dashboard-sidebar-menu"> which flex-grows to fill available space with overflow-y: auto. |
panel | Bottom panel (e.g. user account, version, footer links). Rendered inside <div class="tc-dashboard-sidebar-panel"> pinned at the bottom with a top hairline divider. |
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-dashboard-sidebar-width | 15rem | Fixed width of the sidebar column. |
--bs-dashboard-sidebar-bg | var(--tc-surface-hover) | Background of the sidebar. |
--bs-dashboard-sidebar-color | var(--tc-text) | Default text color. |
--bs-dashboard-sidebar-border | var(--tc-border) | Color of the trailing-edge hairline and region dividers. |
--bs-dashboard-sidebar-brand-padding | 1rem | Padding inside the brand region. |
--bs-dashboard-sidebar-menu-padding | 0.5rem 0 | Padding inside the menu region. |
--bs-dashboard-sidebar-panel-padding | 0.75rem 1rem | Padding inside the panel region. |
--bs-dashboard-sidebar-panel-border | var(--tc-border) | Color of the panel's top hairline divider. |
<tc-dashboard-sidebar>
<div slot="brand">MyApp</div>
<nav slot="menu">
<a href="/dashboard">Dashboard</a>
<a href="/settings">Settings</a>
</nav>
<div slot="panel">user@example.com</div>
</tc-dashboard-sidebar>
<tc-dashboard-sidebar style="--bs-dashboard-sidebar-width: 11rem">
<div slot="brand">Narrow</div>
<nav slot="menu"><a href="/">Home</a></nav>
</tc-dashboard-sidebar>
tc-container
Responsive fixed-width container.
Attributes
| Attribute | Type | Default | Description |
|---|
fluid | boolean | false | Full-width fluid container |
size | sm|md|lg|xl|xxl | — | Max-width breakpoint |
<tc-container></tc-container>
<tc-container fluid></tc-container>
<tc-container size="lg"></tc-container>
tc-row
Grid row wrapper.
Attributes
| Attribute | Type | Default | Description |
|---|
cols | number | — | Default column count |
cols-sm/md/lg/xl/xxl | number | — | Responsive column count |
gutter | 0|1|2|3|4|5 | — | Gutter size |
align | start|center|end|baseline|stretch | — | Align items |
justify | start|center|end|between|around|evenly | — | Justify content |
<tc-row cols="3" gutter="3">
<tc-col>A</tc-col>
<tc-col>B</tc-col>
<tc-col>C</tc-col>
</tc-row>
tc-col
Grid column.
Attributes
| Attribute | Type | Default | Description |
|---|
span | 1–12|auto | auto | Column span |
span-sm/md/lg/xl/xxl | 1–12|auto | — | Responsive span |
offset | 0–11 | — | Offset columns |
order | first|last|0–5 | — | Order |
<tc-col span="6">Half width</tc-col>
<tc-col span="12" span-md="6">Full then half</tc-col>
tc-safe-area
Layout wrapper that insets its content by the device's env(safe-area-inset-*) CSS environment variables (the areas carved out by notches, rounded corners, and home indicators). An optional extra attribute adds a uniform extra padding on top of the env insets on every side. No visible chrome — purely structural.
Tag: tc-safe-area
Attributes
| Attribute | Type | Default | Description |
|---|
extra | string | — | Extra padding added uniformly on all four sides on top of env(safe-area-inset-*). Accepts any CSS length string (16px, 1rem, 5%, …). When omitted only the env insets are applied (zero on most desktop browsers). |
JS Properties
| Property | Type | Description |
|---|
extra | string | Mirrors the extra attribute. Set to '' to remove the override. |
Events
None. tc-safe-area is a purely structural layout element.
Slots
| Name | Description |
|---|
| (default) | Content to render inside the safe-area padding. Children are direct descendants of the host element. |
CSS custom properties
| Property | Default | Description |
|---|
--bs-safe-area-extra | 0px | The extra padding value. Overridden by the element's inline style when the extra attribute is set; override it directly via style if you need richer control. |
<tc-safe-area>
<div>HUD content</div>
</tc-safe-area>
<tc-safe-area extra="16px">
<div>HUD content with extra padding</div>
</tc-safe-area>
<tc-safe-area extra="1.5rem">
<div>Content</div>
</tc-safe-area>
tc-spacer
Purely structural spacing element. Fills available space in a flex container when size is omitted, or provides a fixed dimension along the given axis when size is set. The element is aria-hidden and carries no visible chrome.
Tag: tc-spacer
Attributes
| Attribute | Type | Default | Description |
|---|
size | string | number | — | Fixed size along the axis. Bare numbers are treated as px (e.g. size="24" → 24px). Any CSS length string is accepted (1rem, 50%, …). When omitted the spacer sets flex: 1 1 auto and expands to fill available space. |
axis | horizontal|vertical | vertical | Axis along which space is applied. vertical sets a fixed/flexible height; horizontal sets a fixed/flexible width. |
Events
None. tc-spacer is a purely presentational element.
Slots
None.
<tc-spacer size="32"></tc-spacer>
<tc-spacer size="1rem" axis="horizontal"></tc-spacer>
<div style="display:flex">
<span>Left</span>
<tc-spacer axis="horizontal"></tc-spacer>
<span>Right</span>
</div>
tc-stack
Flexbox row/column layout primitive. Children are composed along a single axis with configurable gap, alignment, justification, and optional wrapping. The element has no visible chrome; it is purely structural.
Tag: tc-stack
Attributes
| Attribute | Type | Default | Description |
|---|
direction | vertical|horizontal | vertical | Flex axis. vertical → flex-direction: column; horizontal → flex-direction: row. |
gap | string | var(--bs-stack-gap, 0px) | CSS gap between children (e.g. 8px, 1rem). When absent the --bs-stack-gap custom property applies. |
align | string | var(--bs-stack-align, stretch) | align-items value (e.g. center, flex-start). When absent the --bs-stack-align custom property applies. |
justify | string | var(--bs-stack-justify, flex-start) | justify-content value (e.g. space-between, center). When absent the --bs-stack-justify custom property applies. |
wrap | boolean | false | When present enables flex-wrap: wrap. |
inline | boolean | false | When present switches the host to display: inline-flex. |
CSS custom properties (theming contract)
| Property | Default | Description |
|---|
--bs-stack-gap | 0px | Default gap when the gap attribute is absent. |
--bs-stack-align | stretch | Default align-items when the align attribute is absent. |
--bs-stack-justify | flex-start | Default justify-content when the justify attribute is absent. |
Events
None. tc-stack is a purely structural element.
Slots
Default slot — all child elements are laid out in the flex container.
<tc-stack gap="8px">
<div>Item A</div>
<div>Item B</div>
<div>Item C</div>
</tc-stack>
<tc-stack direction="horizontal" justify="space-between" align="center">
<span>Left</span>
<span>Right</span>
</tc-stack>
<tc-stack direction="horizontal" gap="8px" wrap>
<div>Alpha</div>
<div>Beta</div>
<div>Gamma</div>
</tc-stack>
<p>
Before
<tc-stack inline direction="horizontal" gap="4px" align="center">
<span>A</span><span>B</span>
</tc-stack>
after.
</p>
tc-grid
CSS-grid layout primitive. Set a column and/or row count, a gap, and a uniform cell size; children are laid out directly as grid items. Purely structural — no visible chrome, no shadows, no border-radius. All cosmetics flow through --bs-grid-* custom properties.
Responsive (mobile-first). Every layout attribute has per-breakpoint variants — columns-{bp}, rows-{bp}, gap-{bp}, cell-size-{bp} where {bp} is sm | md | lg | xl | xxl (576 / 768 / 992 / 1200 / 1400 px). The bare attribute is the base/mobile value; each breakpoint variant overrides it from that viewport up, exactly like tc-col's span-{bp}. The breakpoint cascade is resolved in pure CSS media queries — there is no JS resize listener, so it reflows for free and is SSR-safe.
Tag: tc-grid
Attributes
| Attribute | Type | Default | Description |
|---|
columns | string | number | — | Number of equal-width columns. Produces grid-template-columns: repeat(<columns>, <cell-size>). When omitted, no explicit column track is set. |
rows | string | number | — | Number of equal-height rows. Produces grid-template-rows: repeat(<rows>, <cell-size>). When omitted, no explicit row track is set. |
gap | string | number | 0 | Gap between cells. Bare numbers are treated as px (e.g. gap="8" → 8px); any CSS length string is accepted (1rem, 0.5em, …). |
cell-size | string | number | 1fr | Size of each track in the repeat(). Bare numbers are treated as px (e.g. cell-size="64" → 64px); any CSS grid track size is accepted (1fr, 64px, minmax(0, 1fr), …). |
columns-{bp} | string | number | — | Per-breakpoint column count ({bp} = sm/md/lg/xl/xxl). Overrides columns from that breakpoint up. |
rows-{bp} | string | number | — | Per-breakpoint row count. Overrides rows from that breakpoint up. |
gap-{bp} | string | number | — | Per-breakpoint gap. Overrides gap from that breakpoint up. |
cell-size-{bp} | string | number | — | Per-breakpoint track size. Overrides cell-size (for both column and row tracks) from that breakpoint up. |
JS Properties
| Property | Type | Description |
|---|
columns | string | Reflects the columns attribute. |
rows | string | Reflects the rows attribute. |
gap | string | Reflects the gap attribute. |
cellSize | string | Reflects the cell-size attribute. |
Events
None. tc-grid is a purely presentational element.
Slots
Default slot — the grid items (any elements). They are laid out directly; the element does not wrap or re-render them.
Theming custom properties
| Property | Default | Description |
|---|
--bs-grid-template-columns | none | Resolved base column template; written by the element when columns is set. |
--bs-grid-template-rows | none | Resolved base row template; written by the element when rows is set. |
--bs-grid-gap | 0 | Resolved base gap; written by the element when gap is set. |
--bs-grid-template-columns-{bp} | — | Per-breakpoint column template; written only when that breakpoint overrides columns/cell-size. Falls back through smaller breakpoints to the base var. |
--bs-grid-template-rows-{bp} | — | Per-breakpoint row template; written only when that breakpoint overrides rows/cell-size. |
--bs-grid-gap-{bp} | — | Per-breakpoint gap; written only when that breakpoint overrides gap. |
<tc-grid columns="3" gap="8">
<div>1</div><div>2</div><div>3</div>
<div>4</div><div>5</div><div>6</div>
</tc-grid>
<tc-grid columns="1" columns-md="2" columns-lg="4" gap="8" gap-md="16">
<div>1</div><div>2</div><div>3</div><div>4</div>
</tc-grid>
<tc-grid columns="4" cell-size="64px" gap="1rem">
<div>A</div><div>B</div><div>C</div><div>D</div>
</tc-grid>
<div style="background: var(--tc-border)">
<tc-grid columns="3" gap="1">
<div style="background: var(--tc-surface)">cpu</div>
<div style="background: var(--tc-surface)">mem</div>
<div style="background: var(--tc-surface)">disk</div>
</tc-grid>
</div>
tc-anchor
Layout primitive that absolutely positions its slotted content at a corner, edge, or the centre of the nearest positioned ancestor. Purely structural — no visible chrome, no shadows, no border-radius. Set position: relative (or any non-static positioning) on the parent so the anchor pins to it.
Tag: tc-anchor
Attributes
| Attribute | Type | Default | Description |
|---|
position | top-left|top|top-right|left|center|right|bottom-left|bottom|bottom-right | top-left | Where the content is pinned within the positioned ancestor. Edge positions (top, left, center, …) centre along the cross-axis via transform. An absent or unrecognised value falls back to top-left. |
inset | string | number | 0px | Offset from the anchored edges. Bare numbers are treated as px (e.g. inset="8" → 8px); any CSS length string is accepted (1rem, 12px, …). Ignored along centred axes. |
JS Properties
| Property | Type | Description |
|---|
position | AnchorPosition | Reflects the position attribute; returns top-left when absent/invalid. |
inset | string | Reflects the inset attribute; returns 0px when absent. |
Events
None. tc-anchor is a purely presentational layout element.
Slots
| Slot | Description |
|---|
| (default) | Content to position. Rendered as light-DOM children of the absolutely-positioned host. |
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-anchor-inset | 0px | Edge offset consumed by every position. Set automatically from the inset attribute; can also be themed directly. |
<div style="position: relative">
<tc-anchor position="top-right" inset="8">
<tc-badge variant="danger">New</tc-badge>
</tc-anchor>
</div>
<div style="position: relative; height: 200px">
<tc-anchor position="center">
<span>Centred</span>
</tc-anchor>
</div>
<div style="position: relative">
<tc-anchor>
<span>Corner</span>
</tc-anchor>
</div>
tc-aspect-ratio-box
Layout primitive that holds its slotted content at a fixed intrinsic aspect ratio (e.g. 16:9). Uses the modern CSS aspect-ratio property with a padding-bottom percentage fallback for older engines. Purely structural — no visible chrome, no shadows, sharp corners. Constrain the width (e.g. via max-width or a grid column) and the height follows the ratio automatically.
Tag: tc-aspect-ratio-box
Attributes
| Attribute | Type | Default | Description |
|---|
ratio | string | 16 / 9 | Width/height ratio. Accepts the CSS aspect-ratio syntax (16 / 9) or a colon pair (16:9). The fallback percentage is derived automatically. |
JS Properties
| Property | Type | Description |
|---|
ratio | string | Reflects the ratio attribute; returns 16 / 9 when absent. Setting an empty/falsy value removes the attribute (restoring the default). |
Events
None. tc-aspect-ratio-box is a purely presentational layout element.
Slots
| Slot | Description |
|---|
| (default) | Content to size. Rendered into the inner .tc-aspect-ratio-box__content wrapper, which fills the ratio box. |
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-aspect-ratio-box-ratio | 16 / 9 | The applied aspect ratio. Set automatically from the ratio attribute; can also be themed directly. |
--bs-aspect-ratio-box-fallback | 56.25% | padding-bottom percentage used by the no-aspect-ratio fallback. Derived automatically from ratio. |
--bs-aspect-ratio-box-bg | transparent | Background fill of the box. |
--bs-aspect-ratio-box-border | 0 | Border shorthand for the box (e.g. 1px solid var(--tc-border)). |
<tc-aspect-ratio-box style="max-width: 480px">
<iframe src="https://www.youtube.com/embed/…" style="width:100%;height:100%"></iframe>
</tc-aspect-ratio-box>
<tc-aspect-ratio-box ratio="4:3" style="max-width: 360px">
<img src="photo.jpg" alt="" style="width:100%;height:100%;object-fit:cover">
</tc-aspect-ratio-box>
<tc-aspect-ratio-box ratio="1 / 1" style="max-width: 220px">
<div>1 : 1</div>
</tc-aspect-ratio-box>
tc-gilded-frame
Hairline-framed surface that wraps its slotted content. Styled to the toolcase voice — sharp corners, slate neutrals, 1px hairline borders, no decorative shadow. The three tones map onto the neutral ramp: dark = ink fill with light text, leather = muted slate fill, transparent = no fill with a hairline border only.
Tag: tc-gilded-frame
Attributes
| Attribute | Type | Default | Description |
|---|
tone | 'dark' | 'leather' | 'transparent' | dark | Surface treatment. dark = ink fill / light text; leather = muted slate fill; transparent = borderless fill, hairline border, inherited text color. Unknown values fall back to dark. Reflected as a class on the host. |
padding | 'none' | 'sm' | 'md' | 'lg' | 'xl' | md | Inner padding step (0 / 0.5rem / 1rem / 1.5rem / 2rem). Unknown values fall back to md. |
JS Properties
| Property | Type | Description |
|---|
tone | GildedFrameTone | Reflects the tone attribute; returns dark when absent or invalid. |
padding | GildedFramePadding | Reflects the padding attribute; returns md when absent or invalid. |
Events
None. tc-gilded-frame is a purely presentational wrapper.
Slots
| Slot | Description |
|---|
| (default) | Content to frame. Rendered into the inner .tc-gilded-frame-content wrapper. |
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-gilded-frame-bg | var(--tc-ink) | Surface fill. Overridden per tone (dark ink, leather muted slate, transparent). |
--bs-gilded-frame-color | rgba(248, 250, 252, 0.92) | Text color. Overridden per tone. |
--bs-gilded-frame-border-color | rgba(255, 255, 255, 0.1) | Hairline border color. Overridden per tone. |
--bs-gilded-frame-border-width | 1px | Border width. |
--bs-gilded-frame-padding | 1rem | Inner padding. Set by the padding attribute's modifier class. |
<tc-gilded-frame>
Framed content.
</tc-gilded-frame>
<tc-gilded-frame tone="leather" padding="lg">
<h5>Framed panel</h5>
<p>Any markup can be slotted inside.</p>
</tc-gilded-frame>
<tc-gilded-frame tone="transparent" padding="sm">
Hairline outline only.
</tc-gilded-frame>
tc-resizable-panel
Two-pane layout with a draggable divider, localStorage persistence, and keyboard resizing. The first two element children become pane A and pane B. Drag the divider to resize, focus it and use the arrow keys to nudge the split, Home/End to jump to the min/max, or double-click to reset to defaultSizes. Sizes are percentages summing to 100; the host should be given an explicit size by its container.
Tag: tc-resizable-panel
Attributes
| Attribute | Type | Default | Description |
|---|
direction | horizontal|vertical | horizontal | horizontal = side-by-side panes with a vertical divider; vertical = stacked panes with a horizontal divider. |
min-size | number | 10 | Minimum size of either pane, as a percentage (clamped to 0–49). Neither pane can be dragged below this. |
storage-key | string | — | When set, the current split is persisted to localStorage[storage-key] (as a [a, b] JSON tuple) on every settled change and restored on load. |
JS Properties
| Property | Type | Description |
|---|
defaultSizes | [number, number] | The initial [a, b] split (percentages, default [50, 50]). Used when there is no persisted value, and as the double-click reset target. |
sizes | [number, number] | Get or set the current [a, b] split. Setting clamps to min-size and patches the panes without a full re-render. |
onResize | ((sizes: [number, number]) => void) | null | Optional callback fired when the divider settles (drag end, keyboard, double-click). Mirrors the tc-resize event. |
Events
| Event | Detail | Description |
|---|
tc-resize | { sizes: [number, number] } | Fired when the divider settles after a drag, keyboard nudge, or double-click reset. |
Slots
The default slot's first two element children are placed into pane A and pane B respectively. Any further children are ignored.
Accessibility
The divider is role="separator" with aria-orientation (matching direction), tabindex="0", and aria-valuenow/aria-valuemin/aria-valuemax reflecting pane A's percentage. Focus is always visible; reduced motion is honoured.
<tc-resizable-panel direction="horizontal" min-size="15" storage-key="editor-split">
<div>Pane A — first child</div>
<div>Pane B — second child</div>
</tc-resizable-panel>
<script>
const el = document.querySelector('tc-resizable-panel')
el.defaultSizes = [60, 40]
el.addEventListener('tc-resize', e => console.log(e.detail.sizes))
</script>
tc-scroll-area
Scrollable container with configurable max dimensions and scroll axis. The slotted children are placed into an inner content element; overflow is applied to the wrapper based on axis. Sharp corners and a thin slate scrollbar (Firefox scrollbar-* + WebKit ::-webkit-scrollbar); no outer border by default — wrap it yourself, or switch on the optional 1px hairline via --bs-scroll-area-border.
Tag: tc-scroll-area
Attributes
| Attribute | Type | Default | Description |
|---|
axis | x|y|both | y | Which axis can scroll. y → overflow-y:auto; overflow-x:hidden; x → overflow-x:auto; overflow-y:hidden; both → overflow:auto. |
max-height | number | CSS length | — | Maximum height of the scrollable area. A bare number is treated as px (max-height="240" → 240px); any other value (60vh, 100%, 20rem) passes through unchanged. Applied inline only when provided. |
max-width | number | CSS length | — | Maximum width of the scrollable area, with the same number→px resolution. Applied inline only when provided. |
JS Properties
| Property | Type | Description |
|---|
axis | 'x' | 'y' | 'both' | Reflects the axis attribute. |
maxHeight | string | number | null | Reflects the max-height attribute (number coerced to a string). |
maxWidth | string | number | null | Reflects the max-width attribute (number coerced to a string). |
Events
None.
Slots
The default slot holds the scrollable content; it is moved into the inner .tc-scroll-area-content element and preserved across re-renders.
Accessibility
When the content overflows, the scroll container receives tabindex="0" so it is keyboard-scrollable (arrow keys, Page Up/Down); the tabindex is removed automatically when content fits. Native scrolling is always intact. Focus is visible (:focus-visible outline), the scrollbar uses the strong-border slate token for contrast, and reduced motion is honoured.
<tc-scroll-area axis="y" max-height="240" style="border:1px solid var(--tc-border)">
<p>Long content…</p>
<p>…that overflows vertically.</p>
</tc-scroll-area>
<tc-scroll-area axis="both" max-height="60vh" max-width="100%">
<div style="width:900px">Wide and tall content scrolls on both axes.</div>
</tc-scroll-area>
tc-artboard-backdrop
Decorative full-bleed backdrop surface for staging artwork, previews, or hero content. Styled to the web-components design system — the slate neutral ramp, sharp corners (border-radius: 0), and a 1px hairline. Purely structural: slotted children are projected as light-DOM children of the host; no shadow root, no shadow (this is a base surface, not an overlay tier).
Tag: tc-artboard-backdrop
Attributes
| Attribute | Type | Default | Description |
|---|
kind | dark|scene|parch | dark | Surface tone. dark → ink surface with inverse (white) text; scene → muted slate well with a hairline; parch → light paper surface with a hairline. An absent or unrecognised value falls back to dark. |
padding | none|sm|md|lg|xl | md | Inner padding step (0, 0.75rem, 1.5rem, 2.5rem, 4rem). An absent or unrecognised value falls back to md. |
JS Properties
| Property | Type | Description |
|---|
kind | ArtboardBackdropKind | Reflects the kind attribute; returns dark when absent/invalid. |
padding | ArtboardBackdropPadding | Reflects the padding attribute; returns md when absent/invalid. |
Events
None. tc-artboard-backdrop is a purely presentational surface.
Slots
| Slot | Description |
|---|
| (default) | Content staged on the backdrop. Rendered as light-DOM children of the host. |
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-artboard-backdrop-bg | var(--tc-ink) | Surface background (overridden per kind). |
--bs-artboard-backdrop-color | var(--tc-text-inverse) | Text colour (overridden per kind). |
--bs-artboard-backdrop-border-color | transparent | Hairline border colour (overridden per kind). |
--bs-artboard-backdrop-radius | 0 | Corner radius — sharp by mandate. |
--bs-artboard-backdrop-padding | 1.5rem | Inner padding (set by the padding step). |
<tc-artboard-backdrop>
<h3>Featured artwork</h3>
</tc-artboard-backdrop>
<tc-artboard-backdrop kind="scene" padding="xl">
<img src="preview.png" alt="preview">
</tc-artboard-backdrop>
<tc-artboard-backdrop kind="parch" padding="none">
<canvas></canvas>
</tc-artboard-backdrop>
tc-theme
Theming host element — the --tc-* token override container. Every tc-* component drives its cosmetics through --bs-<component>-* custom properties whose defaults resolve to the design-system --tc-* tokens (e.g. --bs-panel-bg: var(--tc-surface)). Wrapping a subtree in <tc-theme> and re-pointing those tokens on it therefore re-skins every nested component at once — no per-component overrides needed. The default tokens are already applied globally at :root, so components are themed out of the box; reach for tc-theme only when you want a subtree to differ from the ambient skin.
tc-theme is display: contents — it adds no layout box. Inherited properties (the --tc-* / --bs-* custom properties, plus color and font-family) pass straight through to descendants, but box properties (background, padding, border) will not paint because there is no box. For a themed backdrop, wrap the content in tc-artboard-backdrop or set the background on your own container.
Two ways to theme a subtree:
- Named theme via the
name attribute — opt into a bundled skin. default is the product (slate) voice applied globally; dungeon (gilded fantasy), aurora (dark "production-AI"), sunshine (warm citrus boutique), neon (dark synthwave / cyberpunk, dual magenta + cyan accents) and blueprint (light vector-blueprint, rounded corners) are opt-in skins that stay inert until a tc-theme wrapper requests them. Each named skin is scoped under tc-theme[name="…"] (a plain wrapper carrying [data-tc-theme="…"] is matched too). The dungeon, aurora, sunshine, neon and blueprint skins reference display fonts (Cinzel / EB Garamond for dungeon; Orbitron / Ubuntu Mono for neon; Space Grotesk / Chakra Petch for blueprint) that are not bundled — load them on the host page for the full look; all degrade to system serifs/sans.
- Ad-hoc token overrides — set
--tc-* (or the finer-grained --bs-<component>-*) custom properties directly on the tc-theme element via style or a class. Because the tokens inherit through the display: contents box, every descendant component picks them up.
Every named theme additionally ships nine accent variants selected with the variant attribute: ocean (blue / cyan), forest (green / lime), ember (orange / gold), royal (violet / magenta), mint (teal / mint), rose (rose / pink), crimson (red / coral), indigo (indigo / periwinkle) and slate (steel / silver). A variant swaps only the primary and secondary accent colours (and their derived hovers, soft tints, glows, gradients, focus rings and link colours) — canvas, surfaces, text ramp, semantic status colours and the theme's structure stay untouched: <tc-theme name="blueprint" variant="ocean"> (or [data-tc-theme="blueprint"][data-tc-variant="ocean"] on a plain wrapper).
Tag: tc-theme
Attributes
| Attribute | Type | Default | Description |
|---|
name | default|dungeon|aurora|sunshine|neon|blueprint | — | Selects a bundled named theme for the wrapped subtree. Absent → the subtree inherits the ambient (global :root) theme. Unrecognised values simply match no theme scope, so the subtree keeps the inherited skin. |
variant | ocean|forest|ember|royal|mint|rose|crimson|indigo|slate | — | Accent variant of the named theme — swaps only the primary and secondary accent colours (plus derived hovers, tints, glows, focus rings and links). Requires name; absent → the theme's base accents. |
JS Properties
| Property | Type | Description |
|---|
name | string | Reflects the name attribute. Returns '' when absent. Setting a truthy value writes the attribute; setting ''/falsy removes it. |
variant | string | Reflects the variant attribute. Returns '' when absent. Setting a truthy value writes the attribute; setting ''/falsy removes it. |
Events
None.
Slots
| Slot | Description |
|---|
| (default) | The themed subtree. Rendered in place (the host is display: contents, so children participate in the parent's layout directly — no projection, no wrapper box). |
CSS Custom Properties
tc-theme defines no custom properties of its own — it is a passthrough host for the design-system tokens. Override any --tc-* token (or --bs-<component>-* contract variable) on the element to re-skin its descendants. Common roots: --tc-surface, --tc-surface-muted, --tc-border, --tc-text, --tc-text-muted, --tc-accent, --tc-app-accent, --tc-success/--tc-info/--tc-warning/--tc-danger, --tc-font-sans, --tc-font-mono.
Ergonomics floor tokens (defined at :root, overridable like any --tc-* token):
| Token | Default | Description |
|---|
--tc-min-touch-target | 44px | Coarse-pointer hit-area floor — pagination, tables, icon buttons, sliders, form controls, and the dashboard toggle resolve their @media (pointer: coarse) hit areas from it (44px = iOS HIG; bump app-side for strict Android 48dp). |
--tc-control-height | 2.375rem | Minimum inner height of text controls; max()'d with the touch target on coarse pointers. |
--tc-font-size-min | 12px | Legibility floor — clamped via max() in badge, eyebrow, and side-nav captions. |
<tc-theme name="dungeon">
<tc-panel bordered>
<tc-panel-header heading="Quest Log"></tc-panel-header>
<tc-button variant="primary">Accept</tc-button>
</tc-panel>
</tc-theme>
<tc-theme name="dungeon" variant="ocean">
<tc-button variant="primary">Accept</tc-button>
</tc-theme>
<tc-theme style="--tc-accent: #7c3aed; --tc-app-accent: #7c3aed; --tc-border: #e9d5ff; --tc-surface-muted: #faf5ff;">
<tc-card>
<tc-button variant="primary">Themed action</tc-button>
<tc-badge variant="primary">New</tc-badge>
</tc-card>
</tc-theme>
<tc-button variant="primary">Default action</tc-button>
Content
tc-accordion
Collapsible accordion container. Wrap tc-accordion-item children inside to build a group where only one item can be open at a time (unless always-open is set).
Attributes
| Attribute | Type | Default | Description |
|---|
flush | boolean | false | Removes borders and rounded corners so the accordion sits edge-to-edge with its parent |
always-open | boolean | false | Allows multiple items to be expanded simultaneously |
<tc-accordion>
<tc-accordion-item header="First item" open>Body of the first item.</tc-accordion-item>
<tc-accordion-item header="Second item">Body of the second item.</tc-accordion-item>
<tc-accordion-item header="Third item">Body of the third item.</tc-accordion-item>
</tc-accordion>
tc-accordion-item
Single panel inside a tc-accordion. Renders a clickable header button and a collapsible body region; default slot content becomes the body.
Attributes
| Attribute | Type | Default | Description |
|---|
header | string | — | Text for the clickable header button |
open | boolean | false | Expanded state of this item |
Events: tc-show, tc-shown, tc-hide, tc-hidden
<tc-accordion>
<tc-accordion-item header="Details" open>
<p>Any body content goes here.</p>
</tc-accordion-item>
</tc-accordion>
tc-action-header
Flex header row with slotted title content on the left and a row of action buttons on the right. Dispatches a tc-exec custom event when an action button is clicked.
Attributes
| Attribute | Type | Default | Description |
|---|
disabled | boolean | false | Disables all action buttons and suppresses tc-exec |
Properties
| Property | Type | Default | Description |
|---|
actions | ActionHeaderAction[] | [] | Array of action descriptors (see below) |
onExec | (key: string) => void | null | null | Optional callback invoked in addition to the tc-exec event |
ActionHeaderAction shape
| Field | Type | Required | Description |
|---|
key | string | yes | Unique identifier, returned in tc-exec detail |
label | string | no | Button label text |
icon | string | no | Lucide icon name in PascalCase (e.g. "Pencil", "Trash2") |
variant | string | no | Bootstrap button variant — default "secondary" |
disabled | boolean | no | Disables this individual action |
Events
| Event | Detail | Description |
|---|
tc-exec | { key: string } | Fired (bubbles) when a non-disabled action button is clicked |
Slots
The element's light-DOM children are placed in the left content region (.tc-action-header-content). Use headings, text, or badges here.
<tc-action-header id="hdr">
<strong>Users</strong>
</tc-action-header>
<script>
const el = document.getElementById('hdr')
el.actions = [
{ key: 'add', label: 'Add', icon: 'Plus' },
{ key: 'export', label: 'Export', icon: 'Download' },
{ key: 'delete', label: 'Delete', variant: 'danger' },
]
el.addEventListener('tc-exec', e => console.log('exec', e.detail.key))
</script>
tc-action-items
Dropdown menu button with keyboard-accessible items positioned relative to the trigger. Fires tc-action-click when an item is chosen and closes the menu.
Attributes
| Attribute | Type | Default | Description |
|---|
label | string | 'Actions' | Trigger button text |
Properties
| Property | Type | Default | Description |
|---|
items | ActionItem[] | [] | Array of menu item descriptors (see below) |
onActionClick | (key: string) => void | null | null | Optional callback invoked in addition to tc-action-click |
ActionItem shape
| Field | Type | Required | Description |
|---|
key | string | yes | Unique identifier returned in tc-action-click detail |
label | string | yes | Item label text |
icon | string | no | Lucide icon name in PascalCase (e.g. "Pencil", "Trash2") |
disabled | boolean | no | Disables this item (skipped in keyboard navigation) |
danger | boolean | no | Renders the item in danger/destructive color |
divider | boolean | no | Renders a horizontal separator instead of a button |
Events
| Event | Detail | Description |
|---|
tc-action-click | { key: string } | Fired (bubbles) when a non-disabled item is activated |
Slots: none
<tc-action-items id="menu" label="Actions"></tc-action-items>
<script>
const el = document.getElementById('menu')
el.items = [
{ key: 'edit', label: 'Edit', icon: 'Pencil' },
{ key: 'share', label: 'Share', icon: 'Share2' },
{ key: 'div', label: '', divider: true },
{ key: 'delete', label: 'Delete', icon: 'Trash2', danger: true },
]
el.addEventListener('tc-action-click', e => console.log('chosen', e.detail.key))
</script>
tc-action-row-list
Vertical list of action rows, each with a title, optional description, and a CTA button on the right. Fires tc-action-click when a row's button is clicked. Disabled rows render at reduced opacity and are not interactive.
Attributes
| Attribute | Type | Default | Description |
|---|
outline | boolean | false | Render CTA buttons in outline style (btn-outline-*) |
trailing-icon | string | — | Lucide icon name on CTA buttons (omit = chevron-right; none or empty = suppress) |
Properties
| Property | Type | Default | Description |
|---|
actions | ActionRow[] | [] | Array of row descriptors (see below) |
onActionClick | (key: string) => void | null | null | Optional callback invoked in addition to tc-action-click |
ActionRow shape
| Field | Type | Required | Description |
|---|
key | string | yes | Unique identifier returned in tc-action-click detail |
title | string | yes | Row title (Inter 500, --tc-text) |
description | string | no | Secondary line below the title (--tc-text-muted, 12.5px) |
label | string | no | CTA button label text |
icon | string | no | Lucide icon name in PascalCase shown before the label (e.g. "Download") |
variant | string | no | Bootstrap button variant — default "secondary" |
disabled | boolean | no | Disables the CTA; row renders at 50% opacity with pointer-events: none |
Events
| Event | Detail | Description |
|---|
tc-action-click | { key: string } | Fired (bubbles) when a non-disabled row's CTA button is clicked |
Slots: none
<tc-action-row-list id="settings-list"></tc-action-row-list>
<script>
const el = document.getElementById('settings-list')
el.actions = [
{ key: 'profile', title: 'Profile', description: 'Manage your account', label: 'Edit' },
{ key: 'billing', title: 'Billing', description: 'Update payment info', label: 'Manage' },
{ key: 'security', title: 'Security', description: 'Passwords and 2FA', label: 'Configure', variant: 'danger' },
{ key: 'api', title: 'API keys', description: 'Manage tokens', label: 'View', disabled: true },
]
el.addEventListener('tc-action-click', e => console.log('clicked', e.detail.key))
</script>
<tc-action-row-list id="export-list" outline trailing-icon="ArrowRight"></tc-action-row-list>
<tc-action-row-list id="plain-list" trailing-icon="none"></tc-action-row-list>
tc-alert
Contextual feedback message.
Attributes
| Attribute | Type | Default | Description |
|---|
variant | primary|secondary|success|danger|warning|info|light|dark | primary | Color variant |
dismissible | boolean | false | Show close button |
Methods: close()
Events: tc-closed
<tc-alert variant="success" dismissible>Operation completed.</tc-alert>
tc-banner
Status banner with a leading icon, body content, an optional action slot or CTA link, and optional localStorage-backed dismissal. Emits tc-dismiss when closed. Uses role="status" (or role="alert" for the error variant) for screen reader announcements.
Tag: tc-banner
Preset alias: tc-announcement-bar is a persistent-announcement preset of tc-banner — role="region", no auto per-variant icon, and the legacy persist-dismiss-key / icon-name attribute names (mapping to storage-key / icon). Everything below applies identically.
Attributes
| Attribute | Type | Default | Description |
|---|
variant | info|warning|success|error|announce | info | Color/tone variant. Selects the left border color, background tint, and default icon. announce is the cyan brand-announcement tone (used by the announcement-bar preset). |
dismissible | boolean | false | Show a close button. Clicking it hides the banner and fires tc-dismiss. |
storage-key | string | — | localStorage key. On connect, if the key is already stored as "dismissed", the banner hides immediately. Clicking close writes the flag so dismissal persists across reloads. (The tc-announcement-bar preset reads persist-dismiss-key instead.) |
icon | string | — | Kebab-case lucide icon name (e.g. "bell", "rocket"). Overrides the per-variant default icon (info → info, warning → triangle-alert, success → circle-check, error → circle-x). (The preset reads icon-name instead, and has no per-variant default.) |
cta-label | string | — | CTA link text. Rendered as a .tc-banner-cta link only when cta-href is also set. |
cta-href | string | — | CTA link URL. Rendered only when cta-label is also set. |
class | string | — | Extra classes applied directly to the host element. |
JS Properties
| Property | Type | Description |
|---|
onDismiss | (() => void) | null | Optional callback fired alongside the tc-dismiss event when the banner is dismissed. |
variant / dismissible / storageKey / iconName / ctaLabel / ctaHref | — | Reflect the matching attributes. storageKey / iconName adapt to the tag's attribute names. |
Events
| Event | Detail | Description |
|---|
tc-dismiss | — | Fired (bubbles, composed) when the close button is clicked. |
Slots
| Slot | Description |
|---|
| (default) | Banner body text / HTML. Rendered inside .tc-banner-content. |
icon | Optional leading icon element. Overrides the icon / icon-name lucide fallback when present. |
action | Optional action element (button, link). Rendered inside .tc-banner-action on the right side of the banner. |
CSS Custom Properties
| Property | Default | Description |
|---|
--bs-banner-padding-x | 1rem | Horizontal padding. |
--bs-banner-padding-y | 0.85rem | Vertical padding. |
--bs-banner-font-size | 0.9rem | Banner text size. |
--bs-banner-gap | 0.75rem | Gap between icon, content, action, and close button. |
--bs-banner-border-width | 4px | Left border width. |
--bs-banner-border-color | var(--tc-info) | Left border and icon color (set by variant). |
--bs-banner-bg | gradient | 135° tinted gradient fill (set by variant). |
--bs-banner-color | emphasis | Dark emphasis text color (set by variant). |
--bs-banner-icon-size | 1rem | Width/height of the leading icon SVG. |
--bs-banner-cta-color | border color | CTA link color (falls back to the variant border color). |
--bs-banner-close-size | 28px | Size of the dismiss button (44px on coarse pointer devices). |
<tc-banner>New documentation is available — check it out.</tc-banner>
<tc-banner variant="error">Failed to connect to the server.</tc-banner>
<tc-banner variant="info" icon="bell">You have 3 unread notifications.</tc-banner>
<tc-banner variant="info">
A new version is available.
<tc-button slot="action" variant="primary" size="sm">Update now</tc-button>
</tc-banner>
<tc-banner variant="warning" dismissible storage-key="my-app-banner">
Maintenance window on Sunday 02:00 UTC.
</tc-banner>
<tc-announcement-bar variant="announce" icon-name="megaphone"
cta-label="Learn more" cta-href="/changelog">
Toolcase v3 is now open-source.
</tc-announcement-bar>
tc-avatar
Circular user avatar. Displays an image, 1–2 initials derived from a name, or a placeholder user glyph when neither is provided. Optionally shows a colour-coded status dot at the bottom-right corner.
Tag: tc-avatar
Attributes
| Attribute | Type | Default | Description |
|---|
src | string | — | Image URL. When present, renders an <img>. Falls back to initials or placeholder on load error. |
alt | string | — | Image alt / accessible label override. Defaults to the name attribute when absent. |
name | string | — | Display name. Used to derive 1–2 initials when src is absent. Also used as the host's aria-label. |
size | small|default|large | default | Avatar diameter: 28 px / 40 px / 56 px. |
status | online|offline|busy|away | — | When set, renders a coloured dot at the bottom-right corner with an aria-label for the status value. |
variant | primary|secondary|success|danger|warning|info | secondary | Background tint for initials / placeholder. Soft tint + dark emphasis text. |
JS Properties
Each attribute is reflected as a same-named JS property (getter/setter).
Events
None.
Slots
None.
CSS custom properties (theming)
| Property | Default | Description |
|---|
--bs-avatar-size | 40px | Diameter of the circle. Overridden per size. |
--bs-avatar-font-size | 0.875rem | Initials font size. |
--bs-avatar-bg | var(--tc-surface-muted) | Background colour (overridden by variant). |
--bs-avatar-color | var(--tc-text-muted) | Initials / glyph colour (overridden by variant). |
--bs-avatar-status-size | 9px | Status dot diameter. |
--bs-avatar-status-ring | 2px | Width of the tc-surface ring around the status dot. |
--bs-avatar-status-offset | 2px | Distance from bottom/right edge to the status dot. |
<tc-avatar src="https://example.com/photo.jpg" name="Alice Johnson"></tc-avatar>
<tc-avatar name="Bob Smith" variant="primary"></tc-avatar>
<tc-avatar variant="secondary"></tc-avatar>