mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-14 22:18:19 +00:00
178 lines
8.4 KiB
Markdown
178 lines
8.4 KiB
Markdown
# 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`.
|