Theming

Theme every c2n component from a handful of design tokens with @c2n/theme, or reach for any component variable directly.

Every c2-* component is styled exclusively through its own CSS custom properties, named --c2-<component>__<part>[__<state>]--<property> (about 700 in total, listed on each component’s API tab). They stay fully independent: you can theme an app by setting those variables one by one, without any other layer.

@c2n/theme adds a convenience layer on top: a small set of design tokens and a generated base theme that maps every component variable onto them. Set ~35 tokens and every component follows; any component variable you set yourself still wins.

Installation

npm install @c2n/theme
import '@c2n/theme/theme.css' // tokens + base theme, once at the app root

Then override the tokens that differ in your brand:

:root {
  --c2-theme--color-primary: #7c3aed;
  --c2-theme--color-primary-hover: #6d28d9;
  --c2-theme--radius-md: 10px;
  --c2-theme--font-family: 'Inter', system-ui, sans-serif;
}

How the layers stack

  1. Tokens--c2-theme--<name>. Shared colours, type, shape, focus ring, motion and elevation. Defined by tokens.css, overridden by your app.
  2. Base themebase.css sets each component variable to var(--c2-theme--<token>, <component default>) on :root. It is generated from the components’ manifests, so coverage grows with the library.
  3. Component variables — the fine-grained escape hatch. --c2-button__container--background-color: crimson on an element or class beats the base theme because the base theme only sets :root values.
  4. Application variant components — repeated looks wrapped in your own components (a class, a wrapper, or a Lit subclass) built from the two layers above.
