A versatile button component that supports multiple visual styles, sizes, and feedback states. Panel powers all surface rendering — giving buttons full access to backgrounds, borders, translucency, and radius controls without reimplementing them.
Three visual emphasis levels: primary uses a solid accent background, secondary uses a surface background with a muted border, and ghost uses a transparent background.
---
import Button from "../Button.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button emphasis="primary">Primary</Button>
<Button emphasis="secondary">Secondary</Button>
<Button emphasis="ghost">Ghost</Button>
</div>Apply semantic feedback colors across all emphasis levels. The default feedback uses the primary color palette.
---
import Button from "../Button.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div class={stack({ gap: "xl", direction: "column" })}>
<div>
<h3>Primary emphasis</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button emphasis="primary">Default</Button>
<Button emphasis="primary" feedback="success">Success</Button>
<Button emphasis="primary" feedback="danger">Danger</Button>
<Button emphasis="primary" feedback="warning">Warning</Button>
</div>
</div>
<div>
<h3>Secondary emphasis</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button emphasis="secondary">Default</Button>
<Button emphasis="secondary" feedback="primary">Primary</Button>
<Button emphasis="secondary" feedback="success">Success</Button>
<Button emphasis="secondary" feedback="danger">Danger</Button>
<Button emphasis="secondary" feedback="warning">Warning</Button>
</div>
</div>
<div>
<h3>Ghost emphasis</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button emphasis="ghost">Default</Button>
<Button emphasis="ghost" feedback="primary">Primary</Button>
<Button emphasis="ghost" feedback="success">Success</Button>
<Button emphasis="ghost" feedback="danger">Danger</Button>
<Button emphasis="ghost" feedback="warning">Warning</Button>
</div>
</div>
</div>Buttons come in four sizes to accommodate different layout needs.
---
import Button from "../Button.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button size="xs">Extra Small</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>The shape prop adds pill, square, or circle variants. Square and circle constrain the button to equal width and height — ideal for icon-only buttons. All shapes maintain their size-proportional height from the shared sizing system.
---
import Button from "../Button.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div class={stack({ gap: "xl", direction: "column" })}>
<div>
<h3>Shapes</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button size="md">Default</Button>
<Button shape="pill" size="md">Pill</Button>
<Button shape="square" size="md">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
</svg>
</Button>
<Button shape="circle" size="md">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</Button>
</div>
</div>
<div>
<h3>Sizes</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button shape="pill" size="xs">Pill xs</Button>
<Button shape="pill" size="sm">Pill sm</Button>
<Button shape="pill" size="md">Pill md</Button>
<Button shape="pill" size="lg">Pill lg</Button>
</div>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
mt: "sm",
})}
>
<Button shape="square" size="xs">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
</svg>
</Button>
<Button shape="square" size="sm">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
</svg>
</Button>
<Button shape="square" size="md">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
</svg>
</Button>
<Button shape="square" size="lg">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
</svg>
</Button>
</div>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
mt: "sm",
})}
>
<Button shape="circle" size="xs">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</Button>
<Button shape="circle" size="sm">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</Button>
<Button shape="circle" size="md">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</Button>
<Button shape="circle" size="lg">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</Button>
</div>
</div>
</div>Control the border style using the border prop. secondary buttons default to border="muted". All border styles use box-shadow and do not affect button dimensions.
---
import Button from "../Button.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div class={stack({ gap: "xl", direction: "column" })}>
<div>
<h3>Secondary with border</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button emphasis="secondary" border="none">None</Button>
<Button emphasis="secondary" border="default">Default</Button>
<Button emphasis="secondary" border="bold">Bold</Button>
<Button emphasis="secondary" border="muted">Muted</Button>
</div>
</div>
<div>
<h3>Ghost with border</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button emphasis="ghost" border="none">None</Button>
<Button emphasis="ghost" border="default">Default</Button>
<Button emphasis="ghost" border="bold">Bold</Button>
<Button emphasis="ghost" border="muted">Muted</Button>
</div>
</div>
</div>Override the default size-proportional border radius with the radius prop. Granular edge and corner props (radiusTop, radiusLeft, radiusTopLeft, etc.) allow asymmetric rounding for grouped or nested layouts.
---
import Button from "../Button.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div class={stack({ gap: "xl", direction: "column" })}>
<div>
<h3>Border radius</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button radius="none">None</Button>
<Button radius="sm">Small</Button>
<Button radius="md">Medium</Button>
<Button radius="xl">XL</Button>
<Button radius="full">Full</Button>
</div>
</div>
<div>
<h3>Partial radius</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button radiusTop="full">Top</Button>
<Button radiusBottom="full">Bottom</Button>
<Button radiusLeft="full">Left</Button>
<Button radiusRight="full">Right</Button>
</div>
</div>
</div>emphasis="primary" uses accent=true by default — a solid full-color (500) background with contrast text. emphasis="secondary" exposes the background prop for elevated, sunken, and transparent options. accent is not available on secondary.
---
import Button from "../Button.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div class={stack({ gap: "xl", direction: "column" })}>
<div>
<h3>Primary (accent by default)</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button emphasis="primary">Action</Button>
<Button emphasis="primary" feedback="success">Success</Button>
<Button emphasis="primary" feedback="danger">Danger</Button>
<Button emphasis="primary" feedback="warning">Warning</Button>
</div>
</div>
<div>
<h3>Secondary with different backgrounds</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button emphasis="secondary" background="surface">Surface</Button>
<Button emphasis="secondary" background="elevated">Elevated</Button>
<Button emphasis="secondary" background="sunken">Sunken</Button>
<Button emphasis="secondary" background="transparent">Transparent</Button>
</div>
</div>
</div>The passThrough prop provides two escape hatches for advanced customization: style accepts any Panda CSS SystemStyleObject applied directly to the button root, and props forwards arbitrary HTML attributes. Use style for layout overrides and props for accessibility attributes like aria-label or data-testid.
---
import Button from "../Button.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div class={stack({ gap: "xl", direction: "column" })}>
<div>
<h3>Custom style via passThrough</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button
passThrough={{
root: {
style: { minWidth: "200px", justifyContent: "start" },
},
}}
>
Wide left-aligned
</Button>
</div>
</div>
<div>
<h3>Custom props via passThrough</h3>
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Button
emphasis="secondary"
passThrough={{
root: {
props: { "aria-label": "Submit form", "data-testid": "submit-btn" },
},
}}
>
Submit
</Button>
</div>
</div>
</div>| Prop | |
|---|---|
emphasis | emphasis Visual emphasis level. primary: Solid accent background with contrast text. secondary: Surface background with a muted border. ghost: Transparent background. Type Default Required |
feedback | feedback Semantic feedback color scheme. default: Primary color palette. success: Green. danger: Red. warning: Orange. Type Default Required |
size | size Size variant. All sizes share the same height system as inputs. xs: Extra small. sm: Small. md: Default. lg: Large. Type Default Required |
shape | shape Shape variant. pill: Fully rounded corners. circle: Equal width/height circle. square: Equal width/height square. Type Default Required |
background | background Background style — only available when emphasis="secondary". surface: Default surface. sunken: Recessed background. elevated: Raised with shadow. transparent: No background. Type Default Required |
accent | accent Use the full-color (500) accent background from the current feedback color with contrast text. Only available on primary and ghost emphasis — primary sets this to true by default. Type Default Required |
border | border Box-shadow border style. none: No border. default: Standard border. bold: Stronger emphasis using a bolder color. muted: Subtle separation. Defaults to muted for secondary, none otherwise. Type Default Required |
translucent | translucent Apply a frosted glass effect with backdrop blur. Works with surface and elevated backgrounds. Type Default Required |
radius | radius Border radius override. Accepts the full spacing scale from none to 11xl, plus full for fully rounded corners. Overrides the default size-proportional radius. Type Default Required |
radiusTop | radiusTop Border radius for top-left and top-right corners. Type Default Required |
radiusBottom | radiusBottom Border radius for bottom-left and bottom-right corners. Type Default Required |
radiusLeft | radiusLeft Border radius for top-left and bottom-left corners. Type Default Required |
radiusRight | radiusRight Border radius for top-right and bottom-right corners. Type Default Required |
radiusTopLeft | radiusTopLeft Border radius for the top-left corner only. Type Default Required |
radiusTopRight | radiusTopRight Border radius for the top-right corner only. Type Default Required |
radiusBottomLeft | radiusBottomLeft Border radius for the bottom-left corner only. Type Default Required |
radiusBottomRight | radiusBottomRight Border radius for the bottom-right corner only. Type Default Required |
passThrough | passThrough Custom styling and props for button elements Type Default Required |
...rest | ...rest Standard HTML button attributes Type Default Required |