Button
Primary call-to-action element with three visual variants that map to action hierarchy. Supports prefix and suffix slots for icons. Renders as an anchor when given an href, making it ideal for navigation-driven actions across landing pages, toolbars, and forms.
<arc-button> Overview
>
Button is the primary CTA element in ARC UI. Its three variants — primary, secondary, and ghost — map directly to a clear action hierarchy: primary draws the eye to the single most important action, secondary offers a visible but lower-emphasis alternative, and ghost provides a minimal, unobtrusive option for tertiary actions.
The `prefix` and `suffix` slots allow you to place icons or other inline elements alongside the button label. This is useful for adding a search icon, an arrow indicator, an RSS icon, or any visual cue that reinforces the button's action.
For links, slot a real `<a>` as the button's only child. That anchor is ordinary HTML in the initial markup, so the link works with JavaScript disabled and before the element upgrades — on a slow connection your hero CTA is clickable immediately rather than after hydration. Button adopts the anchor on upgrade and styles it as the control, so there is only ever one link in the accessibility tree.
The `href` attribute is still fully supported and renders an `<a>` in the shadow root. Reach for it inside app shells behind a login, where JavaScript is a given and the extra markup is noise. Prefer the slotted-anchor form anywhere the page is public, indexed, or first-load critical — that anchor cannot exist without JavaScript when the destination lives on the custom element instead. When no href and no slotted anchor are present, Button behaves as a standard button element for form submissions and interactive triggers.
Three size presets — sm, md, and lg — let you scale buttons to their context. Use lg for hero sections and high-impact CTAs, md for general UI, and sm for compact toolbars or inline actions. All sizes maintain consistent padding ratios and touch targets.Guidelines
When to use
- Use primary for the single most important action on the page
- Pair primary with secondary or ghost to create a clear visual hierarchy
- Use the `prefix` slot for leading icons and `suffix` for trailing arrows or indicators
- Use the lg size for hero sections and above-the-fold CTAs
- Slot a real `<a>` for navigation on public pages so the link survives with JavaScript disabled
- Reserve the `href` attribute for app shells where JavaScript is guaranteed
- Keep button labels short and action-oriented (e.g. "Get Started", "View Docs")
When not to use
- Do not place multiple primary buttons side by side — one primary per action group
- Do not use ghost variant for the most important action; it is too subtle for primary CTAs
- Do not render navigation as a plain button — this hurts accessibility and SEO
- Do not put a second link inside a slotted anchor; the adopted anchor is already the control
- Do not use long sentences as button labels; aim for two to three words maximum
- Do not disable buttons without explaining why the action is unavailable
Features
- Three variants (primary, secondary, ghost) for clear action hierarchy
- Three size presets (sm, md, lg) scaled for context
- Prefix and suffix slots for icons or inline elements alongside the label
- Adopts a slotted <a> so links work without JavaScript and before hydration
- Renders as <a> with href for accessible navigation
- Neon glow hover effect on primary variant
- Focus-visible ring for keyboard accessibility
- Subtle scale-down on active press for tactile feedback
- Disabled state that prevents interaction and dims the element
- Uppercase Tomorrow type treatment for strong visual presence
Preview
Usage
Layout and styling work without JavaScript via the HTML/CSS versions. Interactive features like events and state management require the Web Component or a framework wrapper.
<script type="module" src="@arclux/arc-ui"></script>
<!-- Slot a real <a>: the link works without JS and before hydration -->
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
<arc-button variant="primary" size="lg"><a href="/docs/getting-started">Get Started</a></arc-button>
<arc-button variant="secondary"><a href="/docs/components">View Docs</a></arc-button>
<arc-button variant="ghost"><a href="/docs/tokens">Learn More</a></arc-button>
</div>
<!-- href still works — best inside app shells where JS is guaranteed -->
<arc-button variant="primary" href="/dashboard">Dashboard</arc-button>
<!-- With prefix icon (icons go inside the anchor in the slotted form) -->
<arc-button variant="secondary">
<svg slot="prefix" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path d="M5 4a2 2 0 012-2h6a2 2 0 012 2v14l-5-2.5L5 18V4z"/></svg>
Bookmark
</arc-button> import { Button } from '@arclux/arc-ui-react';
export function HeroActions() {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
<Button variant="primary" size="lg"><a href="/docs/getting-started">Get Started</a></Button>
<Button variant="secondary"><a href="/docs/components">View Docs</a></Button>
<Button variant="ghost"><a href="/docs/tokens">Learn More</a></Button>
</div>
);
}
{/* With prefix icon */}
<Button variant="secondary">
<svg slot="prefix" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path d="M5 4a2 2 0 012-2h6a2 2 0 012 2v14l-5-2.5L5 18V4z"/></svg>
Bookmark
</Button> <script setup>
import { Button } from '@arclux/arc-ui-vue';
</script>
<template>
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
<Button variant="primary" size="lg"><a href="/docs/getting-started">Get Started</a></Button>
<Button variant="secondary"><a href="/docs/components">View Docs</a></Button>
<Button variant="ghost"><a href="/docs/tokens">Learn More</a></Button>
</div>
</template> <script>
import { Button } from '@arclux/arc-ui-svelte';
</script>
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
<Button variant="primary" size="lg"><a href="/docs/getting-started">Get Started</a></Button>
<Button variant="secondary"><a href="/docs/components">View Docs</a></Button>
<Button variant="ghost"><a href="/docs/tokens">Learn More</a></Button>
</div> import { Component } from '@angular/core';
import { Button } from '@arclux/arc-ui-angular';
@Component({
imports: [Button],
template: `
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
<arc-button variant="primary" size="lg"><a href="/docs/getting-started">Get Started</a></arc-button>
<arc-button variant="secondary"><a href="/docs/components">View Docs</a></arc-button>
<arc-button variant="ghost"><a href="/docs/tokens">Learn More</a></arc-button>
</div>
`,
})
export class HeroActionsComponent {} import { Button } from '@arclux/arc-ui-solid';
export function HeroActions() {
return (
<div style={{ display: 'flex', 'align-items': 'center', gap: '12px', 'flex-wrap': 'wrap' }}>
<Button variant="primary" size="lg"><a href="/docs/getting-started">Get Started</a></Button>
<Button variant="secondary"><a href="/docs/components">View Docs</a></Button>
<Button variant="ghost"><a href="/docs/tokens">Learn More</a></Button>
</div>
);
} import { Button } from '@arclux/arc-ui-preact';
export function HeroActions() {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
<Button variant="primary" size="lg"><a href="/docs/getting-started">Get Started</a></Button>
<Button variant="secondary"><a href="/docs/components">View Docs</a></Button>
<Button variant="ghost"><a href="/docs/tokens">Learn More</a></Button>
</div>
);
} <!-- Auto-generated by @arclux/prism — do not edit manually -->
<!-- arc-button — requires button.css + base.css (or arc-ui.css) -->
<span class="arc-button">
<a class="btn" href="#">Button</a>
</span> <!-- Auto-generated by @arclux/prism — do not edit manually -->
<!-- arc-button — self-contained, no external CSS needed -->
<style>
.arc-button:not([data-variant]) .btn:hover,
.arc-button[data-variant="primary"] .btn:hover { box-shadow: 0 0 8px rgba(77,126,247,0.9), 0 0 20px rgba(77,126,247,0.5), 0 0 44px rgba(77,126,247,0.25), 0 0 80px rgba(77,126,247,0.1); }
.arc-button:not([data-variant]) .btn:active,
.arc-button[data-variant="primary"] .btn:active { transform: scale(0.97); box-shadow: 0 0 8px rgba(77, 126, 247,0.5); }
.arc-button[data-variant="secondary"] .btn:hover { border-color: rgb(77, 126, 247);
color: rgb(77, 126, 247);
box-shadow: 0 0 20px rgba(77, 126, 247, 0.15); }
.arc-button[data-variant="secondary"] .btn:active { transform: scale(0.97);
background: rgba(77, 126, 247,0.05); }
.arc-button[data-variant="ghost"] .btn:hover { color: rgb(232, 232, 236);
background: rgba(255, 255, 255,0.03); }
.arc-button[data-variant="ghost"] .btn:active { transform: scale(0.97);
background: rgba(255, 255, 255,0.06); }
.arc-button .btn:focus-visible { outline: none; box-shadow: 0 0 0 2px rgb(3, 3, 7), 0 0 0 4px rgb(77, 126, 247), 0 0 16px rgba(77,126,247,0.3); }
</style>
<span class="arc-button" style="display: inline-flex">
<a class="btn" style="display: inline-flex; align-items: center; justify-content: center; gap: 8px; font-family: 'Tomorrow', system-ui, sans-serif; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; border: 1px solid transparent; border-radius: 10px; cursor: pointer; text-decoration: none; white-space: nowrap; box-sizing: border-box" href="#">Button</a>
</span> API
-
variant'primary' | 'secondary' | 'ghost''primary' - Controls the visual weight and emphasis. Primary is a filled button with a neon glow hover suited for the top-level CTA. Secondary uses a bordered outline for supporting actions. Ghost renders with no border or background, ideal for low-priority or tertiary actions.
-
size'sm' | 'md' | 'lg''md' - Sets the button size. Large (lg) is intended for hero sections and high-impact areas. Medium (md) is the default for general UI. Small (sm) fits compact toolbars, table rows, and inline contexts.
-
hrefstring'' - When provided, the button renders as an <a> element instead of a <button>, making it a navigational link. This is the recommended approach for any action that takes the user to a new page or section.
-
disabledbooleanfalse - When true, dims the button and prevents all pointer and keyboard interaction. Applies reduced opacity and removes hover/focus effects. Consider pairing with a tooltip that explains why the action is unavailable.
-
loadingbooleanfalse - Shows a spinner and disables the button. Use for async operations like form submission or API calls.
-
type'button' | 'submit' | 'reset''button' - Sets the HTML button type attribute. Use `submit` inside forms to trigger native form submission, or `reset` to clear form fields. Only applies when no `href` is set (link buttons ignore this).
See Also
- Icon Button Compact button that renders an icon with optional text label, supporting ghost, secondary, and primary variants.
- Chip A toggleable pill-shaped element for filters, tags, or multi-select options, with a selected state highlighted in accent-primary.
- Link Styled anchor with nav, muted, and default variants.