learning_ai_common_plat/packages/accessibility/src/index.ts
Saravana Achu Mac 1ee97327ee feat(packages): create 9 NomGap-required platform packages
Create source implementations for packages imported by NomGap:
- @bytelyst/accessibility — ARIA helper functions (alertLabel, progressLabel, etc.)
- @bytelyst/celebrations — celebration engine for milestones
- @bytelyst/gentle-notifications — guilt-free notification filtering
- @bytelyst/time-references — human-friendly fasting time references
- @bytelyst/subscription-client — billing/subscription HTTP client
- @bytelyst/quick-actions — progressive disclosure UI helpers
- @bytelyst/referral-client — referral program client
- @bytelyst/marketplace-client — influencer marketplace client
- @bytelyst/org-client — B2B org management client

Made-with: Cursor
2026-03-29 22:24:02 -07:00

125 lines
2.8 KiB
TypeScript

export type AlertA11yProps = {
role: 'alert';
'aria-live': 'assertive' | 'polite';
'aria-label': string;
};
export function alertLabel(level: string, description: string): AlertA11yProps {
return {
role: 'alert',
'aria-live': level === 'danger' ? 'assertive' : 'polite',
'aria-label': description,
};
}
export type ProgressbarA11yProps = {
role: 'progressbar';
'aria-label': string;
'aria-valuenow': number;
'aria-valuemin': number;
'aria-valuemax': number;
'aria-valuetext': string;
};
export function progressLabel(
label: string,
valuePct: number,
description: string,
): ProgressbarA11yProps {
return {
role: 'progressbar',
'aria-label': label,
'aria-valuenow': valuePct,
'aria-valuemin': 0,
'aria-valuemax': 100,
'aria-valuetext': description,
};
}
export type AriaLabelOnly = { 'aria-label': string };
export function streakLabel(days: number): AriaLabelOnly {
return { 'aria-label': `${days} day streak` };
}
export type ButtonA11yProps = {
'aria-label': string;
'aria-roledescription'?: string;
};
export function buttonLabel(text: string, hint?: string): ButtonA11yProps {
return {
'aria-label': text,
...(hint ? { 'aria-roledescription': hint } : {}),
};
}
export function achievementLabel(name: string, description: string): AriaLabelOnly {
return { 'aria-label': `Achievement: ${name}${description}` };
}
export type TimerA11yProps = {
'aria-label': string;
'aria-live': 'polite';
};
export function timerLabel(
hours: number,
minutes: number,
seconds: number,
status: string,
): TimerA11yProps {
return {
'aria-label': `Timer: ${hours}h ${minutes}m ${seconds}s, ${status}`,
'aria-live': 'polite',
};
}
export type SliderA11yProps = {
role: 'slider';
'aria-label': string;
'aria-valuenow': number;
'aria-valuemin': number;
'aria-valuemax': number;
};
export function sliderLabel(
label: string,
value: number,
min: number,
max: number,
): SliderA11yProps {
return {
role: 'slider',
'aria-label': label,
'aria-valuenow': value,
'aria-valuemin': min,
'aria-valuemax': max,
};
}
function plural(n: number, singular: string, pluralForm: string): string {
const word = n === 1 ? singular : pluralForm;
return `${n} ${word}`;
}
/**
* Spoken-friendly duration from a fractional hour value, e.g. 12 → "12 hours", 1.5 → "1 hour 30 minutes".
*/
export function formatDurationForA11y(hours: number): string {
const totalMinutes = Math.round(hours * 60);
const h = Math.floor(totalMinutes / 60);
const m = totalMinutes % 60;
if (h === 0 && m === 0) {
return '0 minutes';
}
if (m === 0) {
return plural(h, 'hour', 'hours');
}
if (h === 0) {
return plural(m, 'minute', 'minutes');
}
return `${plural(h, 'hour', 'hours')} ${plural(m, 'minute', 'minutes')}`;
}