Artisan workshop with modular UI components being assembled on a workbench

Bearnie: Astro Components You Actually Own

The Orange Cat
The Orange Cat

Bearnie is a component library for Astro and Tailwind CSS that takes a fundamentally different approach to UI tooling. Instead of installing a package and importing opaque components from node_modules, Bearnie copies component source code directly into your project. You get 60+ accessible, production-ready components that you fully own and can modify however you like. Think of it as the shadcn/ui experience, but built from the ground up for Astro with vanilla JavaScript instead of React.

Why Bearnie Stands Out

Bearnie isn't just another component library. Here's what makes it different:

  • Full Code Ownership: Components live in your project directory, not behind a package version. No surprise breaking changes, no dependency hell, no black boxes.
  • Zero Framework Overhead: Interactive components use vanilla JavaScript. Static components ship absolutely no JavaScript to the browser.
  • WCAG 2.1 AA Compliance: Every component ships with proper ARIA attributes, keyboard navigation, and screen reader support baked in.
  • Tailwind CSS v4 Integration: Theming is handled through CSS variables with automatic light and dark mode support.
  • 60+ Components: From buttons and inputs to sidebars, command palettes, and carousels, there's a component for nearly every situation.
  • MCP Server: An AI-assisted installation path lets tools like Claude, Cursor, and VS Code Copilot add components for you.

Setting Up Shop

Fresh Project

The fastest way to get started is to scaffold a new project:

npm create bearnie my-app
# or
yarn create bearnie my-app
# or
pnpm create bearnie my-app

This sets up an Astro project pre-configured with Tailwind CSS v4, the Bearnie theming system, and a base layout ready for component use.

Existing Project

If you already have an Astro project running, you can start adding components individually using the CLI:

npx bearnie add button

That command fetches the button component from Bearnie's registry and drops it into your project. The component becomes a regular .astro file in your codebase. No imports from external packages, no version pinning, just your code.

Your First Components

Laying Down a Button

After adding the button component, using it feels exactly like any other Astro component:

---
// src/pages/index.astro
import Button from "../components/ui/button.astro";
---

<Button variant="primary" size="lg">
  Get Started
</Button>

<Button variant="outline" size="sm">
  Learn More
</Button>

<Button variant="ghost" disabled>
  Coming Soon
</Button>

Since the component lives in your project, you can open the file and tweak the variants, adjust the Tailwind classes, or add entirely new props. There's no API boundary between you and the component.

Cards on the Table

The card component provides a structured container with sensible defaults:

---
import Card from "../components/ui/card.astro";
import Button from "../components/ui/button.astro";
---

<Card>
  <div slot="header">
    <h3>Subscription Plan</h3>
    <p>Everything you need to get started</p>
  </div>

  <ul>
    <li>Unlimited projects</li>
    <li>Priority support</li>
    <li>Custom domains</li>
  </ul>

  <div slot="footer">
    <Button variant="primary" size="lg">
      Subscribe Now
    </Button>
  </div>
</Card>

The card ships with no JavaScript at all. It's pure HTML and Tailwind CSS, which means zero runtime cost for what is fundamentally a layout concern.

Gathering Input

Forms are where accessibility really matters, and Bearnie handles the heavy lifting:

---
import Input from "../components/ui/input.astro";
import Label from "../components/ui/label.astro";
import Select from "../components/ui/select.astro";
import Button from "../components/ui/button.astro";
---

<form>
  <div>
    <Label for="email">Email Address</Label>
    <Input
      id="email"
      type="email"
      placeholder="you@example.com"
      required
    />
  </div>

  <div>
    <Label for="role">Role</Label>
    <Select id="role">
      <option value="developer">Developer</option>
      <option value="designer">Designer</option>
      <option value="manager">Manager</option>
    </Select>
  </div>

  <Button type="submit" variant="primary">
    Sign Up
  </Button>
</form>

Every form component includes proper label associations, focus management, and keyboard interaction patterns that meet WCAG 2.1 AA standards.

Power Moves

Theming with CSS Variables

Bearnie's theming system uses CSS variables, which means you can swap the entire look of your components by changing a few values:

// src/styles/theme.css

