@afixt/a11y-calc
v0.1.3
Published
An accessible calculator React component with basic and scientific modes, styled after macOS Calculator.
Readme
@afixt/a11y-calc
An accessible calculator React component styled after the macOS built-in calculator, with basic and scientific modes. Built with accessibility as the primary design constraint.
Features
- Basic mode — standard four-function calculator (add, subtract, multiply, divide, percent, sign toggle)
- Scientific mode — trigonometric, logarithmic, exponential, power, root, and factorial functions, parentheses, constants (π, e), degree/radian toggle, and a "2nd" function key
- Fully accessible — screen reader announcements, keyboard support, ARIA attributes, high-contrast and reduced-motion support
- Themeable — all colors and sizes exposed as CSS custom properties and an
optional
themeprop
Installation
npm install @afixt/a11y-calcUsage
import { Calculator } from '@afixt/a11y-calc';
import '@afixt/a11y-calc/style.css';
// Basic usage
<Calculator />
// Start in scientific mode
<Calculator initialMode="scientific" />
// Custom theme
<Calculator theme={{
'calc-bg': '#1a1a2e',
'btn-operator-bg': '#e94560',
'btn-number-bg': '#16213e',
'btn-number-text': '#eee',
'display-bg': '#0f3460',
'display-text': '#ffffff',
}} />Props
| Prop | Type | Default | Description |
| ------------- | ------------------------- | --------- | ----------------------------------- |
| initialMode | 'basic' \| 'scientific' | 'basic' | Which mode the calculator starts in |
| theme | CalculatorTheme | — | Partial theme override object |
Theming
Every visual property is exposed as a CSS custom property. You can override them
globally in CSS or per-instance via the theme prop.
CSS Custom Properties
| Property | Default (dark) | Description |
| ----------------------------- | -------------- | --------------------------------------- |
| --calc-bg | #2a2a2a | Calculator body background |
| --calc-radius | 12px | Border radius of the calculator |
| --calc-width | 320px | Width in basic mode |
| --calc-width-scientific | 720px | Width in scientific mode |
| --display-bg | #1c1c1c | Display area background |
| --display-text | #ffffff | Display text color |
| --display-font-size | 3rem | Display number size |
| --btn-size | 70px | Minimum button height |
| --btn-font-size | 1.5rem | Button text size |
| --btn-number-bg | #505050 | Number button background |
| --btn-number-text | #ffffff | Number button text |
| --btn-number-hover | #6a6a6a | Number button hover |
| --btn-function-bg | #a5a5a5 | Function button (AC, %, +/-) background |
| --btn-function-text | #000000 | Function button text |
| --btn-operator-bg | #f09a36 | Operator button background |
| --btn-operator-text | #ffffff | Operator button text |
| --btn-operator-hover | #f5b04e | Operator button hover |
| --btn-operator-pressed-bg | #ffffff | Active operator background |
| --btn-operator-pressed-text | #f09a36 | Active operator text |
| --btn-scientific-bg | #3a3a3a | Scientific button background |
| --btn-scientific-text | #ffffff | Scientific button text |
| --focus-ring-color | #58a6ff | Focus indicator color |
| --focus-ring-width | 3px | Focus indicator width |
Example: global CSS override
.calculator {
--calc-bg: #1a1a2e;
--btn-operator-bg: #e94560;
--btn-number-bg: #16213e;
}The calculator also responds to prefers-color-scheme: light,
prefers-contrast: more, and prefers-reduced-motion: reduce media queries
automatically.
Accessibility
Conformance
The <Calculator> component targets WCAG 2.2 AA. Accessibility is verified
at four layers:
eslint-plugin-jsx-a11yat lint time@afixt/a11y-assertvia Vitest against the rendered component@afixt/a11y-assertvia Playwright against the running demo- A dedicated
accessibilityGitHub Actions workflow on every PR
See ADR 0009 for the layering
rationale. Known limitations are tracked as issues labeled a11y.
Semantic HTML
- All buttons are native
<button type="button">elements - The display uses the
<output>element - The calculator container uses
role="application"witharia-roledescription="calculator"to allow keyboard passthrough in screen readers
ARIA Attributes
- Every symbolic button has an explicit
aria-label(e.g.,÷is labeled "Divide",√xis "Square root") - Active operator buttons have
aria-pressed="true" - A live region (
aria-live="polite") announces every action in natural language:- Digit press:
"5" - Operator:
"plus" - Equals:
"5 plus 3 equals 8" - Scientific:
"square root of 9 equals 3" - Error:
"Error, cannot divide by zero" - Clear:
"All cleared, 0"
- Digit press:
Keyboard Support
| Key | Action |
| --------------- | ----------------------------- |
| 0–9 | Input digit |
| . | Decimal point |
| + - * / | Operators |
| Enter or = | Equals |
| Escape | Clear all |
| Backspace | Delete last digit |
| % | Percent |
| ^ | Power (scientific mode) |
| ( ) | Parentheses (scientific mode) |
Visual Accessibility
- Minimum 44px touch targets on all calculator buttons
- Visible focus indicators (never suppressed)
- Light/dark mode support (
prefers-color-scheme) - High contrast support (
prefers-contrast: more) - Reduced motion support (
prefers-reduced-motion) - Font sizes use
remunits
Scientific Mode Functions
| Button | Aria Label | Function | | ------------ | ----------------------------------------------- | --------------------------------- | | sin / asin | Sine / Arc sine | Trigonometric sine and inverse | | cos / acos | Cosine / Arc cosine | Trigonometric cosine and inverse | | tan / atan | Tangent / Arc tangent | Trigonometric tangent and inverse | | sinh / asinh | Hyperbolic sine / Inverse hyperbolic sine | Hyperbolic functions | | cosh / acosh | Hyperbolic cosine / Inverse hyperbolic cosine | Hyperbolic functions | | tanh / atanh | Hyperbolic tangent / Inverse hyperbolic tangent | Hyperbolic functions | | ln / log₁₀ | Natural log / Log base 10 | Logarithms | | x² / x³ | x squared / x cubed | Powers | | xⁿ | x to the power of n | Arbitrary power | | eˣ / 10ˣ | e to the power / 10 to the power | Exponentials | | √x / ∛x | Square root / Cube root | Roots | | 1/x | Reciprocal | Reciprocal | | x! | Factorial | Factorial | | π | Pi | 3.14159... | | e | Euler's number e | 2.71828... | | Rad/Deg | Switch angle mode | Toggle radians/degrees | | 2nd | Second function | Toggles alternate functions |
Development
First-time setup
git clone https://github.com/AFixt/a11y-calc.git
cd a11y-calc
bash scripts/bootstrap.sh # installs every external scanner used by the hooks
npm ciThe bootstrap script installs trufflehog, lychee, semgrep, osv-scanner,
codeql, dependency-check, and zap via Homebrew on macOS (with Linux/WSL
release-binary hints for the rest). First run of dependency-check seeds the
NVD mirror (~1 GB, 20–40 min — faster with an NVD API key).
Scripts
npm run dev # Start demo dev server (vite)
npm run build # Type-check and build library (dist/)
npm run build:demo # Build the demo app (dist-demo/)
npm run preview # Serve the built demo
npm run lint # ESLint
npm run typecheck # tsc --noEmit across all project references
npm test # Unit tests (vitest)
npm run test:watch # Tests in watch mode
npm run test:coverage # Tests with coverage report
npm run test:e2e # Build demo + run Playwright E2E tests
npm run size # size-limit budgets
npm run lhci # Lighthouse CI against the demo
npm run ai:context # Print project summary (for AI sessions / new contribs)Gates
npm run check— fast: lint + typecheck + stylelint + markdownlint + format checknpm run check:ci— CI target: adds tests, build, size, dupes, links, fast security scanners, license allowlistnpm run check:all— pre-push: full gate including CodeQL and OWASP Dependency-Check
npm run check:all runs automatically on git push via Husky.
Contributing
- Run
bash scripts/bootstrap.shandnpm ci. - Create a branch, commit with
Conventional Commits (enforced by
commitlinton commit-msg). - Husky's
pre-commitrunslint-staged+ type check on staged files- TruffleHog secret scan (see ADR 0012).
pre-pushruns the fullcheck:all.
- TruffleHog secret scan (see ADR 0012).
- Open a PR targeting
main. CI runscheck:ci, Lighthouse CI, and the a11y workflow. Scheduled workflows (CodeQL, OWASP Dependency-Check, OWASP ZAP, lychee online) run weekly onmain. - Non-obvious engineering decisions get an ADR in
docs/adr/— seedocs/templates/adr-template.md.
Tech Stack
- React + TypeScript
- Vite for bundling
- Vitest + Testing Library for unit tests
- Playwright for E2E tests
License
MIT
