@flavia-dev/a11y-ui-kit-react 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/a11y-ui-kit-react.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +59 -0
- package/dist/index.js +122 -53
- package/dist/index.js.map +1 -1
- package/package.json +35 -4
package/README.md
CHANGED
|
@@ -17,14 +17,14 @@ Production-ready accessible React components following **WCAG 2.1 AA standards**
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install
|
|
20
|
+
npm install @flavia-dev/a11y-ui-kit-react
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
25
|
```tsx
|
|
26
|
-
import { Button } from '
|
|
27
|
-
import '
|
|
26
|
+
import { Button } from '@flavia-dev/a11y-ui-kit-react';
|
|
27
|
+
import '@flavia-dev/a11y-ui-kit-react/styles.css';
|
|
28
28
|
|
|
29
29
|
function App() {
|
|
30
30
|
return (
|
|
@@ -1 +1 @@
|
|
|
1
|
-
._button_1003x_2{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:.5rem;font-family:inherit;font-weight:600;line-height:1.5;border:none;border-radius:.5rem;cursor:pointer;transition:all .2s ease;text-decoration:none;white-space:nowrap}._button_1003x_2:focus-visible{outline:3px solid #3b82f6;outline-offset:2px}._button_1003x_2[aria-disabled=true],._disabled_1003x_26{cursor:not-allowed;opacity:.6;pointer-events:none}._primary_1003x_33{background-color:#3b82f6;color:#fff}._primary_1003x_33:hover:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#2563eb}._primary_1003x_33:active:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#1d4ed8}._secondary_1003x_46{background-color:#64748b;color:#fff}._secondary_1003x_46:hover:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#475569}._secondary_1003x_46:active:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#334155}._danger_1003x_59{background-color:#ef4444;color:#fff}._danger_1003x_59:hover:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#dc2626}._danger_1003x_59:active:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#b91c1c}._small_1003x_73{padding:.5rem 1rem;font-size:.875rem}._medium_1003x_78{padding:.625rem 1.25rem;font-size:1rem}._large_1003x_83{padding:.75rem 1.5rem;font-size:1.125rem}._fullWidth_1003x_89{width:100%}._loading_1003x_94{pointer-events:none}._loadingContainer_1003x_98{display:inline-flex;align-items:center;gap:.5rem}._spinner_1003x_104{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:_spin_1003x_104 .75s linear infinite}@keyframes _spin_1003x_104{to{transform:rotate(360deg)}}._srOnly_1003x_121{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}
|
|
1
|
+
._button_1003x_2{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:.5rem;font-family:inherit;font-weight:600;line-height:1.5;border:none;border-radius:.5rem;cursor:pointer;transition:all .2s ease;text-decoration:none;white-space:nowrap}._button_1003x_2:focus-visible{outline:3px solid #3b82f6;outline-offset:2px}._button_1003x_2[aria-disabled=true],._disabled_1003x_26{cursor:not-allowed;opacity:.6;pointer-events:none}._primary_1003x_33{background-color:#3b82f6;color:#fff}._primary_1003x_33:hover:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#2563eb}._primary_1003x_33:active:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#1d4ed8}._secondary_1003x_46{background-color:#64748b;color:#fff}._secondary_1003x_46:hover:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#475569}._secondary_1003x_46:active:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#334155}._danger_1003x_59{background-color:#ef4444;color:#fff}._danger_1003x_59:hover:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#dc2626}._danger_1003x_59:active:not([aria-disabled=true]):not(._disabled_1003x_26){background-color:#b91c1c}._small_1003x_73{padding:.5rem 1rem;font-size:.875rem}._medium_1003x_78{padding:.625rem 1.25rem;font-size:1rem}._large_1003x_83{padding:.75rem 1.5rem;font-size:1.125rem}._fullWidth_1003x_89{width:100%}._loading_1003x_94{pointer-events:none}._loadingContainer_1003x_98{display:inline-flex;align-items:center;gap:.5rem}._spinner_1003x_104{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:_spin_1003x_104 .75s linear infinite}@keyframes _spin_1003x_104{to{transform:rotate(360deg)}}._srOnly_1003x_121{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}._inputWrapper_sim0c_2{display:inline-flex;flex-direction:column;gap:.25rem}._inputWrapper_sim0c_2._fullWidth_sim0c_8{width:100%}._label_sim0c_13{font-size:.875rem;font-weight:500;color:#374151;margin-bottom:.25rem}._label_sim0c_13._required_sim0c_20:after{content:""}._requiredIndicator_sim0c_24{color:#dc2626;margin-left:.25rem}._input_sim0c_2{font-family:inherit;font-size:1rem;line-height:1.5;color:#1f2937;background-color:#fff;border:1px solid #6b7280;border-radius:.375rem;transition:all .2s ease;outline:none}._input_sim0c_2:focus{border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}._input_sim0c_2:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}._small_sim0c_53{padding:.375rem .75rem;font-size:.875rem}._medium_sim0c_58{padding:.5rem .875rem;font-size:1rem}._large_sim0c_63{padding:.625rem 1rem;font-size:1.125rem}._input_sim0c_2._error_sim0c_69{border-color:#dc2626}._input_sim0c_2._error_sim0c_69:focus{border-color:#dc2626;box-shadow:0 0 0 3px #dc26261a}._input_sim0c_2._disabled_sim0c_78,._input_sim0c_2:disabled{background-color:#f3f4f6;color:#6b7280;cursor:not-allowed;opacity:.8}._fullWidth_sim0c_8{width:100%}._helperText_sim0c_91,._errorText_sim0c_92{font-size:.875rem;margin-top:.25rem}._helperText_sim0c_91{color:#6b7280}._errorText_sim0c_92{color:#dc2626;font-weight:500}._visuallyHidden_sim0c_107{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),h=require("react"),$="_button_1003x_2",q="_disabled_1003x_26",B="_primary_1003x_33",C="_secondary_1003x_46",T="_danger_1003x_59",D="_small_1003x_73",O="_medium_1003x_78",k="_large_1003x_83",w="_fullWidth_1003x_89",H="_loading_1003x_94",R="_loadingContainer_1003x_98",K="_spinner_1003x_104",S="_spin_1003x_104",E="_srOnly_1003x_121",a={button:$,disabled:q,primary:B,secondary:C,danger:T,small:D,medium:O,large:k,fullWidth:w,loading:H,loadingContainer:R,spinner:K,spin:S,srOnly:E},j=h.forwardRef(({variant:_="primary",size:u="medium",children:m,disabled:l=!1,loading:n=!1,fullWidth:d=!1,className:p,type:r="button",onClick:c,...x},b)=>{const t=l||n,f=i=>{if(t){i.preventDefault();return}c?.(i)},y=i=>{t&&(i.key==="Enter"||i.key===" ")&&i.preventDefault()},o=[a.button,a[_],a[u],n&&a.loading,d&&a.fullWidth,t&&a.disabled,p].filter(Boolean).join(" ");return s.jsxs("button",{ref:b,type:r,className:o,"aria-disabled":t,"aria-busy":n||void 0,onClick:f,onKeyDown:y,...x,children:[n&&s.jsxs("span",{role:"status","aria-live":"polite",className:a.loadingContainer,children:[s.jsx("span",{className:a.spinner,"aria-hidden":"true"}),s.jsx("span",{className:a.srOnly,children:"Loading..."})]}),s.jsx("span",{"aria-hidden":n||void 0,children:m})]})});j.displayName="Button";const F="_inputWrapper_sim0c_2",L="_fullWidth_sim0c_8",M="_label_sim0c_13",P="_required_sim0c_20",z="_requiredIndicator_sim0c_24",A="_input_sim0c_2",G="_small_sim0c_53",J="_medium_sim0c_58",Q="_large_sim0c_63",U="_error_sim0c_69",V="_disabled_sim0c_78",X="_helperText_sim0c_91",Y="_errorText_sim0c_92",Z="_visuallyHidden_sim0c_107",e={inputWrapper:F,fullWidth:L,label:M,required:P,requiredIndicator:z,input:A,small:G,medium:J,large:Q,error:U,disabled:V,helperText:X,errorText:Y,visuallyHidden:Z},v=h.forwardRef(({label:_,type:u="text",size:m="medium",error:l,helperText:n,fullWidth:d=!1,hideLabel:p=!1,required:r=!1,disabled:c=!1,className:x,id:b,...t},f)=>{const y=h.useId(),o=b||y,i=l?`${o}-error`:void 0,g=n?`${o}-helper`:void 0,N=[i,g].filter(Boolean).join(" ")||void 0,W=[e.input,e[m],l&&e.error,c&&e.disabled,d&&e.fullWidth,x].filter(Boolean).join(" "),I=[e.label,p&&e.visuallyHidden,r&&e.required].filter(Boolean).join(" ");return s.jsxs("div",{className:`${e.inputWrapper} ${d?e.fullWidth:""}`,children:[s.jsxs("label",{htmlFor:o,className:I,children:[_,r&&s.jsx("span",{className:e.requiredIndicator,"aria-label":"required",children:" *"})]}),s.jsx("input",{ref:f,id:o,type:u,className:W,required:r,disabled:c,"aria-invalid":l?!0:void 0,"aria-describedby":N,"aria-required":r||void 0,...t}),n&&!l&&s.jsx("span",{id:g,className:e.helperText,children:n}),l&&s.jsx("span",{id:i,className:e.errorText,role:"alert",children:l})]})});v.displayName="Input";exports.Button=j;exports.Input=v;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/Button/Button.tsx"],"sourcesContent":["// src/atoms/Button/Button.tsx\nimport React from \"react\";\nimport styles from \"./Button.module.css\";\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: \"primary\" | \"secondary\" | \"danger\";\n size?: \"small\" | \"medium\" | \"large\";\n children: React.ReactNode;\n loading?: boolean;\n fullWidth?: boolean;\n}\n\n/**\n * Accessible button component following WCAG 2.1 AA standards\n *\n * Features:\n * - Native <button> element for semantic HTML\n * - Full keyboard support (Tab, Enter, Space)\n * - Screen reader announcements for loading state\n * - Visible focus indicator\n * - Color contrast compliant\n *\n * @example\n * ```tsx\n * <Button variant=\"primary\" onClick={handleClick}>\n * Click me\n * </Button>\n *\n * <Button loading disabled>\n * Submitting...\n * </Button>\n * ```\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = \"primary\",\n size = \"medium\",\n children,\n disabled = false,\n loading = false,\n fullWidth = false,\n className,\n type = \"button\",\n onClick,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n\n // Handle click with disabled check (prevents action but keeps focusable)\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (isDisabled) {\n event.preventDefault();\n return;\n }\n onClick?.(event);\n };\n\n // Handle keyboard events to prevent action when disabled\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (isDisabled && (event.key === \"Enter\" || event.key === \" \")) {\n event.preventDefault();\n }\n };\n\n const classNames = [\n styles.button,\n styles[variant],\n styles[size],\n loading && styles.loading,\n fullWidth && styles.fullWidth,\n isDisabled && styles.disabled,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <button\n ref={ref}\n type={type}\n className={classNames}\n aria-disabled={isDisabled}\n aria-busy={loading || undefined}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {loading && (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className={styles.loadingContainer}\n >\n <span className={styles.spinner} aria-hidden=\"true\" />\n <span className={styles.srOnly}>Loading...</span>\n </span>\n )}\n <span aria-hidden={loading || undefined}>{children}</span>\n </button>\n );\n }\n);\n\nButton.displayName = \"Button\";\n"],"names":["Button","React","variant","size","children","disabled","loading","fullWidth","className","type","onClick","props","ref","isDisabled","handleClick","event","handleKeyDown","classNames","styles","jsxs","jsx"],"mappings":"olBAkCaA,EAASC,EAAM,WAC1B,CACE,CACE,QAAAC,EAAU,UACV,KAAAC,EAAO,SACP,SAAAC,EACA,SAAAC,EAAW,GACX,QAAAC,EAAU,GACV,UAAAC,EAAY,GACZ,UAAAC,EACA,KAAAC,EAAO,SACP,QAAAC,EACA,GAAGC,CAAA,EAELC,IACG,CACH,MAAMC,EAAaR,GAAYC,EAGzBQ,EAAeC,GAA+C,CAClE,GAAIF,EAAY,CACdE,EAAM,eAAA,EACN,MACF,CACAL,IAAUK,CAAK,CACjB,EAGMC,EAAiBD,GAAkD,CACnEF,IAAeE,EAAM,MAAQ,SAAWA,EAAM,MAAQ,MACxDA,EAAM,eAAA,CAEV,EAEME,EAAa,CACjBC,EAAO,OACPA,EAAOhB,CAAO,EACdgB,EAAOf,CAAI,EACXG,GAAWY,EAAO,QAClBX,GAAaW,EAAO,UACpBL,GAAcK,EAAO,SACrBV,CAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG,EAEX,OACEW,EAAAA,KAAC,SAAA,CACC,IAAAP,EACA,KAAAH,EACA,UAAWQ,EACX,gBAAeJ,EACf,YAAWP,GAAW,OACtB,QAASQ,EACT,UAAWE,EACV,GAAGL,EAEH,SAAA,CAAAL,GACCa,EAAAA,KAAC,OAAA,CACC,KAAK,SACL,YAAU,SACV,UAAWD,EAAO,iBAElB,SAAA,CAAAE,EAAAA,IAAC,OAAA,CAAK,UAAWF,EAAO,QAAS,cAAY,OAAO,EACpDE,EAAAA,IAAC,OAAA,CAAK,UAAWF,EAAO,OAAQ,SAAA,YAAA,CAAU,CAAA,CAAA,CAAA,EAG9CE,EAAAA,IAAC,OAAA,CAAK,cAAad,GAAW,OAAY,SAAAF,CAAA,CAAS,CAAA,CAAA,CAAA,CAGzD,CACF,EAEAJ,EAAO,YAAc"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/atoms/Button/Button.tsx","../src/atoms/Input/Input.tsx"],"sourcesContent":["// src/atoms/Button/Button.tsx\nimport React from \"react\";\nimport styles from \"./Button.module.css\";\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: \"primary\" | \"secondary\" | \"danger\";\n size?: \"small\" | \"medium\" | \"large\";\n children: React.ReactNode;\n loading?: boolean;\n fullWidth?: boolean;\n}\n\n/**\n * Accessible button component following WCAG 2.1 AA standards\n *\n * Features:\n * - Native <button> element for semantic HTML\n * - Full keyboard support (Tab, Enter, Space)\n * - Screen reader announcements for loading state\n * - Visible focus indicator\n * - Color contrast compliant\n *\n * @example\n * ```tsx\n * <Button variant=\"primary\" onClick={handleClick}>\n * Click me\n * </Button>\n *\n * <Button loading disabled>\n * Submitting...\n * </Button>\n * ```\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = \"primary\",\n size = \"medium\",\n children,\n disabled = false,\n loading = false,\n fullWidth = false,\n className,\n type = \"button\",\n onClick,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n\n // Handle click with disabled check (prevents action but keeps focusable)\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (isDisabled) {\n event.preventDefault();\n return;\n }\n onClick?.(event);\n };\n\n // Handle keyboard events to prevent action when disabled\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (isDisabled && (event.key === \"Enter\" || event.key === \" \")) {\n event.preventDefault();\n }\n };\n\n const classNames = [\n styles.button,\n styles[variant],\n styles[size],\n loading && styles.loading,\n fullWidth && styles.fullWidth,\n isDisabled && styles.disabled,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <button\n ref={ref}\n type={type}\n className={classNames}\n aria-disabled={isDisabled}\n aria-busy={loading || undefined}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {loading && (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className={styles.loadingContainer}\n >\n <span className={styles.spinner} aria-hidden=\"true\" />\n <span className={styles.srOnly}>Loading...</span>\n </span>\n )}\n <span aria-hidden={loading || undefined}>{children}</span>\n </button>\n );\n }\n);\n\nButton.displayName = \"Button\";\n","import React, { useId } from \"react\";\nimport styles from \"./Input.module.css\";\n\nexport interface InputProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\"> {\n /**\n * Input label (required for accessibility)\n */\n label: string;\n\n /**\n * Input type\n * @default \"text\"\n */\n type?: \"text\" | \"email\" | \"password\" | \"tel\" | \"url\" | \"search\" | \"number\";\n\n /**\n * Input size\n * @default \"medium\"\n */\n size?: \"small\" | \"medium\" | \"large\";\n\n /**\n * Error message (when present, input is marked as invalid)\n */\n error?: string;\n\n /**\n * Helper text (additional description)\n */\n helperText?: string;\n\n /**\n * Full width input\n * @default false\n */\n fullWidth?: boolean;\n\n /**\n * Hide label visually but keep it for screen readers\n * @default false\n */\n hideLabel?: boolean;\n}\n\n/**\n * Accessible Input component following WCAG 2.1 AA standards\n *\n * Features:\n * - Native <input> element for semantic HTML\n * - Proper label association via htmlFor\n * - Error states with aria-invalid and aria-describedby\n * - Helper text support\n * - Visible focus indicator\n * - Required indicator\n *\n * @example\n * ```tsx\n * <Input\n * label=\"Email\"\n * type=\"email\"\n * required\n * error=\"Invalid email address\"\n * helperText=\"We'll never share your email\"\n * />\n * ```\n */\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(\n (\n {\n label,\n type = \"text\",\n size = \"medium\",\n error,\n helperText,\n fullWidth = false,\n hideLabel = false,\n required = false,\n disabled = false,\n className,\n id: providedId,\n ...props\n },\n ref\n ) => {\n // Generate unique IDs for accessibility\n const generatedId = useId();\n const id = providedId || generatedId;\n const errorId = error ? `${id}-error` : undefined;\n const helperTextId = helperText ? `${id}-helper` : undefined;\n\n // Build aria-describedby\n const describedBy = [errorId, helperTextId].filter(Boolean).join(\" \") || undefined;\n\n const inputClasses = [\n styles.input,\n styles[size],\n error && styles.error,\n disabled && styles.disabled,\n fullWidth && styles.fullWidth,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n const labelClasses = [\n styles.label,\n hideLabel && styles.visuallyHidden,\n required && styles.required,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div className={`${styles.inputWrapper} ${fullWidth ? styles.fullWidth : \"\"}`}>\n <label htmlFor={id} className={labelClasses}>\n {label}\n {required && <span className={styles.requiredIndicator} aria-label=\"required\"> *</span>}\n </label>\n\n <input\n ref={ref}\n id={id}\n type={type}\n className={inputClasses}\n required={required}\n disabled={disabled}\n aria-invalid={error ? true : undefined}\n aria-describedby={describedBy}\n aria-required={required || undefined}\n {...props}\n />\n\n {helperText && !error && (\n <span id={helperTextId} className={styles.helperText}>\n {helperText}\n </span>\n )}\n\n {error && (\n <span id={errorId} className={styles.errorText} role=\"alert\">\n {error}\n </span>\n )}\n </div>\n );\n }\n);\n\nInput.displayName = \"Input\";\n"],"names":["Button","React","variant","size","children","disabled","loading","fullWidth","className","type","onClick","props","ref","isDisabled","handleClick","event","handleKeyDown","classNames","styles","jsxs","jsx","Input","label","error","helperText","hideLabel","required","providedId","generatedId","useId","id","errorId","helperTextId","describedBy","inputClasses","labelClasses"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -31,4 +31,63 @@ export declare interface ButtonProps extends default_2.ButtonHTMLAttributes<HTML
|
|
|
31
31
|
fullWidth?: boolean;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Accessible Input component following WCAG 2.1 AA standards
|
|
36
|
+
*
|
|
37
|
+
* Features:
|
|
38
|
+
* - Native <input> element for semantic HTML
|
|
39
|
+
* - Proper label association via htmlFor
|
|
40
|
+
* - Error states with aria-invalid and aria-describedby
|
|
41
|
+
* - Helper text support
|
|
42
|
+
* - Visible focus indicator
|
|
43
|
+
* - Required indicator
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* <Input
|
|
48
|
+
* label="Email"
|
|
49
|
+
* type="email"
|
|
50
|
+
* required
|
|
51
|
+
* error="Invalid email address"
|
|
52
|
+
* helperText="We'll never share your email"
|
|
53
|
+
* />
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare const Input: default_2.ForwardRefExoticComponent<InputProps & default_2.RefAttributes<HTMLInputElement>>;
|
|
57
|
+
|
|
58
|
+
export declare interface InputProps extends Omit<default_2.InputHTMLAttributes<HTMLInputElement>, "size"> {
|
|
59
|
+
/**
|
|
60
|
+
* Input label (required for accessibility)
|
|
61
|
+
*/
|
|
62
|
+
label: string;
|
|
63
|
+
/**
|
|
64
|
+
* Input type
|
|
65
|
+
* @default "text"
|
|
66
|
+
*/
|
|
67
|
+
type?: "text" | "email" | "password" | "tel" | "url" | "search" | "number";
|
|
68
|
+
/**
|
|
69
|
+
* Input size
|
|
70
|
+
* @default "medium"
|
|
71
|
+
*/
|
|
72
|
+
size?: "small" | "medium" | "large";
|
|
73
|
+
/**
|
|
74
|
+
* Error message (when present, input is marked as invalid)
|
|
75
|
+
*/
|
|
76
|
+
error?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Helper text (additional description)
|
|
79
|
+
*/
|
|
80
|
+
helperText?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Full width input
|
|
83
|
+
* @default false
|
|
84
|
+
*/
|
|
85
|
+
fullWidth?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Hide label visually but keep it for screen readers
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
hideLabel?: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
34
93
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,82 +1,151 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import
|
|
3
|
-
const
|
|
4
|
-
button:
|
|
5
|
-
disabled:
|
|
6
|
-
primary:
|
|
7
|
-
secondary:
|
|
8
|
-
danger:
|
|
9
|
-
small:
|
|
10
|
-
medium:
|
|
11
|
-
large:
|
|
12
|
-
fullWidth:
|
|
13
|
-
loading:
|
|
14
|
-
loadingContainer:
|
|
1
|
+
import { jsxs as _, jsx as l } from "react/jsx-runtime";
|
|
2
|
+
import v, { useId as $ } from "react";
|
|
3
|
+
const C = "_button_1003x_2", q = "_disabled_1003x_26", B = "_primary_1003x_33", T = "_secondary_1003x_46", j = "_danger_1003x_59", D = "_small_1003x_73", k = "_medium_1003x_78", w = "_large_1003x_83", H = "_fullWidth_1003x_89", O = "_loading_1003x_94", R = "_loadingContainer_1003x_98", K = "_spinner_1003x_104", E = "_spin_1003x_104", F = "_srOnly_1003x_121", n = {
|
|
4
|
+
button: C,
|
|
5
|
+
disabled: q,
|
|
6
|
+
primary: B,
|
|
7
|
+
secondary: T,
|
|
8
|
+
danger: j,
|
|
9
|
+
small: D,
|
|
10
|
+
medium: k,
|
|
11
|
+
large: w,
|
|
12
|
+
fullWidth: H,
|
|
13
|
+
loading: O,
|
|
14
|
+
loadingContainer: R,
|
|
15
15
|
spinner: K,
|
|
16
|
-
spin:
|
|
17
|
-
srOnly:
|
|
18
|
-
}, L =
|
|
16
|
+
spin: E,
|
|
17
|
+
srOnly: F
|
|
18
|
+
}, L = v.forwardRef(
|
|
19
19
|
({
|
|
20
|
-
variant:
|
|
21
|
-
size:
|
|
22
|
-
children:
|
|
23
|
-
disabled:
|
|
24
|
-
loading:
|
|
25
|
-
fullWidth:
|
|
26
|
-
className:
|
|
27
|
-
type:
|
|
28
|
-
onClick:
|
|
29
|
-
...
|
|
20
|
+
variant: m = "primary",
|
|
21
|
+
size: u = "medium",
|
|
22
|
+
children: p,
|
|
23
|
+
disabled: a = !1,
|
|
24
|
+
loading: s = !1,
|
|
25
|
+
fullWidth: d = !1,
|
|
26
|
+
className: f,
|
|
27
|
+
type: r = "button",
|
|
28
|
+
onClick: c,
|
|
29
|
+
...b
|
|
30
30
|
}, y) => {
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
33
|
-
|
|
31
|
+
const t = a || s, h = (i) => {
|
|
32
|
+
if (t) {
|
|
33
|
+
i.preventDefault();
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
},
|
|
36
|
+
c?.(i);
|
|
37
|
+
}, x = (i) => {
|
|
38
|
+
t && (i.key === "Enter" || i.key === " ") && i.preventDefault();
|
|
39
|
+
}, o = [
|
|
40
40
|
n.button,
|
|
41
|
-
n[
|
|
42
|
-
n[
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
n[m],
|
|
42
|
+
n[u],
|
|
43
|
+
s && n.loading,
|
|
44
|
+
d && n.fullWidth,
|
|
45
|
+
t && n.disabled,
|
|
46
|
+
f
|
|
47
47
|
].filter(Boolean).join(" ");
|
|
48
|
-
return /* @__PURE__ */
|
|
48
|
+
return /* @__PURE__ */ _(
|
|
49
49
|
"button",
|
|
50
50
|
{
|
|
51
51
|
ref: y,
|
|
52
|
-
type:
|
|
53
|
-
className:
|
|
54
|
-
"aria-disabled":
|
|
55
|
-
"aria-busy":
|
|
56
|
-
onClick:
|
|
57
|
-
onKeyDown:
|
|
58
|
-
...
|
|
52
|
+
type: r,
|
|
53
|
+
className: o,
|
|
54
|
+
"aria-disabled": t,
|
|
55
|
+
"aria-busy": s || void 0,
|
|
56
|
+
onClick: h,
|
|
57
|
+
onKeyDown: x,
|
|
58
|
+
...b,
|
|
59
59
|
children: [
|
|
60
|
-
|
|
60
|
+
s && /* @__PURE__ */ _(
|
|
61
61
|
"span",
|
|
62
62
|
{
|
|
63
63
|
role: "status",
|
|
64
64
|
"aria-live": "polite",
|
|
65
65
|
className: n.loadingContainer,
|
|
66
66
|
children: [
|
|
67
|
-
/* @__PURE__ */
|
|
68
|
-
/* @__PURE__ */
|
|
67
|
+
/* @__PURE__ */ l("span", { className: n.spinner, "aria-hidden": "true" }),
|
|
68
|
+
/* @__PURE__ */ l("span", { className: n.srOnly, children: "Loading..." })
|
|
69
69
|
]
|
|
70
70
|
}
|
|
71
71
|
),
|
|
72
|
-
/* @__PURE__ */
|
|
72
|
+
/* @__PURE__ */ l("span", { "aria-hidden": s || void 0, children: p })
|
|
73
73
|
]
|
|
74
74
|
}
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
);
|
|
78
78
|
L.displayName = "Button";
|
|
79
|
+
const z = "_inputWrapper_sim0c_2", A = "_fullWidth_sim0c_8", G = "_label_sim0c_13", J = "_required_sim0c_20", M = "_requiredIndicator_sim0c_24", P = "_input_sim0c_2", Q = "_small_sim0c_53", S = "_medium_sim0c_58", U = "_large_sim0c_63", V = "_error_sim0c_69", X = "_disabled_sim0c_78", Y = "_helperText_sim0c_91", Z = "_errorText_sim0c_92", ee = "_visuallyHidden_sim0c_107", e = {
|
|
80
|
+
inputWrapper: z,
|
|
81
|
+
fullWidth: A,
|
|
82
|
+
label: G,
|
|
83
|
+
required: J,
|
|
84
|
+
requiredIndicator: M,
|
|
85
|
+
input: P,
|
|
86
|
+
small: Q,
|
|
87
|
+
medium: S,
|
|
88
|
+
large: U,
|
|
89
|
+
error: V,
|
|
90
|
+
disabled: X,
|
|
91
|
+
helperText: Y,
|
|
92
|
+
errorText: Z,
|
|
93
|
+
visuallyHidden: ee
|
|
94
|
+
}, se = v.forwardRef(
|
|
95
|
+
({
|
|
96
|
+
label: m,
|
|
97
|
+
type: u = "text",
|
|
98
|
+
size: p = "medium",
|
|
99
|
+
error: a,
|
|
100
|
+
helperText: s,
|
|
101
|
+
fullWidth: d = !1,
|
|
102
|
+
hideLabel: f = !1,
|
|
103
|
+
required: r = !1,
|
|
104
|
+
disabled: c = !1,
|
|
105
|
+
className: b,
|
|
106
|
+
id: y,
|
|
107
|
+
...t
|
|
108
|
+
}, h) => {
|
|
109
|
+
const x = $(), o = y || x, i = a ? `${o}-error` : void 0, g = s ? `${o}-helper` : void 0, N = [i, g].filter(Boolean).join(" ") || void 0, W = [
|
|
110
|
+
e.input,
|
|
111
|
+
e[p],
|
|
112
|
+
a && e.error,
|
|
113
|
+
c && e.disabled,
|
|
114
|
+
d && e.fullWidth,
|
|
115
|
+
b
|
|
116
|
+
].filter(Boolean).join(" "), I = [
|
|
117
|
+
e.label,
|
|
118
|
+
f && e.visuallyHidden,
|
|
119
|
+
r && e.required
|
|
120
|
+
].filter(Boolean).join(" ");
|
|
121
|
+
return /* @__PURE__ */ _("div", { className: `${e.inputWrapper} ${d ? e.fullWidth : ""}`, children: [
|
|
122
|
+
/* @__PURE__ */ _("label", { htmlFor: o, className: I, children: [
|
|
123
|
+
m,
|
|
124
|
+
r && /* @__PURE__ */ l("span", { className: e.requiredIndicator, "aria-label": "required", children: " *" })
|
|
125
|
+
] }),
|
|
126
|
+
/* @__PURE__ */ l(
|
|
127
|
+
"input",
|
|
128
|
+
{
|
|
129
|
+
ref: h,
|
|
130
|
+
id: o,
|
|
131
|
+
type: u,
|
|
132
|
+
className: W,
|
|
133
|
+
required: r,
|
|
134
|
+
disabled: c,
|
|
135
|
+
"aria-invalid": a ? !0 : void 0,
|
|
136
|
+
"aria-describedby": N,
|
|
137
|
+
"aria-required": r || void 0,
|
|
138
|
+
...t
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
s && !a && /* @__PURE__ */ l("span", { id: g, className: e.helperText, children: s }),
|
|
142
|
+
a && /* @__PURE__ */ l("span", { id: i, className: e.errorText, role: "alert", children: a })
|
|
143
|
+
] });
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
se.displayName = "Input";
|
|
79
147
|
export {
|
|
80
|
-
L as Button
|
|
148
|
+
L as Button,
|
|
149
|
+
se as Input
|
|
81
150
|
};
|
|
82
151
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/Button/Button.tsx"],"sourcesContent":["// src/atoms/Button/Button.tsx\nimport React from \"react\";\nimport styles from \"./Button.module.css\";\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: \"primary\" | \"secondary\" | \"danger\";\n size?: \"small\" | \"medium\" | \"large\";\n children: React.ReactNode;\n loading?: boolean;\n fullWidth?: boolean;\n}\n\n/**\n * Accessible button component following WCAG 2.1 AA standards\n *\n * Features:\n * - Native <button> element for semantic HTML\n * - Full keyboard support (Tab, Enter, Space)\n * - Screen reader announcements for loading state\n * - Visible focus indicator\n * - Color contrast compliant\n *\n * @example\n * ```tsx\n * <Button variant=\"primary\" onClick={handleClick}>\n * Click me\n * </Button>\n *\n * <Button loading disabled>\n * Submitting...\n * </Button>\n * ```\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = \"primary\",\n size = \"medium\",\n children,\n disabled = false,\n loading = false,\n fullWidth = false,\n className,\n type = \"button\",\n onClick,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n\n // Handle click with disabled check (prevents action but keeps focusable)\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (isDisabled) {\n event.preventDefault();\n return;\n }\n onClick?.(event);\n };\n\n // Handle keyboard events to prevent action when disabled\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (isDisabled && (event.key === \"Enter\" || event.key === \" \")) {\n event.preventDefault();\n }\n };\n\n const classNames = [\n styles.button,\n styles[variant],\n styles[size],\n loading && styles.loading,\n fullWidth && styles.fullWidth,\n isDisabled && styles.disabled,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <button\n ref={ref}\n type={type}\n className={classNames}\n aria-disabled={isDisabled}\n aria-busy={loading || undefined}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {loading && (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className={styles.loadingContainer}\n >\n <span className={styles.spinner} aria-hidden=\"true\" />\n <span className={styles.srOnly}>Loading...</span>\n </span>\n )}\n <span aria-hidden={loading || undefined}>{children}</span>\n </button>\n );\n }\n);\n\nButton.displayName = \"Button\";\n"],"names":["Button","React","variant","size","children","disabled","loading","fullWidth","className","type","onClick","props","ref","isDisabled","handleClick","event","handleKeyDown","classNames","styles","jsxs","jsx"],"mappings":";;;;;;;;;;;;;;;;;GAkCaA,IAASC,EAAM;AAAA,EAC1B,CACE;AAAA,IACE,SAAAC,IAAU;AAAA,IACV,MAAAC,IAAO;AAAA,IACP,UAAAC;AAAA,IACA,UAAAC,IAAW;AAAA,IACX,SAAAC,IAAU;AAAA,IACV,WAAAC,IAAY;AAAA,IACZ,WAAAC;AAAA,IACA,MAAAC,IAAO;AAAA,IACP,SAAAC;AAAA,IACA,GAAGC;AAAA,EAAA,GAELC,MACG;AACH,UAAMC,IAAaR,KAAYC,GAGzBQ,IAAc,CAACC,MAA+C;AAClE,UAAIF,GAAY;AACd,QAAAE,EAAM,eAAA;AACN;AAAA,MACF;AACA,MAAAL,IAAUK,CAAK;AAAA,IACjB,GAGMC,IAAgB,CAACD,MAAkD;AACvE,MAAIF,MAAeE,EAAM,QAAQ,WAAWA,EAAM,QAAQ,QACxDA,EAAM,eAAA;AAAA,IAEV,GAEME,IAAa;AAAA,MACjBC,EAAO;AAAA,MACPA,EAAOhB,CAAO;AAAA,MACdgB,EAAOf,CAAI;AAAA,MACXG,KAAWY,EAAO;AAAA,MAClBX,KAAaW,EAAO;AAAA,MACpBL,KAAcK,EAAO;AAAA,MACrBV;AAAA,IAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,WACE,gBAAAW;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAP;AAAA,QACA,MAAAH;AAAA,QACA,WAAWQ;AAAA,QACX,iBAAeJ;AAAA,QACf,aAAWP,KAAW;AAAA,QACtB,SAASQ;AAAA,QACT,WAAWE;AAAA,QACV,GAAGL;AAAA,QAEH,UAAA;AAAA,UAAAL,KACC,gBAAAa;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,aAAU;AAAA,cACV,WAAWD,EAAO;AAAA,cAElB,UAAA;AAAA,gBAAA,gBAAAE,EAAC,QAAA,EAAK,WAAWF,EAAO,SAAS,eAAY,QAAO;AAAA,gBACpD,gBAAAE,EAAC,QAAA,EAAK,WAAWF,EAAO,QAAQ,UAAA,aAAA,CAAU;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAG9C,gBAAAE,EAAC,QAAA,EAAK,eAAad,KAAW,QAAY,UAAAF,EAAA,CAAS;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGzD;AACF;AAEAJ,EAAO,cAAc;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/atoms/Button/Button.tsx","../src/atoms/Input/Input.tsx"],"sourcesContent":["// src/atoms/Button/Button.tsx\nimport React from \"react\";\nimport styles from \"./Button.module.css\";\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: \"primary\" | \"secondary\" | \"danger\";\n size?: \"small\" | \"medium\" | \"large\";\n children: React.ReactNode;\n loading?: boolean;\n fullWidth?: boolean;\n}\n\n/**\n * Accessible button component following WCAG 2.1 AA standards\n *\n * Features:\n * - Native <button> element for semantic HTML\n * - Full keyboard support (Tab, Enter, Space)\n * - Screen reader announcements for loading state\n * - Visible focus indicator\n * - Color contrast compliant\n *\n * @example\n * ```tsx\n * <Button variant=\"primary\" onClick={handleClick}>\n * Click me\n * </Button>\n *\n * <Button loading disabled>\n * Submitting...\n * </Button>\n * ```\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = \"primary\",\n size = \"medium\",\n children,\n disabled = false,\n loading = false,\n fullWidth = false,\n className,\n type = \"button\",\n onClick,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n\n // Handle click with disabled check (prevents action but keeps focusable)\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (isDisabled) {\n event.preventDefault();\n return;\n }\n onClick?.(event);\n };\n\n // Handle keyboard events to prevent action when disabled\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (isDisabled && (event.key === \"Enter\" || event.key === \" \")) {\n event.preventDefault();\n }\n };\n\n const classNames = [\n styles.button,\n styles[variant],\n styles[size],\n loading && styles.loading,\n fullWidth && styles.fullWidth,\n isDisabled && styles.disabled,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <button\n ref={ref}\n type={type}\n className={classNames}\n aria-disabled={isDisabled}\n aria-busy={loading || undefined}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {loading && (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className={styles.loadingContainer}\n >\n <span className={styles.spinner} aria-hidden=\"true\" />\n <span className={styles.srOnly}>Loading...</span>\n </span>\n )}\n <span aria-hidden={loading || undefined}>{children}</span>\n </button>\n );\n }\n);\n\nButton.displayName = \"Button\";\n","import React, { useId } from \"react\";\nimport styles from \"./Input.module.css\";\n\nexport interface InputProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\"> {\n /**\n * Input label (required for accessibility)\n */\n label: string;\n\n /**\n * Input type\n * @default \"text\"\n */\n type?: \"text\" | \"email\" | \"password\" | \"tel\" | \"url\" | \"search\" | \"number\";\n\n /**\n * Input size\n * @default \"medium\"\n */\n size?: \"small\" | \"medium\" | \"large\";\n\n /**\n * Error message (when present, input is marked as invalid)\n */\n error?: string;\n\n /**\n * Helper text (additional description)\n */\n helperText?: string;\n\n /**\n * Full width input\n * @default false\n */\n fullWidth?: boolean;\n\n /**\n * Hide label visually but keep it for screen readers\n * @default false\n */\n hideLabel?: boolean;\n}\n\n/**\n * Accessible Input component following WCAG 2.1 AA standards\n *\n * Features:\n * - Native <input> element for semantic HTML\n * - Proper label association via htmlFor\n * - Error states with aria-invalid and aria-describedby\n * - Helper text support\n * - Visible focus indicator\n * - Required indicator\n *\n * @example\n * ```tsx\n * <Input\n * label=\"Email\"\n * type=\"email\"\n * required\n * error=\"Invalid email address\"\n * helperText=\"We'll never share your email\"\n * />\n * ```\n */\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(\n (\n {\n label,\n type = \"text\",\n size = \"medium\",\n error,\n helperText,\n fullWidth = false,\n hideLabel = false,\n required = false,\n disabled = false,\n className,\n id: providedId,\n ...props\n },\n ref\n ) => {\n // Generate unique IDs for accessibility\n const generatedId = useId();\n const id = providedId || generatedId;\n const errorId = error ? `${id}-error` : undefined;\n const helperTextId = helperText ? `${id}-helper` : undefined;\n\n // Build aria-describedby\n const describedBy = [errorId, helperTextId].filter(Boolean).join(\" \") || undefined;\n\n const inputClasses = [\n styles.input,\n styles[size],\n error && styles.error,\n disabled && styles.disabled,\n fullWidth && styles.fullWidth,\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n const labelClasses = [\n styles.label,\n hideLabel && styles.visuallyHidden,\n required && styles.required,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div className={`${styles.inputWrapper} ${fullWidth ? styles.fullWidth : \"\"}`}>\n <label htmlFor={id} className={labelClasses}>\n {label}\n {required && <span className={styles.requiredIndicator} aria-label=\"required\"> *</span>}\n </label>\n\n <input\n ref={ref}\n id={id}\n type={type}\n className={inputClasses}\n required={required}\n disabled={disabled}\n aria-invalid={error ? true : undefined}\n aria-describedby={describedBy}\n aria-required={required || undefined}\n {...props}\n />\n\n {helperText && !error && (\n <span id={helperTextId} className={styles.helperText}>\n {helperText}\n </span>\n )}\n\n {error && (\n <span id={errorId} className={styles.errorText} role=\"alert\">\n {error}\n </span>\n )}\n </div>\n );\n }\n);\n\nInput.displayName = \"Input\";\n"],"names":["Button","React","variant","size","children","disabled","loading","fullWidth","className","type","onClick","props","ref","isDisabled","handleClick","event","handleKeyDown","classNames","styles","jsxs","jsx","Input","label","error","helperText","hideLabel","required","providedId","generatedId","useId","id","errorId","helperTextId","describedBy","inputClasses","labelClasses"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flavia-dev/a11y-ui-kit-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Enterprise-grade accessible React components library following WCAG 2.1 AA/AAA standards. Production-ready, type-safe, and fully customizable UI components with built-in accessibility.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
11
12
|
"import": "./dist/index.js",
|
|
12
|
-
"require": "./dist/index.cjs"
|
|
13
|
-
"types": "./dist/index.d.ts"
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
14
|
},
|
|
15
15
|
"./styles.css": "./dist/a11y-ui-kit-react.css"
|
|
16
16
|
},
|
|
@@ -20,8 +20,28 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"dev": "vite",
|
|
22
22
|
"build": "tsc && vite build",
|
|
23
|
-
"test": "vitest",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"test:unit": "vitest run --reporter=verbose \"src/atoms/**/*.utils.test.ts\"",
|
|
26
|
+
"test:component": "vitest run --reporter=verbose --run src/atoms/Input/Input.test.tsx src/atoms/Button/Button.test.tsx",
|
|
27
|
+
"test:a11y": "vitest run --reporter=verbose --run src/atoms/Input/Input.a11y.test.tsx src/atoms/Button/Button.a11y.test.tsx",
|
|
28
|
+
"test:contrast": "vitest run --reporter=verbose --run src/test/color-contrast.test.ts",
|
|
29
|
+
"test:coverage": "vitest run --coverage",
|
|
24
30
|
"test:ui": "vitest --ui",
|
|
31
|
+
"test:visual": "playwright test tests/visual",
|
|
32
|
+
"test:visual:update": "playwright test tests/visual --update-snapshots",
|
|
33
|
+
"test:visual:ui": "playwright test tests/visual --ui",
|
|
34
|
+
"test:visual:button": "playwright test tests/visual/Button.visual.test.ts",
|
|
35
|
+
"test:visual:input": "playwright test tests/visual/Input.visual.test.ts",
|
|
36
|
+
"test:visual:button:update": "playwright test tests/visual/Button.visual.test.ts --update-snapshots",
|
|
37
|
+
"test:visual:input:update": "playwright test tests/visual/Input.visual.test.ts --update-snapshots",
|
|
38
|
+
"test:e2e": "playwright test tests/e2e",
|
|
39
|
+
"test:e2e:button": "playwright test tests/e2e/Button.e2e.test.ts",
|
|
40
|
+
"test:e2e:input": "playwright test tests/e2e/Input.e2e.test.ts",
|
|
41
|
+
"test:e2e:ui": "playwright test tests/e2e --ui",
|
|
42
|
+
"test:playwright": "playwright test",
|
|
43
|
+
"storybook": "storybook dev -p 6006",
|
|
44
|
+
"build-storybook": "storybook build",
|
|
25
45
|
"typecheck": "tsc --noEmit"
|
|
26
46
|
},
|
|
27
47
|
"keywords": [
|
|
@@ -49,18 +69,29 @@
|
|
|
49
69
|
"react-dom": ">=18.0.0"
|
|
50
70
|
},
|
|
51
71
|
"devDependencies": {
|
|
72
|
+
"@axe-core/react": "^4.11.0",
|
|
73
|
+
"@playwright/test": "^1.57.0",
|
|
74
|
+
"@storybook/addon-a11y": "^8.6.14",
|
|
75
|
+
"@storybook/addon-essentials": "^8.6.14",
|
|
76
|
+
"@storybook/addon-links": "^8.6.14",
|
|
77
|
+
"@storybook/react": "^8.6.14",
|
|
78
|
+
"@storybook/react-vite": "^8.6.14",
|
|
52
79
|
"@testing-library/dom": "^10.4.1",
|
|
53
80
|
"@testing-library/jest-dom": "^6.9.1",
|
|
54
81
|
"@testing-library/react": "^16.3.1",
|
|
55
82
|
"@testing-library/user-event": "^14.6.1",
|
|
83
|
+
"@types/jest-axe": "^3.5.9",
|
|
56
84
|
"@types/node": "^25.0.2",
|
|
57
85
|
"@types/react": "^19.2.7",
|
|
58
86
|
"@types/react-dom": "^19.2.3",
|
|
59
87
|
"@vitejs/plugin-react": "^5.1.2",
|
|
60
88
|
"@vitest/ui": "^4.0.15",
|
|
89
|
+
"axe-core": "^4.11.0",
|
|
90
|
+
"jest-axe": "^10.0.0",
|
|
61
91
|
"jsdom": "^27.3.0",
|
|
62
92
|
"react": "^19.2.3",
|
|
63
93
|
"react-dom": "^19.2.3",
|
|
94
|
+
"storybook": "^8.6.14",
|
|
64
95
|
"typescript": "^5.9.3",
|
|
65
96
|
"vite": "^7.3.0",
|
|
66
97
|
"vite-plugin-dts": "^4.5.4",
|