@dezkareid/components 1.0.1 → 1.0.3

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.
Files changed (48) hide show
  1. package/dist/components.min.css +1 -1
  2. package/dist/css/tag.module.css.js +1 -1
  3. package/dist/css/theme-toggle.module.css.js +1 -1
  4. package/dist/react/Button/index.d.ts +3 -3
  5. package/dist/react/Button/index.d.ts.map +1 -1
  6. package/dist/react/Button/index.js +1 -1
  7. package/dist/react/Button/index.js.map +1 -1
  8. package/dist/react/Card/index.d.ts +3 -3
  9. package/dist/react/Card/index.d.ts.map +1 -1
  10. package/dist/react/Card/index.js.map +1 -1
  11. package/dist/react/Tag/index.d.ts +3 -3
  12. package/dist/react/Tag/index.d.ts.map +1 -1
  13. package/dist/react/Tag/index.js.map +1 -1
  14. package/dist/react/ThemeToggle/index.d.ts.map +1 -1
  15. package/dist/react/ThemeToggle/index.js +6 -2
  16. package/dist/react/ThemeToggle/index.js.map +1 -1
  17. package/dist/shared/js/theme.js +4 -4
  18. package/dist/shared/js/theme.js.map +1 -1
  19. package/dist/shared/types/button.d.ts +1 -1
  20. package/dist/shared/types/button.d.ts.map +1 -1
  21. package/dist/shared/types/card.d.ts +2 -1
  22. package/dist/shared/types/card.d.ts.map +1 -1
  23. package/dist/shared/types/tag.d.ts +2 -2
  24. package/dist/shared/types/tag.d.ts.map +1 -1
  25. package/dist/shared/types/theme-toggle.d.ts +1 -2
  26. package/dist/shared/types/theme-toggle.d.ts.map +1 -1
  27. package/package.json +14 -5
  28. package/src/astro/Button/index.astro +1 -1
  29. package/src/astro/ThemeToggle/index.astro +49 -11
  30. package/src/css/button.module.css +49 -14
  31. package/src/css/card.module.css +3 -1
  32. package/src/css/tag.module.css +22 -7
  33. package/src/css/theme-toggle.module.css +26 -0
  34. package/src/react/Button/index.test.tsx +17 -7
  35. package/src/react/Button/index.tsx +4 -3
  36. package/src/react/Card/index.test.tsx +13 -3
  37. package/src/react/Card/index.tsx +3 -3
  38. package/src/react/Tag/index.test.tsx +10 -0
  39. package/src/react/Tag/index.tsx +3 -3
  40. package/src/react/ThemeToggle/index.test.tsx +25 -2
  41. package/src/react/ThemeToggle/index.tsx +57 -9
  42. package/src/shared/js/theme.ts +4 -4
  43. package/src/shared/types/button.ts +1 -1
  44. package/src/shared/types/card.ts +2 -1
  45. package/src/shared/types/tag.ts +2 -2
  46. package/src/shared/types/theme-toggle.ts +1 -3
  47. package/src/vue/Button/index.vue +1 -1
  48. package/src/vue/ThemeToggle/index.vue +52 -9
