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

@northtech/climbalong-ui

v0.0.8

Published

Climbalong's Angular components and Tailwind CSS theme.

Readme

Climbalong UI

Climbalong's Angular components and Tailwind CSS v4 theme. The design follows the spectator site: its palette, Inter and Graphik typography, 4px control corners and component behavior.

| Component | API | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Button | button[caBtn], a[caBtn] (CaButton) | | Select | <ca-select> (CaSelect, CaOptionTemplate, CaSelectedTemplate) | | Multi select | <ca-multi-select> (CaMultiSelect) | | Autocomplete | <ca-autocomplete> (CaAutoComplete) | | Segmented control | <ca-segmented-control> (CaSegmentedControl) | | Text input | input[caTextInput] (CaTextInput) | | Number input | input[caNumberInput] (CaNumberInput) | | Input number | <ca-input-number> (CaInputNumber) | | Input group | <ca-input-group>, <ca-input-group-addon> (CaInputGroup, CaInputGroupAddon) | | Textarea | textarea[caTextarea] (CaTextarea) | | Checkbox | input[caCheckbox] (CaCheckbox) | | Radio | input[caRadio] (CaRadio) | | Switch | input[caSwitch] (CaSwitch) | | Switch values | input[caSwitch][trueValue], input[caSwitch][falseValue] (CaSwitchValues) | | Slider | <ca-slider> (CaSlider) | | Tag | <ca-tag> (CaTag) | | Badge | <ca-badge>, [caBadge] (CaBadge, CaBadgeOverlay) | | Avatar | <ca-avatar>, <ca-avatar-group> (CaAvatar, CaAvatarGroup) | | Divider | <ca-divider> (CaDivider) | | Skeleton | <ca-skeleton> (CaSkeleton) | | Loader | <ca-loader> (CaLoader) | | Message | <ca-message> (CaMessage) | | Toast | <ca-toaster> (CaToaster), toast() | | Card | [caCard] (CaCard) | | Accordion | <ca-accordion>, <ca-accordion-item> (CaAccordion, CaAccordionItem) | | Tooltip | [caTooltip] (CaTooltip) | | Popover | <ca-popover>, button[caPopoverTriggerFor] (CaPopover, CaPopoverTrigger) | | Dialog | <ca-dialog>, ng-template[caDialogHeader] (CaDialog, CaDialogHeader) | | Drawer | <ca-drawer>, ng-template[caDrawerHeader] (CaDrawer, CaDrawerHeader) | | Menu | <ca-menu>, [caMenuTriggerFor] (CaMenu, CaMenuTrigger, CaMenuItem) | | Nav menu | <ca-nav-menu>, <ca-nav-menu-item>, ng-template[caNavMenuContent], a[caNavMenuLink] (CaNavMenu, CaNavMenuItem, CaNavMenuContent, CaNavMenuLink) | | Split button | <ca-split-button> (CaSplitButton) | | Tabs | <ca-tabs>, <ca-tab-list>, [caTab], <ca-tab-panel>, <ca-tab-router-panel> (CaTabs, CaTabList, CaTab, CaTabPanel, CaTabContent, CaTabRouterPanel) | | Color picker | <ca-color-picker> (CaColorPicker) | | Date picker | <ca-date-picker> (CaDatePicker) | | Table | <ca-table>, ng-template[caTableHeader\|caTableBody\|caTableFooter\|caTableEmpty], th[caSortableColumn], tr[caSelectableRow], <ca-table-checkbox>, <ca-table-header-checkbox> (CaTable and friends) | | Row expansion | ng-template[caTableRowExpansion], button[caRowToggler] (CaTableRowExpansion, CaRowToggler) | | Paginator | <ca-paginator> (CaPaginator) |

Components are added as applications need them.

Text inputs also support native types such as email, password, search, tel, url, date, and time. The other form directives style native controls, so labels, form submission, keyboard behavior, and number constraints continue to work. Checkbox and switch use type="checkbox"; radio uses type="radio"; number uses type="number".

Develop

nvm use
npm ci
npm start

Open http://127.0.0.1:4311, or set another port with PORT=4321 npm start. The playground documents each component and the foundations, with search (⌘K or /), live examples and an API reference generated from api.json. npm test builds the playground and runs the browser suite.

Install

npm install @northtech/climbalong-ui

