Files
basecoat/AGENTS.md
T

8.4 KiB
Raw Blame History

Basecoat – Agent Guide

What is Basecoat?

Basecoat is a vanilla JavaScript/HTML/CSS port of shadcn/ui components. It reuses shadcn/ui's design patterns and Tailwind CSS classes while maintaining semantic HTML, native browser elements, and full accessibility (ARIA attributes, proper hierarchies, keyboard navigation). The goal is to provide shadcn-quality components without React dependencies.

Quickstart

  • Install deps: npm i
  • Dev docs server: npm run docs:dev
  • Build CSS/JS: npm run build
  • Build static docs: npm run docs:build

What to edit (and what not)

  • Edit sources only:
    • src/css/base/base.css – Shared base layer, tokens, and semantic utility classes
    • src/css/components/*.css – Component CSS classes (Tailwind + custom)
    • src/css/styles/style-*.css – Style-pack overrides (vega, nova, maia, lyra, mira, luma, sera, rhea)
    • src/css/basecoat.css – Aggregate CSS entrypoint
    • src/js/*.js – Individual component JS files (kebab-case, ESM) + basecoat.js (component registry)
    • src/nunjucks/*.njk and src/jinja/*.html.jinja – Component template macros
    • docs/src/components/*.njk – Component documentation pages with examples
    • docs/src/_includes/ – Layout templates, partials, and navigation
  • Do not edit build outputs:
    • packages/*/dist – built CSS/JS bundles
    • _site/ – generated docs site