@@ -1 +1 @@
1
- .button-module_button__-i1WR{align-items:center;border:none;border-radius:var(--spacing-4);cursor:pointer;display:inline-flex;font-family:var(--font-family-base);font-weight:var(--font-weight-medium);justify-content:center;line-height:var(--font-line-height-none);text-decoration:none;transition:opacity .15s ease;white-space:nowrap}.button-module_button--sm__oyPIp,.button-module_button--small__seN6c{font-size:var(--font-size-200);padding:var(--spacing-4) var(--spacing-12)}.button-module_button--md__KsJg5,.button-module_button--medium__4vh1u{font-size:var(--font-size-300);padding:var(--spacing-8) var(--spacing-24)}.button-module_button--large__g15uD,.button-module_button--lg__kuuR5{font-size:var(--font-size-400);padding:var(--spacing-12) var(--spacing-32)}.button-module_button--primary__5NKaZ{background-color:var(--color-primary);border:1px solid transparent;color:var(--color-text-inverse)}.button-module_button--primary__5NKaZ:hover:not(.button-module_button--disabled__5X7It){opacity:.88}.button-module_button--outline__inPG2,.button-module_button--secondary__7CZqp{background-color:transparent;border:1px solid var(--color-primary);color:var(--color-primary)}.button-module_button--outline__inPG2:hover:not(.button-module_button--disabled__5X7It),.button-module_button--secondary__7CZqp:hover:not(.button-module_button--disabled__5X7It){background-color:var(--color-background-secondary)}.button-module_button--success__Y-GUS{background-color:var(--color-success);border:1px solid transparent;color:var(--color-text-inverse)}.button-module_button--success__Y-GUS:hover:not(.button-module_button--disabled__5X7It){opacity:.88}.button-module_button--ghost__7k3yA{background-color:transparent;border:1px solid transparent;color:var(--color-text-primary)}.button-module_button--ghost__7k3yA:hover:not(.button-module_button--disabled__5X7It){background-color:var(--color-background-secondary)}.button-module_button--disabled__5X7It{cursor:not-allowed;opacity:.4;pointer-events:none}.tag-module_tag__DqU0X{align-items:center;border-radius:var(--spacing-4);display:inline-flex;font-family:var(--font-family-base);font-size:var(--font-size-100);font-weight:var(--font-weight-medium);line-height:var(--font-line-height-none);padding:var(--spacing-4) var(--spacing-8)}.tag-module_tag--default__s9pfU{background-color:var(--color-background-secondary);color:var(--color-text-primary)}.tag-module_tag--success__e8fMj{background-color:var(--color-success);color:var(--color-text-inverse)}.tag-module_tag--danger__9b-l4{background-color:var(--color-danger);color:var(--color-text-inverse)}.card-module_card__KEaH3{background-color:var(--color-background-secondary);border-radius:var(--spacing-8);color:var(--color-text-primary);display:block;padding:var(--spacing-24);width:100%}.card-module_card--raised__hBodH{box-shadow:0 2px 8px rgba(0,0,0,.12)}.card-module_card--flat__vLG1l{box-shadow:none}.theme-toggle-module_theme-toggle__pBmqG{align-items:center;background-color:transparent;border:none;border:1px solid var(--color-background-secondary);border-radius:var(--spacing-4);color:var(--color-text-primary);cursor:pointer;display:inline-flex;font-family:var(--font-family-base);font-size:var(--font-size-200);font-weight:var(--font-weight-medium);gap:var(--spacing-8);justify-content:center;line-height:var(--font-line-height-none);padding:var(--spacing-8) var(--spacing-12)}.theme-toggle-module_theme-toggle__pBmqG:hover{background-color:var(--color-background-secondary)}.theme-toggle-module_theme-toggle--dark__9yf3H{border-color:var(--color-primary);color:var(--color-primary)}
1
+ .button-module_button__-i1WR{align-items:center;border:none;border-radius:var(--border-radius-medium);cursor:pointer;display:inline-flex;font-family:var(--font-family-base);font-size:var(--font-size-300);font-weight:var(--font-weight-medium);gap:var(--spacing-8);justify-content:center;letter-spacing:var(--font-letter-spacing-wide);line-height:var(--font-line-height-none);text-decoration:none;transition:background-color .15s ease,color .15s ease,border-color .15s ease,box-shadow .15s ease,opacity .15s ease;white-space:nowrap}.button-module_button__-i1WR:focus-visible{box-shadow:0 0 0 2px var(--color-background-primary),0 0 0 4px var(--color-primary);outline:none}.button-module_button--sm__oyPIp,.button-module_button--small__seN6c{border-radius:var(--border-radius-small);font-size:var(--font-size-200);padding:var(--spacing-4) var(--spacing-12)}.button-module_button--md__KsJg5,.button-module_button--medium__4vh1u{font-size:var(--font-size-300);padding:var(--spacing-8) var(--spacing-24)}.button-module_button--large__g15uD,.button-module_button--lg__kuuR5{border-radius:var(--border-radius-large);font-size:var(--font-size-400);padding:var(--spacing-12) var(--spacing-32)}.button-module_button--primary__5NKaZ{--button-primary-color-background:var(--color-primary);--button-primary-color-text:var(--color-text-inverse);background-color:var(--button-primary-color-background);border:1px solid transparent;color:var(--button-primary-color-text)}.button-module_button--primary__5NKaZ:hover:not(.button-module_button--disabled__5X7It){box-shadow:var(--shadow-card-hover);opacity:.92}.button-module_button--outline__inPG2,.button-module_button--secondary__7CZqp{--button-secondary-color-background:transparent;--button-secondary-color-text:var(--color-primary);--button-secondary-color-border:var(--color-primary);background-color:var(--button-secondary-color-background);border:1px solid var(--button-secondary-color-border);color:var(--button-secondary-color-text)}.button-module_button--outline__inPG2:hover:not(.button-module_button--disabled__5X7It),.button-module_button--secondary__7CZqp:hover:not(.button-module_button--disabled__5X7It){background-color:var(--color-background-secondary);box-shadow:var(--shadow-subtle)}.button-module_button--success__Y-GUS{--button-success-color-background:var(--color-success);--button-success-color-text:var(--color-text-primary);background-color:var(--button-success-color-background);border:1px solid transparent;color:var(--button-success-color-text)}.button-module_button--success__Y-GUS:hover:not(.button-module_button--disabled__5X7It){box-shadow:var(--shadow-card-hover);opacity:.92}.button-module_button--ghost__7k3yA{--button-ghost-color-background:transparent;--button-ghost-color-text:var(--color-text-primary);background-color:var(--button-ghost-color-background);border:1px solid transparent;color:var(--button-ghost-color-text)}.button-module_button--ghost__7k3yA:hover:not(.button-module_button--disabled__5X7It){background-color:var(--color-background-secondary);border-color:var(--color-background-secondary)}.button-module_button--disabled__5X7It{cursor:not-allowed;opacity:.4;pointer-events:none}.tag-module_tag__DqU0X{align-items:center;border-radius:var(--spacing-12);display:inline-flex;font-family:var(--font-family-base);font-size:var(--font-size-100);font-weight:var(--font-weight-medium);line-height:var(--font-line-height-none);padding:var(--spacing-4) var(--spacing-8)}.tag-module_tag--default__s9pfU{--tag-default-color-background:var(--color-background-secondary);--tag-default-color-text:var(--color-text-primary);background-color:var(--tag-default-color-background);color:var(--tag-default-color-text)}.tag-module_tag--success__e8fMj{--tag-success-color-background:var(--color-success);--tag-success-color-text:var(--color-text-primary);background-color:var(--tag-success-color-background);color:var(--tag-success-color-text)}.tag-module_tag--danger__9b-l4{--tag-danger-color-background:var(--color-danger);--tag-danger-color-text:var(--color-text-primary);background-color:var(--tag-danger-color-background);color:var(--tag-danger-color-text)}.tag-module_tag--warning__aHbOj{--tag-warning-color-background:var(--color-warning);--tag-warning-color-text:var(--color-text-primary);background-color:var(--tag-warning-color-background);color:var(--tag-warning-color-text)}.card-module_card__KEaH3{background-color:var(--color-background-secondary);border-radius:var(--spacing-8);color:var(--color-text-primary);display:block;padding:var(--spacing-24);width:100%}.card-module_card--raised__hBodH{box-shadow:0 2px 8px rgba(0,0,0,.12)}.card-module_card--flat__vLG1l{border:1px solid var(--color-background-secondary);box-shadow:none}.theme-toggle-module_theme-toggle__pBmqG{align-items:center;background-color:transparent;border:none;border:1px solid var(--color-background-secondary);border-radius:var(--spacing-4);color:var(--color-text-primary);cursor:pointer;display:inline-flex;font-family:var(--font-family-base);font-size:var(--font-size-200);font-weight:var(--font-weight-medium);gap:var(--spacing-8);justify-content:center;line-height:var(--font-line-height-none);padding:var(--spacing-8) var(--spacing-12)}.theme-toggle-module_theme-toggle__pBmqG:hover{background-color:var(--color-background-secondary)}.theme-toggle-module_theme-toggle--dark__9yf3H{border-color:var(--color-primary);color:var(--color-primary)}.theme-toggle-module_theme-toggle__icon__YJaUi{flex-shrink:0;height:1em;width:1em}.theme-toggle-module_theme-toggle__wrapper__Q0frz{display:inline-flex;position:relative}.theme-toggle-module_sr-only__OgD4W{clip:rect(0,0,0,0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}
@@ -1,4 +1,4 @@
1
- var styles = {"tag":"tag-module_tag__DqU0X","tag--default":"tag-module_tag--default__s9pfU","tag--success":"tag-module_tag--success__e8fMj","tag--danger":"tag-module_tag--danger__9b-l4"};
1
+ var styles = {"tag":"tag-module_tag__DqU0X","tag--default":"tag-module_tag--default__s9pfU","tag--success":"tag-module_tag--success__e8fMj","tag--danger":"tag-module_tag--danger__9b-l4","tag--warning":"tag-module_tag--warning__aHbOj"};
2
2
 
3
3
  export { styles as default };
4
4
  //# sourceMappingURL=tag.module.css.js.map
@@ -1,4 +1,4 @@
1
- var styles = {"theme-toggle":"theme-toggle-module_theme-toggle__pBmqG","theme-toggle--dark":"theme-toggle-module_theme-toggle--dark__9yf3H"};
1
+ var styles = {"theme-toggle":"theme-toggle-module_theme-toggle__pBmqG","theme-toggle--dark":"theme-toggle-module_theme-toggle--dark__9yf3H","theme-toggle__icon":"theme-toggle-module_theme-toggle__icon__YJaUi","theme-toggle__wrapper":"theme-toggle-module_theme-toggle__wrapper__Q0frz","sr-only":"theme-toggle-module_sr-only__OgD4W"};
2
2
 
3
3
  export { styles as default };
4
4
  //# sourceMappingURL=theme-toggle.module.css.js.map
@@ -1,6 +1,6 @@
1
1
  import type { ButtonHTMLAttributes } from 'react';
2
- import type { ButtonProps } from '../../shared/types/button';
3
- type Props = ButtonProps & ButtonHTMLAttributes<HTMLButtonElement>;
4
- export declare function Button({ variant, size, disabled, children, className, ...rest }: Props): import("react/jsx-runtime").JSX.Element;
2
+ import type { ButtonProperties } from '../../shared/types/button';
3
+ type Properties = ButtonProperties & ButtonHTMLAttributes<HTMLButtonElement>;
4
+ export declare function Button({ variant, size, disabled, children, className, ...rest }: Properties): import("react/jsx-runtime").JSX.Element;
5
5
  export {};
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/Button/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAG7D,KAAK,KAAK,GAAG,WAAW,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;AAEnE,wBAAgB,MAAM,CAAC,EACrB,OAAmB,EACnB,IAAW,EACX,QAAgB,EAChB,QAAQ,EACR,SAAS,EACT,GAAG,IAAI,EACR,EAAE,KAAK,2CAgBP"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/Button/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAElD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,KAAK,UAAU,GAAG,gBAAgB,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;AAE7E,wBAAgB,MAAM,CAAC,EACrB,OAAmB,EACnB,IAAW,EACX,QAAgB,EAChB,QAAQ,EACR,SAAS,EACT,GAAG,IAAI,EACR,EAAE,UAAU,2CAiBZ"}
@@ -3,7 +3,7 @@ import cx from '../../_virtual/index.js';
3
3
  import styles from '../../css/button.module.css.js';