The package requires Angular 21.2 or later (core, common, forms and router), Angular CDK, @ng-icons/core with @ng-icons/phosphor-icons, ngx-sonner v3.1, and Tailwind CSS v4.

Import the theme after Tailwind in your global stylesheet:

@import 'tailwindcss';
@import '@northtech/climbalong-ui/theme.css';

The theme registers the package's compiled components as a Tailwind source. Your build therefore generates the utility classes the components use, together with your own. It also includes the CDK overlay styles.

Load the bundled Inter font through Angular's styles option, so Angular resolves the font file. Graphik is not bundled; set it up in your project as described in Fonts.

"styles": ["node_modules/@northtech/climbalong-ui/fonts.css", "src/styles.css"]

Import components individually:

import { Component, signal } from '@angular/core';
import { CaButton, CaSelect, type SelectOption } from '@northtech/climbalong-ui';

@Component({
  selector: 'app-results-filter',
  imports: [CaButton, CaSelect],
  template: `
    <ca-select label="Round" size="large" [options]="rounds" [(value)]="round" />
    <button caBtn type="button" size="large" [loading]="saving()">Save</button>
  `,
})
export class ResultsFilter {
  readonly rounds: SelectOption<string>[] = [
    { label: 'Qualification', value: 'qualification' },
    { label: 'Final', value: 'final' },
  ];
  readonly round = signal<string | null | undefined>('qualification');
  readonly saving = signal(false);
}

API conventions

  • severity is a semantic colour: primary, secondary, info, success, warn, error or contrast (the exported Severity type). Badge, message and tag use it; tag also has basic. Card keeps tone, because it colours a surface.
  • variant is a visual style: button (primary, outlined, ghost…), message (default or simple), loader (spinner or dots), tab list (underline, pills or segmented) and accordion item.
  • shape is rectangle or circle (button, avatar, skeleton). A pill tag is rounded.
  • label is always the accessible name. Avatars show initials; buttons take the native aria-label.
  • orientation is horizontal or vertical (divider, slider, segmented control).
  • Text templates use named placeholders, such as {count} in selectedItemsLabel and {first}, {last} and {totalRecords} in the paginator's page report.
  • inputId sets the id of the focusable element, for an external <label for>.
  • button[caBtn] and th[caSortableColumn] are components, so a template reference on them is the component instance rather than the element. CaButton has nativeElement and focus(); use { read: ElementRef } in viewChild for an ElementRef. Other attribute selectors are directives, where a plain #ref is the element.
  • Component defaults are CSS recipes in the components layer (.ca-tag, .ca-message…), so any Tailwind utility you put on a component overrides them: <ca-divider class="my-8" />, <ca-tag class="bg-brand-500">. Popovers, tooltips (caTooltipClass) and menus (panelClass, item styleClass) apply your classes to their panel the same way. Classes are not merged at runtime, so the library has no tailwind-merge dependency.

Date picker

CaDatePicker has no date library: it uses Date and Intl.DateTimeFormat (locale). dateFormat tokens are d/dd, m/mm, M/MM (month name), yy/yyyy (four-digit year) and y (two-digit year). selectionMode is single, range or multiple; showTime adds hours and minutes, and timeOnly inline is a standalone time picker bound to a Date. The calendar header has month and year selects next to the previous and next month buttons; years run from minDate to maxDate, or 100 years either side of the shown year. Typed or pasted text is parsed on Enter or when focus leaves the input. Text that is not an available date in dateFormat is discarded: the value and the input both show the previous date again, however quickly the field is left. An empty input sets the value to null.

Toasts

Add CaToaster to your root component's imports and render <ca-toaster /> once. Call the exported toast function from any component:

import { CaToaster, toast } from '@northtech/climbalong-ui';

toast.success('Registration confirmed.');
toast.error('Results could not be saved.');
toast('Registration removed.', {
  description: 'You can undo this change.',
  action: { label: 'Undo', onClick: () => restoreRegistration() },
});

Toasts use ngx-sonner with rich colors disabled. All statuses have neutral text and a white surface. Status icons are filled Phosphor icons in the 500 shade of the theme's success, info, warning and error colors. The close icon and loading spinner also use Phosphor. The default position is bottom-right, duration is 4000ms, and a close button is enabled. Configure position, duration, closeButton, expand, visibleToasts and offset on CaToaster. The toast API supports descriptions, actions, promises, updates by id and toast.dismiss(). Hovering the toasts or moving focus into one, such as with Tab to its close or action button, pauses every toast's timer; the timers resume once both the pointer and focus have left. The toaster renders in the top layer: while a CaDialog or CaDrawer is open, toasts show above it and its backdrop, in the same corner, and stay clickable.