/* What base.css emits for one variable: the token first, the component's own default as the innermost fallback. */
--c2-list--border-top: var(--c2-theme--border, var(--c2-theme--border-width, 1px) solid var(--c2-theme--color-outline, #e4e4e7));

Composite tokens such as --c2-theme--border and --c2-theme--focus-ring wrap their primitives, so you can set the whole shorthand (--c2-theme--border: 2px dashed hotpink) or just the colour inside it (--c2-theme--color-outline).

Dark mode

tokens.css defines light values on :root and dark values under [data-theme='dark'] and the .c2-dark class, with the OS preference as fallback when no data-theme attribute is present. data-theme="light" / .c2-light opts a subtree out.

<html data-theme="dark">

  <section class="c2-light">Light island inside a dark page</section>
</html>

Because the selectors are not tied to the root element, any subtree can be inverted: a preview canvas, a chat panel, a sidebar.

Bring your own tokens

If your app already has a token system, skip tokens.css, load only base.css, and bridge your tokens onto the --c2-theme--* names. Dark mode then follows your own switch automatically.

@import '@c2n/theme/base.css';

:root {
  --c2-theme--color-primary: var(--brand-600);
  --c2-theme--color-on-primary: var(--brand-on-600);
  --c2-theme--color-surface: var(--surface);
  --c2-theme--color-on-surface: var(--text);
  --c2-theme--color-outline: var(--border);
  --c2-theme--radius-md: var(--radius);
  --c2-theme--font-family: var(--font-sans);
  --c2-theme--focus-ring: 2px solid var(--brand-600);
}

Tokens

TokenLightDarkDescription
--c2-theme--color-primary#0265dc#5aa3ffAccent colour: filled buttons, selected states, focused borders, links.
--c2-theme--color-primary-hover#0154b8#7bb6ffAccent colour on hover.
--c2-theme--color-primary-active#01469a#9cc9ffAccent colour while pressed.
--c2-theme--color-on-primary#ffffff#032a5cText and icons drawn on the accent colour.
--c2-theme--color-primary-container#edf1fe#0f2d5cSoft accent surface, e.g. a selected list item.
--c2-theme--color-surface#ffffff#18181bDefault surface of inputs, lists, cards, dialogs.
--c2-theme--color-surface-container-low#fafafa#1f1f23Slightly raised surface: side navigation, read-only fields, subtle hover.
--c2-theme--color-surface-container#f4f4f5#27272aHover surface for rows and icon buttons.
--c2-theme--color-on-surface#18181b#f4f4f5Primary text colour.
--c2-theme--color-on-surface-variant#71717a#a1a1aaSecondary text, placeholders, icons, supporting text.
--c2-theme--color-outline#d4d4d8#3f3f46Resting border colour of inputs, cards and lists.
--c2-theme--color-outline-variant#e4e4e7#27272aHairline dividers and light borders.
--c2-theme--color-outline-strong#a1a1aa#52525bBorder colour on hover.
--c2-theme--color-error#dc2626#f87171Error borders, error text, required indicators.
--c2-theme--color-scrimrgba(9, 9, 11, 0.45)rgba(0, 0, 0, 0.6)Backdrop behind dialogs and drawers.
--c2-theme--color-inverse-surface#18181b#f4f4f5High-contrast surface, e.g. tooltips.
--c2-theme--color-on-inverse-surface#fafafa#18181bText drawn on the inverse surface.
--c2-theme--font-familyunsetsameFont family of every component. Unset by default so components inherit the page font.
--c2-theme--font-size-sm12pxsameSmall text: supporting text, tooltips, descriptions, timestamps.
--c2-theme--font-size-md14pxsameBody text: buttons, inputs, list items, dialog content.
--c2-theme--font-weight-medium500sameMedium weight: buttons, headers, tooltips.
--c2-theme--font-weight-semibold600sameSemibold weight: dialog titles, list headings, avatars.
--c2-theme--radius-sm4pxsameSmall radius: checkboxes, link buttons, inline controls.
--c2-theme--radius-md6pxsameDefault radius: buttons, inputs, list items, tooltips.
--c2-theme--radius-lg8pxsameLarge radius: cards, panels, popovers.
--c2-theme--radius-xl14pxsameExtra large radius: dialogs.
--c2-theme--radius-full999pxsamePill / circle radius: avatars, icon buttons.
--c2-theme--border-width1pxsameWidth of every themed border.
--c2-theme--borderunsetsameComplete resting border shorthand. Unset by default: it falls back to `border-width solid color-outline`.
--c2-theme--focus-ring2px solid rgba(2, 101, 220, 0.4)2px solid rgba(90, 163, 255, 0.5)Focus-visible outline of every component.
--c2-theme--disabled-opacity0.38sameOpacity of disabled components.
--c2-theme--motion-scale1sameMultiplier applied to every transition and animation duration (0 disables motion).
--c2-theme--shadow-md0 8px 24px rgba(24, 24, 27, 0.08)0 8px 24px rgba(0, 0, 0, 0.45)Shadow of popovers, menus and tooltips.
--c2-theme--shadow-lg0 24px 60px rgba(0, 0, 0, 0.25)0 24px 60px rgba(0, 0, 0, 0.6)Shadow of dialogs and drawers.

What the base theme covers

Colours, backgrounds, borders, radii, focus rings, disabled opacity, font sizes and weights, durations and shadows with a concrete default are mapped. Sizes, paddings, gaps, identity colours (avatar fallbacks), status colours and the code viewer’s syntax theme stay component-specific by design, and variables without a default (they inherit) are left untouched. @c2n/theme/report.json lists, per package, what is mapped and what is not.

escAccordionConnected, animated panels with shared borders and single or multiple expansion.LayoutAvatarImage, initials or icon for a person, with status dot and badge.Data displayBadgeTinted pill for status text, counts and dots, optionally pinned to a corner of another element.Data displayBreadcrumbNavigation trail of link buttons with separators, a current page and optional collapsing.NavigationButtonThemeable button with slots for text, prefix, suffix and running icons.ButtonsButton GroupAttached buttons with shared borders, optionally a segmented control with single or multiple selection.ButtonsCardGroups related content and actions on a single bordered surface.LayoutChat InputAuto-growing message box with a send button for chat interfaces.ChatChat MessageOne chat bubble with avatar, title, timestamp and message body.ChatCheckboxNative checkbox behaviour in a quiet, themeable box with an opt-in hover layer.InputsCode ViewerSyntax-highlighted code with line numbers, copy button and dark mode, powered by shiki.Data displayColor AreaTwo-dimensional area for picking saturation and value of a colour.InputsColor SelectColour swatch that opens a full picker built from area and slider.InputsColor SliderHorizontal slider for choosing a hue from 0 to 360.InputsCopy ButtonButton that copies text to the clipboard — the element it sits in, another element by id, or a literal string.ButtonsDetailsCollapsible disclosure built on native details and summary.LayoutIcon ButtonRound, hoverable button wrapping a slotted SVG icon.ButtonsKbdKeyboard key label for shortcuts and command hints, with the semantics of the native kbd element.Data displayLabelCaption that names and activates the control referenced by its for attribute, with a required marker.InputsLink ButtonText-styled control for link and navigation actions, rendered as a real anchor or a button.NavigationListVertical list container with single or multiple selection.Data displayList ItemSelectable row with icon slots, used on its own or as the option of list and select.Data displayMenuCommands, links, checkboxes and submenus in a popover anchored to a trigger.NavigationModalDialog built on the native dialog element: focus trap, backdrop, Escape, title, body and footer.FeedbackNavigation MenuSite navigation bar whose triggers open panels of links below the header.NavigationOverlayAnchored popup built on the browser Popover API, positioned with floating-ui.FeedbackPaginationPage navigation in three layouts: numbered pages, a simple page status, or a table-footer row with rows-per-page.NavigationProgressLinear progress bar, indeterminate or filling to a value, with an optional label and count.FeedbackRadioRadio options built on native inputs, grouped into one value with keyboard navigation.InputsSelectDropdown that pairs a themeable trigger with an anchored list of c2-list-item options.InputsSeperatorHorizontal or vertical rule with an optional label, for dividing content and toolbars.LayoutSheetDialog pinned to an edge of the screen, for content that complements the page rather than interrupting it.FeedbackSide NavResponsive navigation drawer beside the page: pushes the content on large screens, slides over it with a backdrop on small ones.NavigationSkeletonPlaceholder block standing in for content that has not arrived, in three shapes and three animations.FeedbackSliderRange input with a themeable track, thumb, step ticks and value bubble.InputsSpinnerCircular progress indicator, indeterminate or showing a value, with optional text.FeedbackSwitchOn/off toggle on a native switch input, with label, description and thumb icons.InputsTableVirtualized data grid with declarative columns, sorting, selection, pinning and resizing.Data displayTabsTab strip that shows one content panel at a time.NavigationText FieldSingle-line input with icon slots, clear button, helper and error text, and a character counter.InputsTextareaMultiline text input with resizing, helper and error text, and a character counter.InputsToastNotification cards and a manager for stacked, queued notifications with independent lifetimes.FeedbackTooltipContextual hint shown when its target is hovered or focused, rendered in the top layer.FeedbackFeather Icons287 open-source Feather icons, one web component each.IconsMat Icon2,234 Material Icons ligatures rendered through a single element.IconsPhosphor Icons1,512 flexible icons in six weights, one web component each.IconsThemingTheme every c2n component from a handful of design tokens with @c2n/theme, or reach for any component variable directly.GuideUsing c2n in an applicationThe workflow behind every c2n app: load the theme once, use c2-* tags directly, and turn every repeated pattern into a small variant or composed component.Guide