4
4
 
5
5
  function Button({ variant = 'primary', size = 'md', disabled = false, children, className, ...rest }) {
6
- return (jsx("button", { className: cx(styles.button, styles[`button--${variant}`], styles[`button--${size}`], disabled && styles['button--disabled'], className), disabled: disabled, ...rest, children: children }));
6
+ return (jsx("button", { className: cx(styles.button, styles[`button--${variant}`], styles[`button--${size}`], disabled && styles['button--disabled'], className), disabled: disabled, "aria-disabled": disabled, ...rest, children: children }));
7
7
  }
8
8
 
9
9
  export { Button };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/react/Button/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;;AAOM,SAAU,MAAM,CAAC,EACrB,OAAO,GAAG,SAAS,EACnB,IAAI,GAAG,IAAI,EACX,QAAQ,GAAG,KAAK,EAChB,QAAQ,EACR,SAAS,EACT,GAAG,IAAI,EACD,EAAA;AACN,IAAA,QACEA,GAAA,CAAA,QAAA,EAAA,EACE,SAAS,EAAE,EAAE,CACX,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,WAAW,OAAO,CAAA,CAAE,CAAC,EAC5B,MAAM,CAAC,CAAA,QAAA,EAAW,IAAI,EAAE,CAAC,EACzB,QAAQ,IAAI,MAAM,CAAC,kBAAkB,CAAC,EACtC,SAAS,CACV,EACD,QAAQ,EAAE,QAAQ,EAAA,GACd,IAAI,YAEP,QAAQ,EAAA,CACF;AAEb;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/react/Button/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;;AAOM,SAAU,MAAM,CAAC,EACrB,OAAO,GAAG,SAAS,EACnB,IAAI,GAAG,IAAI,EACX,QAAQ,GAAG,KAAK,EAChB,QAAQ,EACR,SAAS,EACT,GAAG,IAAI,EACI,EAAA;IACX,QACEA,gBACE,SAAS,EAAE,EAAE,CACX,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,CAAA,QAAA,EAAW,OAAO,CAAA,CAAE,CAAC,EAC5B,MAAM,CAAC,WAAW,IAAI,CAAA,CAAE,CAAC,EACzB,QAAQ,IAAI,MAAM,CAAC,kBAAkB,CAAC,EACtC,SAAS,CACV,EACD,QAAQ,EAAE,QAAQ,mBACH,QAAQ,EAAA,GACnB,IAAI,EAAA,QAAA,EAEP,QAAQ,EAAA,CACF;AAEb;;;;"}
@@ -1,6 +1,6 @@
1
1
  import type { HTMLAttributes } from 'react';
2
- import type { CardProps } from '../../shared/types/card';
3
- type Props = CardProps & HTMLAttributes<HTMLDivElement>;
4
- export declare function Card({ elevation, children, className, ...rest }: Props): import("react/jsx-runtime").JSX.Element;
2
+ import type { CardProperties } from '../../shared/types/card';
3
+ type Properties = CardProperties & HTMLAttributes<HTMLDivElement>;
4
+ export declare function Card({ elevation, children, className, ...rest }: Properties): import("react/jsx-runtime").JSX.Element;
5
5
  export {};
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/Card/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGzD,KAAK,KAAK,GAAG,SAAS,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;AAExD,wBAAgB,IAAI,CAAC,EAAE,SAAoB,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAMjF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/Card/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAG9D,KAAK,UAAU,GAAG,cAAc,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;AAElE,wBAAgB,IAAI,CAAC,EAAE,SAAoB,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,UAAU,2CAMtF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/react/Card/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;;AAOM,SAAU,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAS,EAAA;IAChF,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,SAAS,CAAA,CAAE,CAAC,EAAE,SAAS,CAAC,EAAA,GAAM,IAAI,EAAA,QAAA,EAC/E,QAAQ,EAAA,CACL;AAEV;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/react/Card/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;;AAOM,SAAU,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAc,EAAA;IACrF,QACEA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,SAAS,CAAA,CAAE,CAAC,EAAE,SAAS,CAAC,EAAA,GAAM,IAAI,EAAA,QAAA,EAC/E,QAAQ,EAAA,CACL;AAEV;;;;"}
@@ -1,6 +1,6 @@
1
1
  import type { HTMLAttributes } from 'react';
2
- import type { TagProps } from '../../shared/types/tag';
3
- type Props = TagProps & HTMLAttributes<HTMLSpanElement>;
4
- export declare function Tag({ variant, children, className, ...rest }: Props): import("react/jsx-runtime").JSX.Element;
2
+ import type { TagProperties } from '../../shared/types/tag';
3
+ type Properties = TagProperties & HTMLAttributes<HTMLSpanElement>;
4
+ export declare function Tag({ variant, children, className, ...rest }: Properties): import("react/jsx-runtime").JSX.Element;
5
5
  export {};
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/Tag/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD,KAAK,KAAK,GAAG,QAAQ,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC;AAExD,wBAAgB,GAAG,CAAC,EAAE,OAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAM/E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/Tag/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAG5D,KAAK,UAAU,GAAG,aAAa,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC;AAElE,wBAAgB,GAAG,CAAC,EAAE,OAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,UAAU,2CAMpF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/react/Tag/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;;AAOM,SAAU,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAS,EAAA;IAC9E,QACEA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,OAAO,CAAA,CAAE,CAAC,EAAE,SAAS,CAAC,EAAA,GAAM,IAAI,EAAA,QAAA,EAC5E,QAAQ,EAAA,CACJ;AAEX;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/react/Tag/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;;AAOM,SAAU,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAc,EAAA;IACnF,QACEA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,OAAO,CAAA,CAAE,CAAC,EAAE,SAAS,CAAC,EAAA,GAAM,IAAI,EAAA,QAAA,EAC5E,QAAQ,EAAA,CACJ;AAEX;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/ThemeToggle/index.tsx"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,4CA6B1B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/ThemeToggle/index.tsx"],"names":[],"mappings":"AA8CA,wBAAgB,WAAW,4CAqC1B"}
@@ -1,13 +1,16 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useState, useEffect } from 'react';
3
3
  import cx from '../../_virtual/index.js';
4
4
  import { getInitialTheme, applyTheme, persistTheme } from '../../shared/js/theme.js';
5
5
  import styles from '../../css/theme-toggle.module.css.js';
6
6
 
7
+ const SunIcon = (jsxs("svg", { "aria-hidden": "true", className: styles['theme-toggle__icon'], xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("circle", { cx: "12", cy: "12", r: "5" }), jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }), jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }), jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }), jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }), jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }), jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }), jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }), jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })] }));
8
+ const MoonIcon = (jsx("svg", { "aria-hidden": "true", className: styles['theme-toggle__icon'], xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) }));
7
9
  function ThemeToggle() {
8
10
  const [theme, setTheme] = useState('light');
9
11
  useEffect(() => {
10
12
  const initial = getInitialTheme();
13
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
11
14
  setTheme(initial);
12
15
  applyTheme(initial);
13
16
  }, []);
@@ -18,7 +21,8 @@ function ThemeToggle() {
18
21
  persistTheme(next);
19
22
  }
20
23
  const isDark = theme === 'dark';
21
- return (jsx("button", { type: "button", className: cx(styles['theme-toggle'], isDark && styles['theme-toggle--dark']), onClick: toggle, "aria-label": isDark ? 'Switch to light mode' : 'Switch to dark mode', "aria-pressed": isDark, children: isDark ? 'Dark' : 'Light' }));
24
+ const label = isDark ? 'Dark' : 'Light';
25
+ return (jsxs("span", { className: styles['theme-toggle__wrapper'], children: [jsxs("button", { type: "button", className: cx(styles['theme-toggle'], isDark && styles['theme-toggle--dark']), onClick: toggle, "aria-label": isDark ? 'Switch to light mode' : 'Switch to dark mode', "aria-pressed": isDark, children: [isDark ? MoonIcon : SunIcon, label] }), jsxs("span", { "aria-live": "polite", className: styles['sr-only'], children: [label, " mode active"] })] }));
22
26
  }