Tables

CaTable renders your <tr>, <th> and <td> templates inside a native <table>. Bind [caTableBody]="rows" to type let-row; with a bare caTableBody the row is any. Sorting (th[caSortableColumn]), pagination (paginator, rows, rowsPerPageOptions), globalFilter, selection (selectionMode, [(selection)], tr[caSelectableRow], ca-table-checkbox), row expansion (caTableRowExpansion, button[caRowToggler], [(expandedRowKeys)] by dataKey), loading and scrollable are opt-in. The table's paginator is a ca-paginator, which also works on its own for lists that are not tables.

Tabs

Tabs on router links follow the current route, and the closest match is selected. They render in one of two ways:

  • A tablist (the default) for links that switch the content of one view, like in-page tabs. Arrow keys move between tabs, Enter or Space follows a link, and ca-tab-router-panel around the <router-outlet> is the tab panel.
  • Navigation, with nav on the ca-tab-list, for links that lead to separate pages, such as a section's sub-pages. The list is a <nav> named by label, the links have no tab roles, the current one has aria-current="page", and Tab moves between them. A ca-tab-router-panel is optional here; it has no role but still keeps focus in place when the route replaces the focused element.

Both look the same: the selected tab and the current link have the same underline.

<ca-tabs>
  <ca-tab-list nav label="Competition">
    <a caTab value="overview" routerLink=".">Overview</a>
    <a caTab value="results" routerLink="results">Results</a>
  </ca-tab-list>
  <router-outlet />
</ca-tabs>

Tooltips

caTooltipDisabled turns a tooltip off, and one that is open closes. To show a tooltip only at some screen sizes, give caTooltipMedia a media query: caTooltipMedia="(min-width: 64.0625rem)" shows it from the lg breakpoint up. While the query does not match, the tooltip behaves as disabled, closes if it is open and removes its id from the host's aria-describedby. On the server the query is not evaluated.

Tags

dot adds a small leading dot in the tag's text colour, and pulse shows the dot and pulses it, for a live status: <ca-tag severity="error" pulse>Live</ca-tag>. The dot is hidden from screen readers, so the text must name the status. The pulse stops when the user prefers reduced motion.

Icons

Components take icons as Phosphor icon names from @ng-icons/phosphor-icons, for example icon="phosphorTrash". Register the icons you pass with provideIcons in your component or application providers:

import { provideIcons } from '@ng-icons/core';
import { phosphorTrash } from '@ng-icons/phosphor-icons/regular';

@Component({ providers: [provideIcons({ phosphorTrash })] })

Badges and skeletons are hidden from screen readers: badge overlays describe their host through aria-describedby, and a loading region should carry aria-busy.

Overlays

CaPopover opens from any event with toggle($event) or show($event) and anchors to that element, so origin is optional; close it with hide() (close() is deprecated). A programmatic CaMenu.close() only returns focus to the trigger when focus was inside the menu. Size a drawer with --ca-drawer-width (left and right, default 22rem) or --ca-drawer-height (top and bottom). CaDialog's breakpoints input takes { '<max-width>': '<width>' }; the narrowest match wins.

Menus, selects, tooltips and other overlays render next to their trigger as inline popovers, so they keep working inside a modal dialog or drawer.

Forms

Value components (select, multiselect, autocomplete, segmented control, slider, number input, date picker and color picker) have a value model and disabled, invalid and touched inputs. They implement the signal forms control contract, so [formField] binds them directly, including disabled and invalid state.

For ngModel, formControl and formControlName, add CaFormsSupport to your imports next to FormsModule or ReactiveFormsModule:

import { FormsModule } from '@angular/forms';
import { CaFormsSupport, CaSelect } from '@northtech/climbalong-ui';

@Component({
  imports: [FormsModule, CaFormsSupport, CaSelect],
  template: `<ca-select label="Round" [options]="rounds" [(ngModel)]="round" required />`,
})

The native form directives accept invalid too, and show the invalid state automatically for ngModel, reactive forms and signal forms once the field is touched. caTextarea autoResize grows and shrinks with its content. To bind non-boolean values to a switch, import CaSwitchValues next to CaSwitch and set trueValue and falseValue.

