/components/CLAUDE.md
CLAUDE.md at /components/CLAUDE.md
Path: components/CLAUDE.md
components/ Directory Guidelines
Directory Boundary
This project has two separate component directories:
components/— Preact components used for MDX rendering, interactive Preact islands, and Astrobook stories. These run in the browser or during MDX SSRsrc/astro/components/— Astro components (.astrofiles) used in page templates, layouts, and static rendering. These are server-only and never hydrated
Do not mix these: Astro components cannot be imported into Preact code, and Preact islands should live in components/, not src/astro/components/.
Styling
Before making style changes: Read /doc/docs/overview/design-system.md for the full design token reference.
- NEVER use inline styles. Always use Tailwind CSS classes
- For dynamic styles, use Tailwind arbitrary values:
className="w-[85%]" - For Framer Motion, extract animation props to typed objects instead of inline styles
- Numeric Tailwind classes prohibited (e.g.,
p-2,m-4,gap-8). Use semantic tokens:p-vgap-*,p-hgap-*,gap-vgap-* - No CSS Modules: This project does not use
.module.cssfiles. All styling is done via Tailwind utility classes. For complex CSS that can’t be expressed as utilities (e.g., keyframe animations, clip-paths), use plain CSS files with unique class prefixes (e.g.,hamster-animation.csswith.hamster-*classes) imported as side effects - For long className strings, use plain single-line strings or template literals (no
ctlhelper needed)
Astrobook
When adding a new component or modifying an existing one, add or update its story file (*.stories.tsx). Verify the story renders correctly with pnpm astrobook:dev.
Component Patterns
- Separate
ArticleNavItemcomponents exist for different contexts:components/top/article-nav-item.tsx— Homepage article grid layoutcomponents/list/article-nav-item.tsx— Category/tag listing pages layout- Different layouts require different responsive behavior and styling
- Always null-check optional props before passing to required-prop components:
{imgUrl && <ResponsiveImage imgUrl={imgUrl} blurHash={blurHash} />}