23
27
 
24
28
  export { ThemeToggle };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/react/ThemeToggle/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;;;;SAMgB,WAAW,GAAA;IACzB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAQ,OAAO,CAAC;IAElD,SAAS,CAAC,MAAK;AACb,QAAA,MAAM,OAAO,GAAG,eAAe,EAAE;QACjC,QAAQ,CAAC,OAAO,CAAC;QACjB,UAAU,CAAC,OAAO,CAAC;IACrB,CAAC,EAAE,EAAE,CAAC;AAEN,IAAA,SAAS,MAAM,GAAA;AACb,QAAA,MAAM,IAAI,GAAU,KAAK,KAAK,OAAO,GAAG,MAAM,GAAG,OAAO;QACxD,QAAQ,CAAC,IAAI,CAAC;QACd,UAAU,CAAC,IAAI,CAAC;QAChB,YAAY,CAAC,IAAI,CAAC;IACpB;AAEA,IAAA,MAAM,MAAM,GAAG,KAAK,KAAK,MAAM;IAE/B,QACEA,gBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,MAAM,IAAI,MAAM,CAAC,oBAAoB,CAAC,CAAC,EAC7E,OAAO,EAAE,MAAM,EAAA,YAAA,EACH,MAAM,GAAG,sBAAsB,GAAG,qBAAqB,EAAA,cAAA,EACrD,MAAM,YAEnB,MAAM,GAAG,MAAM,GAAG,OAAO,EAAA,CACnB;AAEb;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/react/ThemeToggle/index.tsx"],"sourcesContent":[null],"names":["_jsxs","_jsx"],"mappings":"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"}
@@ -1,19 +1,19 @@
1
1
  const STORAGE_KEY = 'color-scheme';
2
2
  function getInitialTheme() {
3
- if (typeof window === 'undefined')
3
+ if (globalThis.window === undefined)
4
4
  return 'light';
5
5
  const stored = localStorage.getItem(STORAGE_KEY);
6
6
  if (stored === 'light' || stored === 'dark')
7
7
  return stored;
8
- return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
8
+ return globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
9
9
  }
10
10
  function applyTheme(theme) {
11
- if (typeof window === 'undefined')
11
+ if (globalThis.window === undefined)
12
12
  return;
13
13
  document.documentElement.setAttribute('color-scheme', theme);
14
14
  }
