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",
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.sunken",
color: "primary.text.contrast",
})}
>
<span>Primary action (button, badge)</span>
<span class={css(labelStyle)}>bg: primary.sunken</span>
<span class={css(labelStyle)}>color: primary.text.contrast</span>
<Badge feedback="success">AA Pass</Badge>
</div>
<div
class={css(cardStyle, {
backgroundColor: "neutral.surface",
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</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.sunken",
color: "danger.text.contrast.bold",
})}
>
<span>Danger / error state</span>
<span class={css(labelStyle)}>bg: danger.sunken</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.sunken",
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