mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-11 04:38:18 +00:00
8.4 KiB
8.4 KiB
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 classessrc/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 entrypointsrc/js/*.js– Individual component JS files (kebab-case, ESM) +basecoat.js(component registry)src/nunjucks/*.njkandsrc/jinja/*.html.jinja– Component template macrosdocs/src/components/*.njk– Component documentation pages with examplesdocs/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 underdist/.packages/cli– CLI; distributed assets live underdist/.
- 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 shadcncn-*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.csssrc/css/styles/style-nova.csssrc/css/styles/style-maia.csssrc/css/styles/style-lyra.csssrc/css/styles/style-mira.csssrc/css/styles/style-luma.csssrc/css/styles/style-sera.csssrc/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:
- Review the current Basecoat component CSS and markup/templates first.
- Review the latest upstream shadcn/ui base component source for the equivalent component.
- Review the relevant sections in all eight upstream style packs (
vega,nova,maia,lyra,mira,luma,sera,rhea). - Decide what belongs in shared Basecoat component CSS versus what belongs in style-pack CSS.
- Keep Basecoat selectors and markup. Do not port shadcn
cn-*selectors directly. - Remove outdated Basecoat visual rules from the shared component CSS if those rules are now style-owned.
- Implement or update the component section in:
src/css/components/<component>.csssrc/css/styles/style-vega.csssrc/css/styles/style-nova.csssrc/css/styles/style-maia.csssrc/css/styles/style-lyra.csssrc/css/styles/style-mira.csssrc/css/styles/style-luma.csssrc/css/styles/style-sera.csssrc/css/styles/style-rhea.css
- Document any non-trivial difference from upstream as either:
- intentional
- drift fixed
- deferred
- Update the migration tracker entry for the component.
- Run
npm run buildafter each component migration. - 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:
- Basecoat component CSS:
src/css/components/<component>.css - Basecoat templates/macros if relevant:
src/nunjucks/*.njk,src/jinja/*.html.jinja - Basecoat docs page:
docs/src/components/<component>.njk - Upstream base component:
../_sandbox/shadcn-ui/apps/v4/registry/bases/base/ui/<component>.tsx - Upstream styles:
../_sandbox/shadcn-ui/apps/v4/registry/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css - 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:
camelCasefor vars/functions;PascalCasefor 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
borderfor lines/separators (notbackgroundon<hr>) - Use
:is()and:has()for compact selectors - Use
isolation: isolateto contain z-index stacking contexts - Use
data-*attributes for styling states,aria-*for accessibility semantics
- Use
- Component patterns:
- Dialog variants use wrapper classes (e.g.,
.command-dialogwraps.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
- Dialog variants use wrapper classes (e.g.,
- 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:
- Does shadcn/ui have it? → Port it
- Is it pure composition? → Document pattern, no component
- Does it need JS behavior? → Create component
- 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.cssand/orsrc/js/*.js. - Update a component's CSS/JS: Edit the relevant file in
src/css/components/*.css,src/css/styles/style-*.css, and/orsrc/js/*.js. - Update a component template: Edit
src/nunjucks/*.njkand/orsrc/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.
- Example:
- 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/cliv4. - Build uses Tailwind CLI and
terser; both are provided via devDependencies.
Git workflow
.gitattributesconfigures merge strategies for generated filesdocs/src/assets/styles.cssauto-resolves to incoming changes (regenerated by Tailwind)- Always run
npm run buildbefore publishing packages
Publishing packages
- Bump version in root
package.json - Run
npm run buildto generate dist files - Run
npm publish --workspacesto publish both packages
Package references
packages/cssusage and details: seepackages/css/README.md.packages/cliusage and details: seepackages/cli/README.md.