15
15
  function persistTheme(theme) {
16
- if (typeof window === 'undefined')
16
+ if (globalThis.window === undefined)
17
17
  return;
18
18
  localStorage.setItem(STORAGE_KEY, theme);
19
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"theme.js","sources":["../../../src/shared/js/theme.ts"],"sourcesContent":[null],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,cAAc;SAElB,eAAe,GAAA;IAC7B,IAAI,OAAO,MAAM,KAAK,WAAW;AAAE,QAAA,OAAO,OAAO;IAEjD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC;AAChD,IAAA,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,MAAM;AAAE,QAAA,OAAO,MAAM;AAE1D,IAAA,OAAO,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,GAAG,MAAM,GAAG,OAAO;AACrF;AAEM,SAAU,UAAU,CAAC,KAAY,EAAA;IACrC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE;IACnC,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC;AAC9D;AAEM,SAAU,YAAY,CAAC,KAAY,EAAA;IACvC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE;AACnC,IAAA,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC;AAC1C;;;;"}
1
+ {"version":3,"file":"theme.js","sources":["../../../src/shared/js/theme.ts"],"sourcesContent":[null],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,cAAc;SAElB,eAAe,GAAA;AAC7B,IAAA,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;AAAE,QAAA,OAAO,OAAO;IAEnD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC;AAChD,IAAA,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,MAAM;AAAE,QAAA,OAAO,MAAM;AAE1D,IAAA,OAAO,UAAU,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,GAAG,MAAM,GAAG,OAAO;AACzF;AAEM,SAAU,UAAU,CAAC,KAAY,EAAA;AACrC,IAAA,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;QAAE;IACrC,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC;AAC9D;AAEM,SAAU,YAAY,CAAC,KAAY,EAAA;AACvC,IAAA,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;QAAE;AACrC,IAAA,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC;AAC1C;;;;"}
@@ -1,6 +1,6 @@
1
1
  export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'success';
2
2
  export type ButtonSize = 'sm' | 'md' | 'lg' | 'small' | 'medium' | 'large';
3
- export interface ButtonProps {
3
+ export interface ButtonProperties {
4
4
  variant?: ButtonVariant;
5
5
  size?: ButtonSize;
6
6
  disabled?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/shared/types/button.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AACtF,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3E,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/shared/types/button.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AACtF,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
@@ -1,5 +1,6 @@
1
1
  export type CardElevation = 'flat' | 'raised';
2
- export interface CardProps {
2
+ export interface CardProperties {
3
3
  elevation?: CardElevation;
4
+ role?: string;
4
5
  }
5
6
  //# sourceMappingURL=card.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/shared/types/card.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B"}
1
+ {"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/shared/types/card.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
@@ -1,5 +1,5 @@
1
- export type TagVariant = 'default' | 'success' | 'danger';
2
- export interface TagProps {
1
+ export type TagVariant = 'default' | 'success' | 'danger' | 'warning';
2
+ export interface TagProperties {
3
3
  variant?: TagVariant;
4
4
  }
5
5
  //# sourceMappingURL=tag.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../src/shared/types/tag.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE1D,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB"}
1
+ {"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../src/shared/types/tag.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB"}
@@ -1,4 +1,3 @@
1
1
  export type Theme = 'light' | 'dark';
2
- export interface ThemeToggleProps {
3
- }
2
+ export type ThemeToggleProperties = Record<never, never>;
4
3
  //# sourceMappingURL=theme-toggle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"theme-toggle.d.ts","sourceRoot":"","sources":["../../../src/shared/types/theme-toggle.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAErC,MAAM,WAAW,gBAAgB;CAEhC"}
1
+ {"version":3,"file":"theme-toggle.d.ts","sourceRoot":"","sources":["../../../src/shared/types/theme-toggle.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAErC,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dezkareid/components",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "A package to export UI components in formats like React, Astro, Vue, etc.",
6
6
  "files": [
@@ -48,7 +48,7 @@
48
48
  "vue"
49
49
  ],
50
50
  "peerDependencies": {
51
- "astro": ">=4.0.0",
51
+ "astro": ">=6.0.0",
52
52
  "react": "^18.0.0 || ^19.0.0",
53
53
  "react-dom": "^18.0.0 || ^19.0.0",
54
54
  "vue": "^3.0.0"
@@ -72,26 +72,35 @@
72
72
  "@dezkareid/design-tokens": "0.0.0"
73
73
  },
74
74
  "devDependencies": {
75
+ "astro": "6.0.6",
76
+ "@dezkareid/eslint-config-ts-base": "1.0.0",
77
+ "@eslint-react/eslint-plugin": "2.13.0",
78
+ "@eslint/js": "9.39.2",
75
79
  "@rollup/plugin-commonjs": "29.0.0",
76
80
  "@rollup/plugin-node-resolve": "16.0.3",
77
81
  "@rollup/plugin-typescript": "12.3.0",
78
82
  "@testing-library/jest-dom": "6.9.1",
79
83
  "@testing-library/react": "16.3.2",
80
84
  "@testing-library/user-event": "14.5.2",
81
- "@types/classnames": "^2.3.4",
85
+ "@types/classnames": "2.3.4",
82
86
  "@types/react": "19.2.9",
83
87
  "@vitejs/plugin-react": "5.1.4",
88
+ "eslint": "9.39.2",
84
89
  "jsdom": "27.4.0",
85
90
  "react": "19.2.4",
86
91
  "react-dom": "19.2.4",
87
92
  "rollup": "4.56.0",
88
93
  "rollup-plugin-postcss": "4.0.2",
89
94
  "typescript": "5.9.3",
95
+ "typescript-eslint": "8.57.0",
90
96
  "vite": "7.3.1",
91
- "vitest": "4.0.18"
97
+ "vitest": "4.0.18",
98
+ "@dezkareid/eslint-plugin-web": "^0.0.0"
92
99
  },
93
100
  "scripts": {
94
101
  "build": "rollup -c rollup.config.mjs",
95
- "test": "vitest --run"
102
+ "lint": "eslint .",
103
+ "lint:fix": "eslint . --fix",
104
+ "test": "vitest --run --config vitest.config.ts"
96
105
  }
97
106
  }
@@ -30,6 +30,6 @@ const classes = [
30
30
  .join(' ');
31
31
  ---
32
32
 
33
- <Tag class={classes} href={href} disabled={!href && disabled} {...rest}>
33
+ <Tag class={classes} href={href} disabled={!href && disabled} aria-disabled={!href && disabled} {...rest}>
34
34
  <slot />
35
35
  </Tag>
@@ -8,6 +8,9 @@ type Props = {
8
8
  const { class: className } = Astro.props;
9
9
 
10
10
  const classes = [styles['theme-toggle'], className ?? ''].filter(Boolean).join(' ');
11
+ const wrapperClass = styles['theme-toggle__wrapper'];
12
+ const iconClass = styles['theme-toggle__icon'];
13
+ const srOnlyClass = styles['sr-only'];
11
14
  ---
12
15
 
13
16
  <!--
@@ -27,28 +30,63 @@ const classes = [styles['theme-toggle'], className ?? ''].filter(Boolean).join('
27
30
  })();
28
31
  </script>
29
32
 
30
- <button
31
- type="button"
32
- class={classes}
33
- data-theme-toggle
34
- aria-label="Toggle colour scheme"
35
- >
36
- <span data-theme-label>Light</span>
37
- </button>
33
+ <span class={wrapperClass}>
34
+ <button
35
+ type="button"
36
+ class={classes}
37
+ data-theme-toggle
38
+ aria-label="Toggle colour scheme"
39
+ aria-pressed="false"
40
+ >
41
+ <!-- Sun icon (light mode default) -->
42
+ <svg
43
+ aria-hidden="true"
44
+ class={iconClass}
45
+ data-theme-icon
46
+ xmlns="http://www.w3.org/2000/svg"
47
+ viewBox="0 0 24 24"
48
+ fill="none"
49
+ stroke="currentColor"
50
+ stroke-width="2"
51
+ stroke-linecap="round"
52
+ stroke-linejoin="round"
53
+ >
54
+ <circle cx="12" cy="12" r="5" />
55
+ <line x1="12" y1="1" x2="12" y2="3" />
56
+ <line x1="12" y1="21" x2="12" y2="23" />
57
+ <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
58
+ <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
59
+ <line x1="1" y1="12" x2="3" y2="12" />
60
+ <line x1="21" y1="12" x2="23" y2="12" />
61
+ <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
62
+ <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
63
+ </svg>
64
+ <span data-theme-label>Light</span>
65
+ </button>
66
+ <span aria-live="polite" class={srOnlyClass} data-theme-live>Light mode active</span>
67
+ </span>
38
68
 
39
69
  <script>
40
70
  import { getInitialTheme, applyTheme, persistTheme } from '../../shared/js/theme';
41
71
 
42
72
  const btn = document.querySelector('[data-theme-toggle]') as HTMLButtonElement | null;
43
73
  const label = document.querySelector('[data-theme-label]') as HTMLElement | null;
74
+ const liveRegion = document.querySelector('[data-theme-live]') as HTMLElement | null;
75
+ const iconEl = document.querySelector('[data-theme-icon]') as SVGElement | null;
76
+
77
+ const sunPath = `<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>`;
78
+ const moonPath = `<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>`;
44
79
 
45
80
  if (btn && label) {
46
81
  let current = getInitialTheme();
47
82
 
48
83
  function update(theme: 'light' | 'dark') {
49
- label!.textContent = theme === 'dark' ? 'Dark' : 'Light';
50
- btn!.setAttribute('aria-pressed', String(theme === 'dark'));
51
- btn!.classList.toggle('theme-toggle--dark', theme === 'dark');
84
+ const isDark = theme === 'dark';
85
+ label!.textContent = isDark ? 'Dark' : 'Light';
86
+ btn!.setAttribute('aria-pressed', String(isDark));
87
+ btn!.classList.toggle('theme-toggle--dark', isDark);
88
+ if (iconEl) iconEl.innerHTML = isDark ? moonPath : sunPath;
89
+ if (liveRegion) liveRegion.textContent = `${isDark ? 'Dark' : 'Light'} mode active`;
52
90
  }
53
91
 
54
92
  update(current);
@@ -9,15 +9,31 @@
9
9
  display: inline-flex;
10
10
  align-items: center;
11
11
  justify-content: center;
12
+ gap: var(--spacing-8);
12
13
  cursor: pointer;
13
14
  border: none;
14
- border-radius: var(--spacing-4);
15
+ border-radius: var(--border-radius-medium);
15
16
  font-family: var(--font-family-base);
16
17
  font-weight: var(--font-weight-medium);
18
+ font-size: var(--font-size-300);
17
19
  line-height: var(--font-line-height-none);
20
+ letter-spacing: var(--font-letter-spacing-wide);
18
21
  text-decoration: none;
19
22
  white-space: nowrap;
20
- transition: opacity 0.15s ease;
23
+ transition:
24
+ background-color 0.15s ease,
25
+ color 0.15s ease,
26
+ border-color 0.15s ease,
27
+ box-shadow 0.15s ease,
28
+ opacity 0.15s ease;
29
+ }
30
+
31
+ /* --- Structure: focus ring --- */
32
+ .button:focus-visible {
33
+ outline: none;
34
+ box-shadow:
35
+ 0 0 0 2px var(--color-background-primary),
36
+ 0 0 0 4px var(--color-primary);
21
37
  }
22
38
 
23
39
  /* --- Structure: size modifiers --- */
@@ -25,6 +41,7 @@
25
41
  .button--small {
26
42
  padding: var(--spacing-4) var(--spacing-12);
27
43
  font-size: var(--font-size-200);
44
+ border-radius: var(--border-radius-small);
28
45
  }
29
46
 
30
47
  .button--md,
@@ -37,49 +54,67 @@
37
54
  .button--large {
38
55
  padding: var(--spacing-12) var(--spacing-32);
39
56
  font-size: var(--font-size-400);
57
+ border-radius: var(--border-radius-large);
40
58
  }
41
59
 
42
60
  /* --- Skin: variant modifiers --- */
43
61
  .button--primary {
44
- background-color: var(--color-primary);
45
- color: var(--color-text-inverse);
62
+ --button-primary-color-background: var(--color-primary);
63
+ --button-primary-color-text: var(--color-text-inverse);
64
+ background-color: var(--button-primary-color-background);
65
+ color: var(--button-primary-color-text);
46
66
  border: 1px solid transparent;
47
67
  }
48
68
 
49
69
  .button--primary:hover:not(.button--disabled) {
50
- opacity: 0.88;
70
+ box-shadow: var(--shadow-card-hover);
71
+ opacity: 0.92;
51
72
  }
52
73
 
53
74
  .button--secondary,
54
75
  .button--outline {
55
- background-color: transparent;
56
- color: var(--color-primary);
57
- border: 1px solid var(--color-primary);
76
+ --button-secondary-color-background: transparent;
77
+ --button-secondary-color-text: var(--color-primary);
78
+ --button-secondary-color-border: var(--color-primary);
79
+ background-color: var(--button-secondary-color-background);
80
+ color: var(--button-secondary-color-text);
81
+ border: 1px solid var(--button-secondary-color-border);
58
82
  }
59
83
 
60
84
  .button--secondary:hover:not(.button--disabled),
61
85
  .button--outline:hover:not(.button--disabled) {
62
86
  background-color: var(--color-background-secondary);
87
+ box-shadow: var(--shadow-subtle);
63
88
  }
64
89
 
90
+ /* Success button: uses text-primary for text to meet WCAG AA 4.5:1
91
+ Light: #22c55e bg + #111827 text = 7.79:1 ✅
92
+ Dark: #14532d bg + #ffffff text = 9.11:1 ✅ */
93
+
65
94
  .button--success {
66
- background-color: var(--color-success);
67
- color: var(--color-text-inverse);
95
+ --button-success-color-background: var(--color-success);
96
+ --button-success-color-text: var(--color-text-primary);
97
+ background-color: var(--button-success-color-background);
98
+ color: var(--button-success-color-text);
68
99
  border: 1px solid transparent;
69
100
  }
70
101
 
71
102
  .button--success:hover:not(.button--disabled) {
72
- opacity: 0.88;
103
+ box-shadow: var(--shadow-card-hover);
104
+ opacity: 0.92;
73
105
  }
74
106
 
75
107
  .button--ghost {
76
- background-color: transparent;
77
- color: var(--color-text-primary);
108
+ --button-ghost-color-background: transparent;
109
+ --button-ghost-color-text: var(--color-text-primary);
110
+ background-color: var(--button-ghost-color-background);
111
+ color: var(--button-ghost-color-text);
78
112
  border: 1px solid transparent;
79
113
  }
80
114
 
81
115
  .button--ghost:hover:not(.button--disabled) {
82
116
  background-color: var(--color-background-secondary);
117
+ border-color: var(--color-background-secondary);
83
118
  }
84
119
 
85
120
  /* --- Skin: state modifier --- */
@@ -87,4 +122,4 @@
87
122
  opacity: 0.4;
88
123
  cursor: not-allowed;
89
124
  pointer-events: none;
90
- }
125
+ }
@@ -20,11 +20,13 @@
20
20
 
21
21
  /* --- Skin: elevation modifiers --- */
22
22
 
23
- /* TODO: Propose --shadow-raised token to the design-tokens package. */
23
+ /* TODO(design-system): needs token --shadow-raised (suggest 0 2px 8px rgba(0,0,0,0.12)) */
24
24
  .card--raised {
25
25
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
26
26
  }
27
27
 
28
+ /* TODO(design-system): needs token --color-border (suggest var(--color-background-secondary)) */
28
29
  .card--flat {
29
30
  box-shadow: none;
31
+ border: 1px solid var(--color-background-secondary);
30
32
  }
@@ -8,7 +8,7 @@
8
8
  .tag {
9
9
  display: inline-flex;
10
10
  align-items: center;
11
- border-radius: var(--spacing-4);
11
+ border-radius: var(--spacing-12);
12
12
  padding: var(--spacing-4) var(--spacing-8);
13
13
  font-family: var(--font-family-base);
14
14
  font-size: var(--font-size-100);
@@ -18,16 +18,31 @@
18
18
 
19
19
  /* --- Skin: variant modifiers --- */
20
20
  .tag--default {
21
- background-color: var(--color-background-secondary);
22
- color: var(--color-text-primary);
21
+ --tag-default-color-background: var(--color-background-secondary);
22
+ --tag-default-color-text: var(--color-text-primary);
23
+ background-color: var(--tag-default-color-background);
24
+ color: var(--tag-default-color-text);
23
25
  }
24
26
 
27
+ /* Success: text-primary gives 7.79:1 light (#22c55e) and 9.11:1 dark (#14532d) ✅ */
25
28
  .tag--success {
26
- background-color: var(--color-success);
27
- color: var(--color-text-inverse);
29
+ --tag-success-color-background: var(--color-success);
30
+ --tag-success-color-text: var(--color-text-primary);
31
+ background-color: var(--tag-success-color-background);
32
+ color: var(--tag-success-color-text);
28
33
  }
29
34
 
35
+ /* Danger: text-primary gives 4.71:1 light (#ef4444) and 10.02:1 dark (#7f1d1d) ✅ */
30
36
  .tag--danger {
31
- background-color: var(--color-danger);
32
- color: var(--color-text-inverse);
37
+ --tag-danger-color-background: var(--color-danger);
38
+ --tag-danger-color-text: var(--color-text-primary);
39
+ background-color: var(--tag-danger-color-background);
40
+ color: var(--tag-danger-color-text);
41
+ }
42
+
43
+ .tag--warning {
44
+ --tag-warning-color-background: var(--color-warning);
45
+ --tag-warning-color-text: var(--color-text-primary);
46
+ background-color: var(--tag-warning-color-background);
47
+ color: var(--tag-warning-color-text);
33
48
  }
@@ -36,3 +36,29 @@
36
36
  color: var(--color-primary);
37
37
  border-color: var(--color-primary);
38
38
  }
39
+
40
+ /* --- Structure: icon --- */
41
+ .theme-toggle__icon {
42
+ width: 1em;
43
+ height: 1em;
44
+ flex-shrink: 0;
45
+ }
46
+
47
+ /* --- Structure: wrapper --- */
48
+ .theme-toggle__wrapper {
49
+ display: inline-flex;
50
+ position: relative;
51
+ }
52
+
53
+ /* --- Utility: visually hidden live region --- */
54
+ .sr-only {
55
+ position: absolute;
56
+ width: 1px;
57
+ height: 1px;
58
+ padding: 0;
59
+ margin: -1px;
60
+ overflow: hidden;
61
+ clip: rect(0, 0, 0, 0);
62
+ white-space: nowrap;
63
+ border: 0;
64
+ }
@@ -11,14 +11,14 @@ describe('Button', () => {
11
11
 
12
12
  it('renders primary variant', () => {
13
13
  render(<Button variant="primary">Primary</Button>);
14
- const btn = screen.getByRole('button');
15
- expect(btn.className).toMatch(/button--primary/);
14
+ const button = screen.getByRole('button');
15
+ expect(button.className).toMatch(/button--primary/);
16
16
  });
17
17
 
18
18
  it('renders secondary variant', () => {
19
19
  render(<Button variant="secondary">Secondary</Button>);
20
- const btn = screen.getByRole('button');
21
- expect(btn.className).toMatch(/button--secondary/);
20
+ const button = screen.getByRole('button');
21
+ expect(button.className).toMatch(/button--secondary/);
22
22
  });
23
23
 
24
24
  it('renders sm size', () => {
@@ -38,9 +38,9 @@ describe('Button', () => {
38
38
 
39
39
  it('is disabled when disabled prop is set', () => {
40
40
  render(<Button disabled>Disabled</Button>);
41
- const btn = screen.getByRole('button');
42
- expect(btn).toBeDisabled();
43
- expect(btn.className).toMatch(/button--disabled/);
41
+ const button = screen.getByRole('button');
42
+ expect(button).toBeDisabled();
43
+ expect(button.className).toMatch(/button--disabled/);
44
44
  });
45
45
 
46
46
  it('does not fire onClick when disabled', async () => {
@@ -56,4 +56,14 @@ describe('Button', () => {
56
56
  await userEvent.click(screen.getByRole('button'));
57
57
  expect(onClick).toHaveBeenCalledOnce();
58
58
  });
59
+
60
+ it('has aria-disabled when disabled', () => {
61
+ render(<Button disabled>Disabled</Button>);
62
+ expect(screen.getByRole('button')).toHaveAttribute('aria-disabled', 'true');
63
+ });
64
+
65
+ it('does not have aria-disabled when enabled', () => {
66
+ render(<Button>Enabled</Button>);
67
+ expect(screen.getByRole('button')).toHaveAttribute('aria-disabled', 'false');
68
+ });
59
69
  });
@@ -1,9 +1,9 @@
1
1
  import type { ButtonHTMLAttributes } from 'react';
2
2
  import cx from 'classnames';
3
- import type { ButtonProps } from '../../shared/types/button';
3
+ import type { ButtonProperties } from '../../shared/types/button';
4
4
  import styles from '../../css/button.module.css';
5
5
 
6
- type Props = ButtonProps & ButtonHTMLAttributes<HTMLButtonElement>;
6
+ type Properties = ButtonProperties & ButtonHTMLAttributes<HTMLButtonElement>;
7
7
 
8
8
  export function Button({
9
9
  variant = 'primary',
@@ -12,7 +12,7 @@ export function Button({
12
12
  children,
13
13
  className,
14
14
  ...rest
15
- }: Props) {
15
+ }: Properties) {
16
16
  return (
17
17
  <button
18
18
  className={cx(
@@ -23,6 +23,7 @@ export function Button({
23
23
  className,
24
24
  )}
25
25
  disabled={disabled}
26
+ aria-disabled={disabled}
26
27
  {...rest}
27
28
  >
28
29
  {children}
@@ -26,13 +26,23 @@ describe('Card', () => {
26
26
 
27
27
  it('applies flat elevation when set', () => {
28
28
  const { container } = render(<Card elevation="flat">Content</Card>);
29
- const el = container.firstChild as HTMLElement;
30
- expect(el.className).toMatch(/card--flat/);
31
- expect(el.className).not.toMatch(/card--raised/);
29
+ const element = container.firstChild as HTMLElement;
30
+ expect(element.className).toMatch(/card--flat/);
31
+ expect(element.className).not.toMatch(/card--raised/);
32
32
  });
33
33
 
34
34
  it('applies raised elevation when explicitly set', () => {
35
35
  const { container } = render(<Card elevation="raised">Content</Card>);
36
36
  expect((container.firstChild as HTMLElement).className).toMatch(/card--raised/);
37
37
  });
38
+
39
+ it('forwards role prop to root element', () => {
40
+ render(<Card role="article">Content</Card>);
41
+ expect(screen.getByRole('article')).toBeInTheDocument();
42
+ });
43
+
44
+ it('applies card--flat class when elevation is flat', () => {
45
+ const { container } = render(<Card elevation="flat">Content</Card>);
46
+ expect((container.firstChild as HTMLElement).className).toMatch(/card--flat/);
47
+ });
38
48
  });
@@ -1,11 +1,11 @@
1
1
  import type { HTMLAttributes } from 'react';
2
2
  import cx from 'classnames';
3
- import type { CardProps } from '../../shared/types/card';
3
+ import type { CardProperties } from '../../shared/types/card';
4
4
  import styles from '../../css/card.module.css';
5
5
 
6
- type Props = CardProps & HTMLAttributes<HTMLDivElement>;
6
+ type Properties = CardProperties & HTMLAttributes<HTMLDivElement>;
7
7
 
8
- export function Card({ elevation = 'raised', children, className, ...rest }: Props) {
8
+ export function Card({ elevation = 'raised', children, className, ...rest }: Properties) {
9
9
  return (
10
10
  <div className={cx(styles.card, styles[`card--${elevation}`], className)} {...rest}>
11
11
  {children}
@@ -32,4 +32,14 @@ describe('Tag', () => {
32
32
  render(<Tag>No variant</Tag>);
33
33
  expect(screen.getByText('No variant').className).toMatch(/tag--default/);
34
34
  });
35
+
36
+ it('renders warning variant', () => {
37
+ render(<Tag variant="warning">Warning</Tag>);
38
+ expect(screen.getByText('Warning').className).toMatch(/tag--warning/);
39
+ });
40
+
41
+ it('forwards aria-label to root span', () => {
42
+ render(<Tag variant="success" aria-label="Success status">Active</Tag>);
43
+ expect(screen.getByLabelText('Success status')).toBeInTheDocument();
44
+ });
35
45
  });
@@ -1,11 +1,11 @@
1
1
  import type { HTMLAttributes } from 'react';
2
2
  import cx from 'classnames';
3
- import type { TagProps } from '../../shared/types/tag';
3
+ import type { TagProperties } from '../../shared/types/tag';
4
4
  import styles from '../../css/tag.module.css';
5
5
 
6
- type Props = TagProps & HTMLAttributes<HTMLSpanElement>;
6
+ type Properties = TagProperties & HTMLAttributes<HTMLSpanElement>;
7
7
 
8
- export function Tag({ variant = 'default', children, className, ...rest }: Props) {
8
+ export function Tag({ variant = 'default', children, className, ...rest }: Properties) {
9
9
  return (
10
10
  <span className={cx(styles.tag, styles[`tag--${variant}`], className)} {...rest}>
11
11
  {children}
@@ -4,12 +4,12 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
4
4
  import { ThemeToggle } from './index';
5
5
 
6
6
  function mockMatchMedia(prefersDark: boolean) {
7
- Object.defineProperty(window, 'matchMedia', {
7
+ Object.defineProperty(globalThis, 'matchMedia', {
8
8
  writable: true,
9
9
  value: vi.fn((query: string) => ({
10
10
  matches: query === '(prefers-color-scheme: dark)' ? prefersDark : false,
11
11
  media: query,
12
- onchange: null,
12
+ onchange: undefined,
13
13
  addEventListener: vi.fn(),
14
14
  removeEventListener: vi.fn(),
15
15
  dispatchEvent: vi.fn(),
@@ -81,4 +81,27 @@ describe('ThemeToggle', () => {
81
81
  await act(async () => { render(<ThemeToggle />); });
82
82
  expect(screen.getByRole('button')).toHaveAttribute('aria-pressed', 'false');
83
83
  });
84
+
85
+ it('has aria-label "Switch to dark mode" when in light mode', async () => {
86
+ await act(async () => { render(<ThemeToggle />); });
87
+ expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Switch to dark mode');
88
+ });
89
+
90
+ it('has aria-label "Switch to light mode" after toggling to dark', async () => {
91
+ await act(async () => { render(<ThemeToggle />); });
92
+ await userEvent.click(screen.getByRole('button'));
93
+ expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Switch to light mode');
94
+ });
95
+
96
+ it('renders an aria-live polite region', async () => {
97
+ await act(async () => { render(<ThemeToggle />); });
98
+ const liveRegion = document.querySelector('[aria-live="polite"]');
99
+ expect(liveRegion).toBeInTheDocument();
100
+ });
101
+
102
+ it('renders an SVG icon with aria-hidden', async () => {
103
+ await act(async () => { render(<ThemeToggle />); });
104
+ const svg = document.querySelector('svg[aria-hidden="true"]');
105
+ expect(svg).toBeInTheDocument();
106
+ });
84
107
  });
@@ -4,11 +4,52 @@ import type { Theme } from '../../shared/types/theme-toggle';
4
4
  import { getInitialTheme, applyTheme, persistTheme } from '../../shared/js/theme';
5
5
  import styles from '../../css/theme-toggle.module.css';
6
6
 
7
+ const SunIcon = (
8
+ <svg
9
+ aria-hidden="true"
10
+ className={styles['theme-toggle__icon']}
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ viewBox="0 0 24 24"
13
+ fill="none"
14
+ stroke="currentColor"
15
+ strokeWidth="2"
16
+ strokeLinecap="round"
17
+ strokeLinejoin="round"
18
+ >
19
+ <circle cx="12" cy="12" r="5" />
20
+ <line x1="12" y1="1" x2="12" y2="3" />
21
+ <line x1="12" y1="21" x2="12" y2="23" />
22
+ <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
23
+ <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
24
+ <line x1="1" y1="12" x2="3" y2="12" />
25
+ <line x1="21" y1="12" x2="23" y2="12" />
26
+ <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
27
+ <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
28
+ </svg>
29
+ );
30
+
31
+ const MoonIcon = (
32
+ <svg
33
+ aria-hidden="true"
34
+ className={styles['theme-toggle__icon']}
35
+ xmlns="http://www.w3.org/2000/svg"
36
+ viewBox="0 0 24 24"
37
+ fill="none"
38
+ stroke="currentColor"
39
+ strokeWidth="2"
40
+ strokeLinecap="round"
41
+ strokeLinejoin="round"
42
+ >
43
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
44
+ </svg>
45
+ );
46
+
7
47
  export function ThemeToggle() {
8
48
  const [theme, setTheme] = useState<Theme>('light');
9
49
 
10
50
  useEffect(() => {
11
51
  const initial = getInitialTheme();
52
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
12
53
  setTheme(initial);
13
54
  applyTheme(initial);
14
55
  }, []);
@@ -21,16 +62,23 @@ export function ThemeToggle() {
21
62
  }
22
63
 
23
64
  const isDark = theme === 'dark';
65
+ const label = isDark ? 'Dark' : 'Light';
24
66
 
25
67
  return (
26
- <button
27
- type="button"
28
- className={cx(styles['theme-toggle'], isDark && styles['theme-toggle--dark'])}
29
- onClick={toggle}
30
- aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
31
- aria-pressed={isDark}
32
- >
33
- {isDark ? 'Dark' : 'Light'}
34
- </button>
68
+ <span className={styles['theme-toggle__wrapper']}>
69
+ <button
70
+ type="button"
71
+ className={cx(styles['theme-toggle'], isDark && styles['theme-toggle--dark'])}
72
+ onClick={toggle}
73
+ aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
74
+ aria-pressed={isDark}
75
+ >
76
+ {isDark ? MoonIcon : SunIcon}
77
+ {label}
78
+ </button>
79
+ <span aria-live="polite" className={styles['sr-only']}>
80
+ {label} mode active
81
+ </span>
82
+ </span>
35
83
  );
36
84
  }
@@ -3,20 +3,20 @@ import type { Theme } from '../types/theme-toggle';
3
3
  const STORAGE_KEY = 'color-scheme';
4
4
 
5
5
  export function getInitialTheme(): Theme {
6
- if (typeof window === 'undefined') return 'light';
6
+ if (globalThis.window === undefined) return 'light';
7
7
 
8
8
  const stored = localStorage.getItem(STORAGE_KEY);
9
9
  if (stored === 'light' || stored === 'dark') return stored;
10
10
 
11
- return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
11
+ return globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
12
12
  }
13
13
 
14
14
  export function applyTheme(theme: Theme): void {
15
- if (typeof window === 'undefined') return;
15
+ if (globalThis.window === undefined) return;
16
16
  document.documentElement.setAttribute('color-scheme', theme);
17
17
  }
18
18
 
19
19
  export function persistTheme(theme: Theme): void {
20
- if (typeof window === 'undefined') return;
20
+ if (globalThis.window === undefined) return;
21
21
  localStorage.setItem(STORAGE_KEY, theme);
22
22
  }
@@ -1,7 +1,7 @@
1
1
  export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'success';
2
2
  export type ButtonSize = 'sm' | 'md' | 'lg' | 'small' | 'medium' | 'large';
3
3
 
4
- export interface ButtonProps {
4
+ export interface ButtonProperties {
5
5
  variant?: ButtonVariant;
6
6
  size?: ButtonSize;
7
7
  disabled?: boolean;
@@ -1,5 +1,6 @@
1
1
  export type CardElevation = 'flat' | 'raised';
2
2
 
3
- export interface CardProps {
3
+ export interface CardProperties {
4
4
  elevation?: CardElevation;
5
+ role?: string;
5
6
  }
@@ -1,5 +1,5 @@
1
- export type TagVariant = 'default' | 'success' | 'danger';
1
+ export type TagVariant = 'default' | 'success' | 'danger' | 'warning';
2
2
 
3
- export interface TagProps {
3
+ export interface TagProperties {
4
4
  variant?: TagVariant;
5
5
  }
@@ -1,5 +1,3 @@
1
1
  export type Theme = 'light' | 'dark';
2
2
 
3
- export interface ThemeToggleProps {
4
- // No required props — self-contained stateful component
5
- }
3
+ export type ThemeToggleProperties = Record<never, never>;
@@ -21,7 +21,7 @@ const classes = computed(() =>
21
21
  </script>
22
22
 
23
23
  <template>
24
- <button :class="classes" :disabled="disabled">
24
+ <button :class="classes" :disabled="disabled" :aria-disabled="disabled">
25
25
  <slot />
26
26
  </button>
27
27
  </template>
@@ -27,13 +27,56 @@ const classes = () =>
27
27
  </script>
28
28
 
29
29
  <template>
30
- <button
31
- type="button"
32
- :class="classes()"
33
- :aria-label="isDark() ? 'Switch to light mode' : 'Switch to dark mode'"
34
- :aria-pressed="isDark()"
35
- @click="toggle"
36
- >
37
- {{ isDark() ? 'Dark' : 'Light' }}
38
- </button>
30
+ <span :class="styles['theme-toggle__wrapper']">
31
+ <button
32
+ type="button"
33
+ :class="classes()"
34
+ :aria-label="isDark() ? 'Switch to light mode' : 'Switch to dark mode'"
35
+ :aria-pressed="isDark()"
36
+ @click="toggle"
37
+ >
38
+ <!-- Moon icon (dark mode) -->
39
+ <svg
40
+ v-if="isDark()"
41
+ aria-hidden="true"
42
+ :class="styles['theme-toggle__icon']"
43
+ xmlns="http://www.w3.org/2000/svg"
44
+ viewBox="0 0 24 24"
45
+ fill="none"
46
+ stroke="currentColor"
47
+ stroke-width="2"
48
+ stroke-linecap="round"
49
+ stroke-linejoin="round"
50
+ >
51
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
52
+ </svg>
53
+ <!-- Sun icon (light mode) -->
54
+ <svg
55
+ v-else
56
+ aria-hidden="true"
57
+ :class="styles['theme-toggle__icon']"
58
+ xmlns="http://www.w3.org/2000/svg"
59
+ viewBox="0 0 24 24"
60
+ fill="none"
61
+ stroke="currentColor"
62
+ stroke-width="2"
63
+ stroke-linecap="round"
64
+ stroke-linejoin="round"
65
+ >
66
+ <circle cx="12" cy="12" r="5" />
67
+ <line x1="12" y1="1" x2="12" y2="3" />
68
+ <line x1="12" y1="21" x2="12" y2="23" />
69
+ <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
70
+ <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
71
+ <line x1="1" y1="12" x2="3" y2="12" />
72
+ <line x1="21" y1="12" x2="23" y2="12" />
73
+ <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
74
+ <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
75
+ </svg>
76
+ {{ isDark() ? 'Dark' : 'Light' }}
77
+ </button>
78
+ <span aria-live="polite" :class="styles['sr-only']">
79
+ {{ isDark() ? 'Dark' : 'Light' }} mode active
80
+ </span>
81
+ </span>
39
82
  </template>