Project layout

  • Workspaces: root npm workspace with publishable packages in packages/*.
  • Packages:
    • packages/css – built CSS and JS bundles under dist/.
    • packages/cli – CLI; distributed assets live under dist/.
  • Docs site: docs/src (Eleventy); output goes to _site/.

Style system

  • Basecoat keeps its own component class API (btn, dialog, popover, etc.). Do not introduce shadcn cn-* classes into Basecoat source.
  • Shadcn/ui style packs are used as reference material, but must be mapped onto Basecoat markup and selectors.
  • Style-pack architecture is one file per style:
    • src/css/styles/style-vega.css
    • src/css/styles/style-nova.css
    • src/css/styles/style-maia.css
    • src/css/styles/style-lyra.css
    • src/css/styles/style-mira.css
    • src/css/styles/style-luma.css
    • src/css/styles/style-sera.css
    • src/css/styles/style-rhea.css
  • Default aggregate and CDN behavior should resolve to Vega unless explicitly changed.
  • Non-default styles should be selected by importing the relevant style file or using a style-specific bundled asset, not by introducing shadcn internal selector conventions.
  • During migration, each component must be reviewed in two layers:
    • shared/base component CSS
    • style-pack CSS overrides
  • Do not assume existing Basecoat component CSS is still the right baseline. Reassess shared rules vs style-owned rules component by component.
  • When Basecoat differs from upstream shadcn/ui, classify the difference as either intentional or drift, and prefer explicit decisions over silent divergence.

Component migration workflow

For each component migration, follow this sequence:

  1. Review the current Basecoat component CSS and markup/templates first.
  2. Review the latest upstream shadcn/ui base component source for the equivalent component.
  3. Review the relevant sections in all eight upstream style packs (vega, nova, maia, lyra, mira, luma, sera, rhea).
  4. Decide what belongs in shared Basecoat component CSS versus what belongs in style-pack CSS.
  5. Keep Basecoat selectors and markup. Do not port shadcn cn-* selectors directly.
  6. Remove outdated Basecoat visual rules from the shared component CSS if those rules are now style-owned.
  7. Implement or update the component section in:
    • src/css/components/<component>.css
    • src/css/styles/style-vega.css
    • src/css/styles/style-nova.css
    • src/css/styles/style-maia.css
    • src/css/styles/style-lyra.css
    • src/css/styles/style-mira.css
    • src/css/styles/style-luma.css
    • src/css/styles/style-sera.css
    • src/css/styles/style-rhea.css
  8. Document any non-trivial difference from upstream as either:
    • intentional
    • drift fixed
    • deferred
  9. Update the migration tracker entry for the component.
  10. Run npm run build after each component migration.
  11. Manually verify the component in docs for:
  • light and dark mode
  • all supported variants and sizes
  • interaction states (hover, focus, disabled, aria-invalid, open/selected states when relevant)

Per-component file checklist:

  1. Basecoat component CSS: src/css/components/<component>.css
  2. Basecoat templates/macros if relevant: src/nunjucks/*.njk, src/jinja/*.html.jinja
  3. Basecoat docs page: docs/src/components/<component>.njk
  4. Upstream base component: ../_sandbox/shadcn-ui/apps/v4/registry/bases/base/ui/<component>.tsx
  5. Upstream styles: ../_sandbox/shadcn-ui/apps/v4/registry/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css
  6. Basecoat styles: src/css/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css

Conventions

  • ESM everywhere (import/export).
  • Prettier defaults (2 spaces; single quotes per file preference).
  • Filenames for assets: kebab-case (e.g., dropdown-menu.js, basecoat.cdn.css).
  • JS naming: camelCase for vars/functions; PascalCase for DOM classes and exported component names when applicable.

Design Principles

  • Minimal code: Prefer native HTML elements and browser behavior over JavaScript abstractions
  • Accessibility-first: Semantic HTML, proper ARIA attributes, keyboard navigation
  • CSS patterns:
    • Use border for lines/separators (not background on <hr>)
    • Use :is() and :has() for compact selectors
    • Use isolation: isolate to contain z-index stacking contexts
    • Use data-* attributes for styling states, aria-* for accessibility semantics
  • Component patterns:
    • Dialog variants use wrapper classes (e.g., .command-dialog wraps .command)
    • JavaScript components register with window.basecoat.register()
    • Emit custom events for user actions (e.g., command:select)
    • Filter disabled options (aria-disabled="true") from all interactions
  • When NOT to create a component:
    • If it's just composition of existing elements (users can do it with standard HTML/Tailwind)
    • If behavior is fully native (e.g., fieldset, legend)

Adding new components

Before creating a component, ask:

  1. Does shadcn/ui have it? → Port it
  2. Is it pure composition? → Document pattern, no component
  3. Does it need JS behavior? → Create component
  4. Is it just CSS? → Add to basecoat.css, document usage

Examples:

  • Field/Fieldset: CSS-only patterns (no JS component)
  • Empty/Item/Input Group: Pure composition (docs only)
  • Command/Select: JS behavior required (full component)

Common tasks

  • Update a component's CSS/JS: Edit src/css/basecoat.css and/or src/js/*.js.
  • Update a component's CSS/JS: Edit the relevant file in src/css/components/*.css, src/css/styles/style-*.css, and/or src/js/*.js.
  • Update a component template: Edit src/nunjucks/*.njk and/or src/jinja/*.html.jinja.
  • Add a new component: Create JS, CSS, templates, and docs page under docs/src/components/*.njk.
  • Update navigation: Edit files in docs/src/_includes/partials/.

Testing

No automated testing configured. User validates manually via the docs site (npm run docs:dev).

Commits and PRs

  • Commits: concise, imperative (≤72 chars), optional scope.
    • Example: select: prevent change event on init.
  • PRs: include description, linked issues (e.g., Closes #123), and before/after screenshots or code snippets for visual/behavioral changes.
  • Keep PRs focused; update docs examples when components or APIs change.

Environment and tooling

  • Node 18+ recommended.
  • Eleventy config: .eleventy.js.
  • Tailwind via @tailwindcss/cli v4.
  • Build uses Tailwind CLI and terser; both are provided via devDependencies.

Git workflow

  • .gitattributes configures merge strategies for generated files
  • docs/src/assets/styles.css auto-resolves to incoming changes (regenerated by Tailwind)
  • Always run npm run build before publishing packages

Publishing packages

  1. Bump version in root package.json
  2. Run npm run build to generate dist files
  3. Run npm publish --workspaces to publish both packages

Package references

  • packages/css usage and details: see packages/css/README.md.
  • packages/cli usage and details: see packages/cli/README.md.