Pindoba targets WCAG 2.1 AA compliance by default, with AAA themes planned for the future.
Pindoba is built with accessibility as a core requirement. Every component targets WCAG 2.1 Level AA by default. AAA-compliant themes are planned for the future.
Accessibility is more important than making components look stunning. A beautiful component that some users can’t operate is a broken component. That said, accessible and beautiful are not mutually exclusive — we work hard to balance both.
When there’s a conflict, accessibility wins:
---
import { css } from "@pindoba/styled-system/css";
import Badge from "@pindoba/astro-badge";
import Button from "@pindoba/astro-button";
import Input from "@pindoba/astro-input";
const containerStyle = css({
display: "flex",
flexDirection: "column",
gap: "lg",
});
const sectionStyle = css.raw({
backgroundColor: "neutral.surface.soft",
borderRadius: "lg",
padding: "lg",
border: "1px solid",
borderColor: "neutral.border.muted",
});
const sectionTitleStyle = css.raw({
fontSize: "md",
fontWeight: "bold",
marginBottom: "md",
color: "neutral.text.bold",
});
const gridStyle = css.raw({
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
gap: "md",
});
const cardStyle = css.raw({
padding: "md",
borderRadius: "md",
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: "xs",
});
const labelStyle = css.raw({
fontSize: "xs",
fontFamily: "mono",
});
---
<div class={containerStyle}>
<div class={css(sectionStyle)}>
<h3 class={css(sectionTitleStyle)}>AA Contrast Examples (Current)</h3>
<div class={css(gridStyle)}>
<div
class={css(cardStyle, {
backgroundColor: "primary.surface.deep",
color: "primary.text.contrast",
})}
>
<span>Primary action (button, badge)</span>
<span class={css(labelStyle)}>bg: primary.surface.deep</span>
<span class={css(labelStyle)}>color: primary.text.contrast</span>
<Badge feedback="success">AA Pass</Badge>
</div>
<div
class={css(cardStyle, {
backgroundColor: "neutral.surface.soft",
color: "neutral.text.bold",
borderStyle: "solid",
borderWidth: "1px",
borderColor: "neutral.border.muted",
})}
>
<span>Body text on surface</span>
<span class={css(labelStyle, { color: "neutral.text" })}
>bg: neutral.surface.soft</span
>
<span class={css(labelStyle, { color: "neutral.text" })}
>color: neutral.text.bold</span
>
<Badge feedback="success">AA Pass</Badge>
</div>
<div
class={css(cardStyle, {
backgroundColor: "danger.surface.deep",
color: "danger.text.contrast.bold",
})}
>
<span>Danger / error state</span>
<span class={css(labelStyle)}>bg: danger.surface.deep</span>
<span class={css(labelStyle)}>color: danger.text.contrast.bold</span>
<Badge feedback="success">AA Pass</Badge>
</div>
</div>
</div>
<div class={css(sectionStyle)}>
<h3 class={css(sectionTitleStyle)}>Focus Indicators</h3>
<div
class={css({
display: "flex",
gap: "lg",
flexWrap: "wrap",
alignItems: "center",
})}
>
<Button>Tab to focus me</Button>
<Input placeholder="Focus this input" />
</div>
</div>
</div>Semantic color tokens include .text.contrast variants that guarantee legible text on solid backgrounds:
import { css } from "@pindoba/styled-system/css";
<button
className={css({
backgroundColor: "primary.surface.deep",
color: "primary.text.contrast",
})}
>
Guaranteed readable text
</button>;
Color is never the only indicator. Always pair it with text, icons, or patterns:
// Color + icon + descriptive text
<span
className={css({
color: "danger.text.bold",
display: "flex",
alignItems: "center",
gap: "xs",
})}
>
<ErrorIcon aria-hidden="true" />
Error: your session has expired. Please sign in again.
</span>
All interactive components use :focus-visible for keyboard-only focus rings, powered by the pindobaOutline utility and the focusVisibleWithin condition:
<button
className={css({
_focusVisible: { pdbO: true },
})}
>
Tab to focus me
</button>
The focusVisibleWithin condition (&:has(:focus-visible, [data-focus-visible])) allows parent elements to react when any child receives keyboard focus.
Every Pindoba component is fully operable with a keyboard:
Enter and Space to activateEscape to close, focus is trapped insideArrow keys to navigate between tabsSpace to toggle, Arrow keys to navigate groupsArrow keys to navigate, Enter to select, Escape to closeComponents respect the user’s motion preferences via the _motionReduce condition:
<div
className={css({
pdbT: "300ms",
_motionReduce: { transition: "none" },
})}
>
Animated content
</div>
Planned themes with enhanced accessibility:
These themes will be opt-in and can be combined with light or dark mode.
<button>, <input>, <select>) instead of <div> with click handlersaria-label.text.contrast tokens for text on colored backgroundsprefers-reduced-motion for all animations