.github/skills/working-with-pm-design-kit/SKILL.md
This skill provides guidance for using the Packmind UI component library (@packmind/ui). It should be used when building or modifying frontend UI with PM-prefixed components, working with Chakra UI in the Packmind codebase, or when questions arise about available components, theming, or layout patterns. Triggers on mentions of PM components, @packmind/ui, Chakra UI usage, design kit, or frontend component implementation.
npx skillsauth add PackmindHub/packmind working-with-pm-design-kitInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The Packmind design kit (@packmind/ui) is a component library built on top of Chakra UI v3. All components are prefixed with PM and provide a consistent, themed API across the application. The source lives in packages/ui/.
Import pattern: Always import from @packmind/ui, never from Chakra UI directly.
import { PMButton, PMBox, PMHeading, PMText } from '@packmind/ui';
Before reaching for a raw <div> or Chakra primitive, check if a PM component exists. Consult references/component-catalog.md for the full inventory organized by category.
PMBox, PMVStack, PMHStack, PMFlex, or PMGrid.PMHeading (with level prop for semantic h1–h6) or PMText (with variant and color).PMButton with the appropriate variant: primary for main actions, secondary/ghost for secondary, danger for destructive.PMInput, PMTextArea, PMSelect, PMCheckbox, PMSwitch, or PMRadioGroup.pmToaster for transient messages, PMAlert for inline messages, PMConfirmationModal for destructive confirmations.PMDialog for modals, PMPopover for contextual info, PMDrawer for side panels.PMEmptyState with title, description, icon, and an action button.PMSkeleton for content areas, PMSpinner for inline indicators.PMColorSwatch to display a color sample.Several PM components use Chakra's compound pattern with dot notation. Always use the compound API — do not try to reconstruct these with standalone elements.
// Dialog
<PMDialog.Root open={isOpen} onOpenChange={setIsOpen}>
<PMDialog.Backdrop />
<PMDialog.Positioner>
<PMDialog.Content>
<PMDialog.Header>
<PMDialog.Title>Title</PMDialog.Title>
<PMDialog.CloseTrigger />
</PMDialog.Header>
{/* body */}
</PMDialog.Content>
</PMDialog.Positioner>
</PMDialog.Root>
// Accordion
<PMAccordion.Root>
<PMAccordion.Item value="section-1">
<PMAccordion.ItemTrigger>Section 1</PMAccordion.ItemTrigger>
<PMAccordion.ItemContent>Content here</PMAccordion.ItemContent>
</PMAccordion.Item>
</PMAccordion.Root>
// Timeline
<PMTimeline.Root>
<PMTimeline.Item>
<PMTimeline.Separator>
<PMTimeline.Indicator />
<PMTimeline.Connector />
</PMTimeline.Separator>
<PMTimeline.Content>
<PMTimeline.Title>Event</PMTimeline.Title>
<PMTimeline.Description>Details</PMTimeline.Description>
</PMTimeline.Content>
</PMTimeline.Item>
</PMTimeline.Root>
// Tabs (compound pattern — preferred for flexible tab layouts)
<PMTabsCompound.Root defaultValue="tab1">
<PMTabsCompound.List>
<PMTabsCompound.Trigger value="tab1">First Tab</PMTabsCompound.Trigger>
<PMTabsCompound.Trigger value="tab2">Second Tab</PMTabsCompound.Trigger>
</PMTabsCompound.List>
<PMTabsCompound.Content value="tab1">First content</PMTabsCompound.Content>
<PMTabsCompound.Content value="tab2">Second content</PMTabsCompound.Content>
</PMTabsCompound.Root>
Key compound components: PMDialog, PMAccordion, PMTimeline, PMCarousel, PMCopiable, PMSelect, PMMenu, PMTreeView, PMTabs, PMTabsCompound.
Always wrap overlays (dialogs, popovers, drawers) inside PMPortal to escape stacking context issues.
Use gap on stacks/grids for consistent spacing between children — never use margin on individual children to create gaps.
<PMVStack gap="4">
<PMHeading level="h2">Title</PMHeading>
<PMText>Description</PMText>
</PMVStack>
For layouts that fill the viewport, use height="100vh" on the root, flex="1" on the expanding section, and minHeight={0} on flex children that need to scroll.
Use PMGrid with gridTemplateColumns for multi-panel layouts:
<PMGrid gridTemplateColumns="minmax(240px, 270px) 1fr minmax(280px, 320px)">
<PMBox>Sidebar</PMBox>
<PMBox>Main</PMBox>
<PMBox>Detail</PMBox>
</PMGrid>
Use PMPage for full-page layouts with title, breadcrumbs, actions, and optional sidebar. Use PMPageSection for collapsible content sections within a page.
Use PMHeading with the level prop for semantic HTML (h1–h6) and color for emphasis:
<PMHeading level="h1" color="primary">Page Title</PMHeading>
<PMHeading level="h3" color="secondary">Section Title</PMHeading>
Available colors: primary, secondary, tertiary, faded, primaryLight, secondaryLight, tertiaryLight.
Use PMText with variant for size and color for emphasis:
<PMText variant="body" color="primary">Main content</PMText>
<PMText variant="small" color="secondary">Supporting text</PMText>
Variants: body, body-important, small, small-important.
Colors: primary, secondary, tertiary, error, faded, warning, success, primaryLight, secondaryLight, tertiaryLight.
Use semantic tokens — never hardcode hex colors or raw Chakra palette values.
| Category | Tokens | Usage |
|----------|--------|-------|
| Background | background.primary, .secondary, .tertiary, .faded | Surface colors (dark to light) |
| Text | text.primary, .secondary, .tertiary, .faded, .error, .warning, .success | Text contrast levels |
| Border | border.primary, .secondary, .tertiary | Border contrast levels |
Use the semantic color names for status indicators:
green palette or text.successred palette or text.errororange palette or text.warningblue palette<PMButton variant="danger">Delete</PMButton>
<PMText color="error">Validation failed</PMText>
<PMBadge colorPalette="green">Active</PMBadge>
PMInput provides label, error state, and helper text out of the box:
<PMInput
label="Project name"
value={name}
onChange={(e) => setName(e.target.value)}
error={errors.name}
helperText="Must be unique within the organization"
maxLength={255}
/>
Group related fields with PMFormContainer:
<PMFormContainer maxWidth="400px" centered>
<PMInput label="Name" />
<PMInput label="Email" />
<PMButton variant="primary" type="submit">Save</PMButton>
</PMFormContainer>
Show errors directly on inputs via the error prop — this adds a red border and displays the message below the field. Disable submit buttons during async operations with isLoading.
import { pmToaster } from '@packmind/ui';
pmToaster.create({
type: 'success', // 'success' | 'error' | 'warning' | 'info' | 'loading'
title: 'Saved',
description: 'Your changes have been saved.',
closable: true,
action: { label: 'Undo', onClick: handleUndo }, // optional
});
<PMConfirmationModal
trigger={<PMButton variant="danger">Delete</PMButton>}
title="Delete project?"
message="This action cannot be undone."
confirmText="Delete"
confirmColorScheme="red"
onConfirm={handleDelete}
isLoading={isDeleting}
/>
<PMAlert.Root status="warning">
<PMAlert.Indicator />
<PMAlert.Title>Attention</PMAlert.Title>
<PMAlert.Description>This feature is in beta.</PMAlert.Description>
</PMAlert.Root>
<PMEmptyState
title="No standards yet"
description="Create your first coding standard to get started."
icon={<LuInbox />}
>
<PMButton variant="primary">Create Standard</PMButton>
</PMEmptyState>
Icons come from react-icons/lu (Lucide icon set). Import with the Lu prefix:
import { LuTrash2, LuPlus, LuChevronDown } from 'react-icons/lu';
<PMButton><LuPlus /> Add Item</PMButton>
<PMIconButton variant="ghost"><LuTrash2 /></PMIconButton>
Control size via fontSize or size props on the icon element.
Use Chakra's responsive object syntax with breakpoints base, sm, md, lg, xl:
<PMBox
display={{ base: 'none', md: 'flex' }}
width={{ base: '100%', lg: '60%' }}
padding={{ base: '4', md: '6' }}
/>
Mobile-first approach: base styles apply to all sizes, then override at larger breakpoints.
| Variant | Usage |
|---------|-------|
| primary | Main action on the page (one per view) |
| secondary | Important but not primary actions |
| tertiary | Low-emphasis actions |
| outline | Alternative to secondary with border emphasis |
| ghost | Minimal actions (toolbar buttons, inline actions) |
| success | Positive confirmations |
| warning | Caution-required actions |
| danger | Destructive actions (delete, remove) |
@packmind/ui exports several hooks for common UI patterns:
Manages sorting state for PMTable. Returns sortKey, sortDirection, handleSort, and getSortDirection:
import { useTableSort } from '@packmind/ui';
const { sortKey, sortDirection, handleSort, getSortDirection } = useTableSort({
defaultSortKey: 'name',
defaultSortDirection: 'asc',
});
<PMTable columns={columns} data={data} onSort={handleSort} />
useFilter for filtering collectionsuseListCollection for managing list data (useful with PMSelect, PMCombobox)useToken for accessing design tokens programmaticallyimport { pmUseToken, pmUseListCollection } from '@packmind/ui';
@chakra-ui/react directly — always use @packmind/ui wrappers.PMDialog, PMDrawer, or PMPopover with PMPortal.PMSkeleton or PMSpinner.as="h1" on headings — use the level prop on PMHeading for semantic HTML.component-catalog.md — Full inventory of all PM components and hooks with props, organized by category.tools
Record polished UI demo videos and screenshots of a running web app using Playwright MCP — for client deliverables, release notes, feature walkthroughs, or bug repros. Produces an HD WebM video with chapter markers, a mandatory animated cursor overlay, and a mandatory subtitle bar that narrates each step (positioned deliberately so it never masks the UI being demonstrated), plus full-page screenshots at each step. Use this whenever the user asks to "record a demo", "create a screencast", "make a UI walkthrough video", "document this feature with video", "show the client how X works", "capture screenshots of the app", or anything similar — even when the user only says "make a video" or "take screenshots" in the context of a running frontend. Also use it when the user wants to demonstrate a workflow, generate marketing-quality footage of an app, or produce repeatable visual documentation.
tools
The canonical recipe for starting, checking, and stopping the Packmind local dev stack with Docker Compose — the single source of truth other skills and the Michel agent defer to. Covers bringing the full stack (PostgreSQL, Redis, NestJS API, React/Vite frontend on :4200, MCP server, nginx) up in the background, the init services (dependency install + TypeORM migrations) you must wait on, the critical host-port trap that the API on container port 3000 is NOT exposed to the host and must be reached via the frontend Vite proxy at localhost:4200/api/v0, confirming the API and frontend are actually serving before you depend on them, the persistent-volume gotcha that leaves stale Postgres schema and node_modules behind between runs, building the CLI, and tearing everything down so no container is left blocking the run. Use this whenever you need Packmind running locally — to verify a change, record a UI or CLI demo, hit the API, seed data, or reproduce a bug — and whenever you are about to start or stop `docker compose`. If you are an autonomous agent (e.g. Michel) that started the stack, you MUST use the teardown half before finishing. Prefer this over running `nx serve` on the host for anything that needs the real, containerized stack.
tools
Best practices for creating GitHub pull requests that include inline images — CLI terminal screenshots (from cli-demo-recorder), UI screenshots/videos (from ui-demo-recorder), or any other visual artifact. Use this skill whenever opening or updating a PR that has visual artifacts to embed, or when images aren't rendering in a PR description. Also use it when asked "how do I add screenshots to a PR", "why isn't my image showing", or "embed a demo in the PR".
tools
--- name: michel-create-packmind-dataset description: Seed a local Packmind instance with a realistic dataset — one organization populated with standards, commands, and skills — so an autonomous agent can exercise its own changes against lifelike data instead of an empty app. Use this whenever you need populated Packmind data to verify a change end-to-end: reproducing a bug that only shows with existing artifacts, recording a UI/CLI demo that needs content on screen, smoke-testing a new endpoint