:root {
  --color-primary: 220 90% 56%;
  --color-primary-foreground: 0 0% 100%;
  --color-secondary: 220 14% 96%;
  --color-secondary-foreground: 220 9% 46%;
  --color-background: 0 0% 100%;
  --color-foreground: 220 9% 6%;
  --color-border: 220 13% 91%;
  --color-ring: 220 90% 56%;
  --radius: 0.5rem;
}

.dark {
  --color-primary: 220 90% 56%;
  --color-primary-foreground: 0 0% 100%;
  --color-background: 220 9% 6%;
  --color-foreground: 0 0% 95%;
  --color-border: 220 13% 20%;
}

Since the components reference these variables through Tailwind, changing themes is instant and affects every component simultaneously. You can even generate custom themes using Bearnie's theme builder, which encodes your color preferences into a shareable hash.

The Command Palette

The command component gives you a searchable, keyboard-navigable command palette:

---
import Command from "../components/ui/command.astro";
---

<Command
  items={[
    { group: "Navigation", items: [
      { label: "Home", value: "home", shortcut: "G H" },
      { label: "Dashboard", value: "dashboard", shortcut: "G D" },
      { label: "Settings", value: "settings", shortcut: "G S" },
    ]},
    { group: "Actions", items: [
      { label: "New Project", value: "new-project", shortcut: "N P" },
      { label: "Search", value: "search", shortcut: "/" },
    ]},
  ]}
/>

The command palette component uses vanilla JavaScript for keyboard handling, search filtering, and focus management. Because Bearnie deduplicates its runtime modules, having multiple interactive components on the same page doesn't multiply the script overhead.

AI-Powered Installation with MCP

Bearnie ships with a Model Context Protocol server, which means AI assistants can add and configure components for you. Add the MCP configuration to your editor:

// .mcp.json or editor-specific config
{
  "mcpServers": {
    "bearnie": {
      "command": "npx",
      "args": ["@bearnie/mcp"]
    }
  }
}

Once configured, you can ask your AI assistant to "add a dialog component" or "set up a sidebar with navigation," and it will use Bearnie's registry to pull the right components into your project. This is particularly useful when building out new pages quickly, as the AI can handle the repetitive scaffolding while you focus on business logic.

Building a Dialog

The dialog component handles focus trapping, escape key behavior, and backdrop clicks:

---
import Dialog from "../components/ui/dialog.astro";
import Button from "../components/ui/button.astro";
import Input from "../components/ui/input.astro";
import Label from "../components/ui/label.astro";
---

<Button data-dialog-trigger="edit-profile">
  Edit Profile
</Button>

<Dialog id="edit-profile" title="Edit Profile">
  <div>
    <Label for="display-name">Display Name</Label>
    <Input id="display-name" value="Jane Developer" />
  </div>

  <div>
    <Label for="bio">Bio</Label>
    <textarea id="bio" class="textarea" rows={3}>
      Building things for the web.
    </textarea>
  </div>

  <div slot="footer">
    <Button variant="outline" data-dialog-close>Cancel</Button>
    <Button variant="primary">Save Changes</Button>
  </div>
</Dialog>

The dialog's JavaScript handles the focus trap automatically, ensuring keyboard users can't tab out of the modal while it's open. Screen readers announce the dialog title when it opens. All of this is in your codebase, so if your project needs a different focus behavior, you can adjust the implementation directly.

The Ownership Advantage

The fundamental pitch of Bearnie is that your component library should be yours. When you run npx bearnie add accordion, you're not adding a dependency. You're receiving a well-crafted starting point that immediately becomes part of your project. Six months from now, if you need to change how the accordion animation works, you open the file and change it. No pull requests to upstream, no waiting for a patch release, no compatibility matrices.

This approach does come with a trade-off: you won't automatically get updates. But for teams building custom design systems on top of Astro, that's often exactly the point. You want stability and control, not a moving target.

With 60+ components covering forms, navigation, feedback, disclosure patterns, and display elements, Bearnie gives Astro developers a serious foundation for building accessible, performant interfaces without reaching for React. If you've been looking for the Astro-native equivalent of shadcn/ui, this is it.