A control shows its invalid state when its form control is invalid and touched, or when you set invalid. The native form directives (caTextInput, caTextarea, caNumberInput, caSwitch) use Angular's built-in accessors.

Required fields and descriptions

Select, autocomplete and date picker forward required and describedBy to their focusable control: the select's combobox trigger, and the text input of the autocomplete and date picker. On the native directives, set required and aria-describedby on the element itself.

  • required (boolean, default false) sets aria-required="true" on the control, and removes it when it turns off. It is also on when [formField] binds a field with a required rule, or when a reactive or ngModel control has Validators.required. On its own the input does not validate, and it never sets the native required attribute. Next to ngModel, formControl or formControlName, the required attribute also adds Angular's required validator, as it does on a native input. An inline date picker has no element that can carry aria-required.
  • describedBy (string | null | undefined, default null) takes space-separated element ids, such as a hint and an error message. They go first in the control's aria-describedby, the calendar group's for an inline date picker. When the binding changes, the old ids are removed and ids added by other code stay. null or an empty string removes them all.

Neither input changes the value, emits valueChange or marks the form dirty or touched. label and inputId keep naming the control. Show the error message only while it applies, and include its id only then:

<form [formGroup]="form">
  @let roundError = form.controls.round.invalid && form.controls.round.touched;
  <ca-select
    label="Round"
    [options]="rounds"
    formControlName="round"
    [describedBy]="roundError ? 'round-hint round-error' : 'round-hint'"
  />
  <p id="round-hint">The round you judge.</p>
  @if (roundError) {
  <p id="round-error">Choose a round.</p>
  }
</form>

Here Validators.required on round sets aria-required. Without it in the control's validators, add required to the element.

Options

Select-like components accept any option objects. optionLabel, optionValue and optionDisabled take a property name or a function. Without optionValue, an option with a value property uses it and any other option is its own value; match object values with dataKey or compareWith. Set group with optionGroupLabel and optionGroupChildren for grouped options. Customize option content with <ng-template caOption let-option>. loadingMessage sets the text shown while loading, and inputId sets the trigger's id so an external <label for> can name it.

ca-segmented-control allows deselecting by default (allowEmpty), like PrimeNG's SelectButton: clicking the selected option clears the value to null. Pass [allowEmpty]="false" to always keep one option selected. ca-autocomplete never filters by itself: handle (search) and update [suggestions].

Fonts

The package bundles Inter in fonts.css. It does not bundle Graphik: Graphik is a licensed typeface, so each project installs its own copy of the font files and declares the faces itself.

The theme uses Graphik for text-display-large, text-display-medium and text-heading-large, and for the font-graphik utility. It needs these files:

| File | font-weight | font-style | Used by | | --------------------------- | ------------- | ------------ | ------------------------------------------- | | Graphik-bold.woff2 | 700 | normal | text-heading-large | | Graphik-bold-italic.woff2 | 700 | italic | text-display-large, text-display-medium |

The family name must be exactly Graphik. Without the faces, those styles fall back to the system sans-serif. Add more weights only if your project uses font-graphik with them.

Put the files where your build serves or bundles them, for example src/assets/fonts/, and declare them in a stylesheet that is part of the global styles:

@font-face {
  font-family: 'Graphik';
  src: url('./assets/fonts/Graphik-bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Graphik';
  src: url('./assets/fonts/Graphik-bold-italic.woff2') format('woff2');
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

The playground does exactly this in projects/playground/src/graphik.css.

Prompt for an agent

Copy this into your coding agent to set up Graphik in a project that uses this package:

Set up the Graphik font for @northtech/climbalong-ui in this project.

The library's theme uses font-family 'Graphik' at weight 700, in normal and italic styles, for
text-display-large, text-display-medium and text-heading-large. The package does not ship Graphik.

1. Find the licensed Graphik files in this project: Graphik-bold.woff2 and
   Graphik-bold-italic.woff2. If they are not here, stop and ask me for them. Do not download
   Graphik from the internet.
2. Put them in a folder the build serves or bundles, such as src/assets/fonts/.
3. Create a stylesheet with two @font-face rules: font-family 'Graphik', font-weight 700,
   font-style normal and italic, font-display swap, each pointing at its file with a URL relative
   to the stylesheet.
4. Add the stylesheet to the global styles, after node_modules/@northtech/climbalong-ui/fonts.css,
   which provides Inter.
5. Check it: on a page that uses text-display-large, document.fonts.check('italic 700 32px Graphik')
   must return true after document.fonts.ready.

Theme

The theme replaces Tailwind defaults with Climbalong values:

  • font-sans is Inter. font-inter and font-graphik are also available.
  • The neutral scale is Climbalong's. brand, error, warning, success and info scales are added, each 50–900.
  • shadow-sm, shadow-md and shadow-lg use Climbalong's shadows.
  • The lg breakpoint is 1025px (64.0625rem). xs (400px) and 3xl (1600px) are added.

Motion values are available as --ca-motion-duration (200ms), --ca-motion-feedback-duration (150ms), --ca-motion-drawer-duration (250ms), and the easings --ca-motion-ease, --ca-motion-ease-in-out and --ca-motion-drawer-ease.

Organization colors

Components use the primary colors, not the brand scale. Override them to apply an organization's colors, either in CSS or at runtime on the document element:

:root {
  --color-primary: #1189c6; /* brand-500: borders, focus rings, indicators, icons */
  --color-primary-strong: #1189c6; /* brand-500: fills behind text, primary text on white */
  --color-primary-hover: #0c6592; /* brand-600: hovered fills and primary text */
  --color-primary-subtle: #d0edfb; /* brand-100: soft fills, hovered surfaces */
  --color-primary-subtle-hover: #a6dcf7; /* brand-200 */
  --color-primary-subtle-foreground: #084563; /* brand-700: text on soft fills */
  --color-primary-foreground: #fff; /* text on strong fills */
}

The same colors are available as utilities, for example bg-primary-strong and text-primary-subtle-foreground.

Contrast

Primary fills and primary text use the brand orange. With white it reaches 3.3:1, which meets WCAG AA for large text (18.66px bold or 24px) and for UI such as borders, focus rings and icons, but not the 4.5:1 AA needs for body text. Every other default pair meets 4.5:1:

| Text | Background | Ratio | Used by | | --------------------------- | ----------------------------------------- | --------- | --------------------------------------------------------------- | | primary-foreground | primary-strong / primary-hover | 3.3 / 4.7 | Primary button, tag and badge, selected segmented control, card | | primary-strong | white | 3.3 | Outlined, ghost and link buttons, selected tab, focused select | | primary-subtle-foreground | primary-subtle / primary-subtle-hover | 5.9 / 4.7 | Subtle button, secondary tag, multi-select chips | | neutral-600 | white | 5.3 | Placeholders | | white | error-600 / error-700 | 5.6 / 8.8 | Error button | | white | brand-600, brand-700 | 4.7 / 7.5 | Your own fills behind body text | | white | success-800, info-700, brand-900 | 6.4+ | Your own fills | | neutral-900 | warning-500, success-500, primary | 5.3+ | Your own fills |

Where a product needs AA for body text on primary, set --color-primary-strong to brand-600 and --color-primary-hover to brand-700; every component follows. When you choose organization colors, keep primary at 3:1 or more against white. neutral-500 (3.7:1) is for disabled text only. White on success-500 or warning-500 is about 1.6:1; use dark text on those, or a darker step.

Typography

Each utility sets family, size, line height, weight and style. Sizes are font size / line height in pixels. Display and heading sizes step up from the lg breakpoint.

| Utility | Family | Weight | Size | From lg | | --------------------- | ------- | ---------- | ----- | --------- | | text-display-large | Graphik | 700 italic | 48/56 | 96/104 | | text-display-medium | Graphik | 700 italic | 32/40 | 64/72 | | text-heading-large | Graphik | 700 | 24/32 | 32/40 | | text-heading-medium | Inter | 700 | 20/28 | 24/32 | | text-heading-small | Inter | 700 | 16/24 | 20/28 | | text-title | Inter | 600 | 16/24 | | | text-body | Inter | 400 | 16/24 | | | text-body-small | Inter | 400 | 14/20 | | | text-label | Inter | 600 | 14/20 | | | text-label-small | Inter | 600 | 12/16 | | | text-caption | Inter | 400 | 12/16 | |

Spectator's type-* classes map directly: for example, type-heading-lg is text-heading-large, type-heading-xs is text-title and type-body-md is text-body.

Buttons

| Size | Text button | Circle | Circle icon | | -------- | ----------- | ------ | ----------- | | small | 32px | 36px | 16px | | medium | 36px | 40px | 20px | | large | 40px | 44px | 24px |

Text buttons use Inter 600 at 14/20px, 16px icons, an 8px gap and 4px corners. Outlines are drawn inside the button, so they do not change its size. Variants are primary (default), subtle, outlined, ghost, ghost-black, ghost-white, outlined-white, basic, link and error. Use shape="circle" for icon-only buttons and give them an accessible name.

loading disables the button, marks it busy and replaces its content with a spinner without changing its size. On links, disabled and loading set aria-disabled and block navigation. A natively disabled button drops keyboard focus to the page, so give a button that can turn disabled while focused (a carousel's Next button, a Save button that starts loading) disabledFocusable: it then gets aria-disabled, stays focusable and ignores clicks.

Add an icon with icon (a Phosphor icon name) and iconPos="right"; a button with only an icon becomes square and needs an aria-label. badge shows a count or, with true or '', a dot in the corner.

CaButton is a component with an attribute selector, so a template reference on the element (#save) or viewChild('save') gives the CaButton instance, not an ElementRef. It has nativeElement and focus(), so save.nativeElement.focus() and save.focus() both work; viewChild('save', { read: ElementRef }) still gives the ElementRef. Every non-circle button is at least as wide as it is tall, so icon-only buttons are square whether the icon comes from icon or a projected <ng-icon>.

To restyle a single button, set --ca-btn-bg, --ca-btn-fg and --ca-btn-border, and their --ca-btn-hover-* counterparts.

Using with AI agents

The package ships documentation for coding agents, generated for each version:

  • llms.txt: an index of guides and components. Start here.
  • docs/components/*.md: one file per component, with its import, inputs with types and defaults, outputs, public methods, playground examples and notes.
  • docs/guides/*.md: install, conventions, fonts, forms, icons, overlays, options, theme and typography.
  • llms-full.txt: everything in one file. api.json: the API as JSON.
  • skills/climbalong-ui/SKILL.md: an Agent Skills skill that points to the above. The format is shared by many coding agents, so any agent that supports skills loads it when a task involves the library.

In an application that uses the package, add this line to AGENTS.md, which most coding agents read. Claude Code reads CLAUDE.md instead, so add the line there too, or import the file with @AGENTS.md:

For @northtech/climbalong-ui components, read node_modules/@northtech/climbalong-ui/llms.txt before writing templates, and the component's file in docs/components/ before using it.

Agents that support skills can also load the skill. Link it into the project's skills folder; because it is a link, it follows the installed version. .agents/skills/ is the shared location that agents such as GitHub Copilot, OpenCode and Antigravity read:

mkdir -p .agents/skills
ln -s ../../node_modules/@northtech/climbalong-ui/skills/climbalong-ui .agents/skills/climbalong-ui

Claude Code reads .claude/skills/ instead:

mkdir -p .claude/skills
ln -s ../../node_modules/@northtech/climbalong-ui/skills/climbalong-ui .claude/skills/climbalong-ui

If your agent uses another folder, link the skill there the same way. Where symbolic links are not available, copy the folder instead, and copy it again after each upgrade of the package.

scripts/build-agent-docs.mjs generates these files into dist/climbalong-ui as the last step of npm run build. It reads the API from the published typings, input defaults from the source, descriptions, usage and examples from the playground pages, and guides from this README. A README section named like a playground page (Toasts, Tables) becomes that component's notes; the other sections become guides. To document something for agents, write a JSDoc comment on the class or input, or add a playground example.

Package and verify

For now, increment only the package's patch version: 0.0.1, 0.0.2, 0.0.3, and so on.

npm run pack                    # builds, checks the agent docs and packs the library tarball
npm test                        # builds the playground and runs the browser suite
npm run test:compat -- 21.2.23  # installs the tarball in a separate Angular app and tests it
npm run docs:check              # fails on documentation gaps or docs missing from the tarball
npm run check:format
npm run release                 # runs the tests, checks the docs and publishes dist/climbalong-ui

docs:check fails when a component class has no JSDoc comment, a library folder has no playground page, or a page has no description, usage snippet or example. Publish with npm run release. The published package is dist/climbalong-ui, which has no npm scripts of its own, so npm publish ./dist/climbalong-ui on its own skips the build and the docs check.

The library is compiled with Angular 21.2.23, CDK 21.2.14, TypeScript 5.9 and partial Ivy compilation. The workspace uses Node 22.14 through .nvmrc. The compatibility run covers the minimum supported Angular version and the latest 21.x.