@alexandregme/zljs 1.12.0 → 1.13.0

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/CHANGELOG.md CHANGED
@@ -8,6 +8,34 @@ A change that needs action in an app has a **Migration** note. When updating, re
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [1.13.0] - 2026-11-02
12
+
13
+ ### Added
14
+
15
+ - `Radio` takes `aria-label`, `required`, `onBlur`, `onKeyDown` and `ref`, as `Input` does, so it works with `register` from `react-hook-form`
16
+
17
+ ### Changed
18
+
19
+ - `Radio` requires `label` or `aria-label` in its type, instead of always `label`
20
+ - `Radio` input now sits inside its `<label>`, as on `Checkbox`, so the gap between the circle and the text is clickable too
21
+ - `Radio` draws a real `focus-visible` outline in the theme's `primary` color instead of a `focus:ring`, so focus stays visible in forced colors mode
22
+
23
+ ### Fixed
24
+
25
+ - A checked `Radio` uses the theme's `primary` color. It had `text-blue-600` and `border-gray-300`, which do nothing on a native radio, so it kept the browser's own color
26
+ - The label of a disabled `Radio` shows the `not-allowed` cursor instead of `pointer`
27
+
28
+ #### Migration
29
+
30
+ Only code that extends `RadioProps`, and tests or selectors that rely on the old structure or classes, need a change. To find them, run the commands below from the app's root. The second also finds other uses of these classes, and those stay as they are:
31
+
32
+ ```sh
33
+ grep -rnE "\bRadioProps\b" . --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=.next
34
+ grep -rnE "text-blue-600|focus:ring-blue-500|nextSibling|previousSibling" . --include='*.test.*' --exclude-dir=node_modules --exclude-dir=.next
35
+ ```
36
+
37
+ Replace `interface X extends RadioProps` with `type X = RadioProps & { ... }`, because `RadioProps` is now a union. In tests, replace those classes with `accent-zl-primary` and `focus-visible:outline-zl-primary`, and find the radio by role instead of by its position next to the label.
38
+
11
39
  ## [1.12.0] - 2026-11-02
12
40
 
13
41
  ### Added
@@ -134,7 +162,8 @@ Replace the shade with the class of the `color` being tested, for example `/(^|\
134
162
 
135
163
  The message of `Error` is `text-zl-danger`. `none` and `ghost` didn't change.
136
164
 
137
- [Unreleased]: https://github.com/alexandregme/zljs/compare/v1.12.0...HEAD
165
+ [Unreleased]: https://github.com/alexandregme/zljs/compare/v1.13.0...HEAD
166
+ [1.13.0]: https://github.com/alexandregme/zljs/compare/v1.12.0...v1.13.0
138
167
  [1.12.0]: https://github.com/alexandregme/zljs/compare/v1.11.0...v1.12.0
139
168
  [1.11.0]: https://github.com/alexandregme/zljs/compare/v1.10.0...v1.11.0
140
169
  [1.10.0]: https://github.com/alexandregme/zljs/compare/v1.9.0...v1.10.0
package/dist/index.d.ts CHANGED
@@ -121,16 +121,26 @@ type InputProps = InputName & {
121
121
 
122
122
  declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
123
123
 
124
- interface RadioProps {
124
+ type RadioName = {
125
125
  label: string;
126
+ "aria-label"?: string;
127
+ } | {
128
+ label?: undefined;
129
+ "aria-label": string;
130
+ };
131
+ type RadioProps = RadioName & {
126
132
  name: string;
127
133
  value: string;
128
134
  checked?: boolean;
129
135
  disabled?: boolean;
136
+ required?: boolean;
130
137
  onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
131
- }
138
+ onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
139
+ onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
140
+ ref?: Ref<HTMLInputElement>;
141
+ };
132
142
 
133
- declare const Radio: ({ label: labelText, name, value, checked, disabled, onChange, }: RadioProps) => react.JSX.Element;
143
+ declare const Radio: ({ label: labelText, "aria-label": ariaLabel, name, value, checked, disabled, required, onChange, onBlur, onKeyDown, ref, }: RadioProps) => react.JSX.Element;
134
144
 
135
145
  interface SelectOption {
136
146
  value: string;
package/dist/index.js CHANGED
@@ -306,41 +306,51 @@ import { useId as useId2 } from "react";
306
306
  // core/forms/radio/radio.styles.ts
307
307
  import { tv as tv4 } from "tailwind-variants";
308
308
  var wrapper2 = tv4({
309
- base: "flex items-center gap-2"
309
+ base: "flex w-fit items-center gap-2 cursor-pointer has-disabled:cursor-not-allowed"
310
310
  });
311
311
  var radio = tv4({
312
- base: "w-4 h-4 text-blue-600 border-gray-300 focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed"
312
+ base: "w-4 h-4 shrink-0 accent-zl-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zl-primary disabled:cursor-not-allowed"
313
313
  });
314
314
  var label2 = tv4({
315
- base: "text-sm text-gray-700 select-none cursor-pointer"
315
+ base: "text-sm text-gray-700 select-none"
316
316
  });
317
317
 
318
318
  // core/forms/radio/radio.tsx
319
319
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
320
320
  var Radio = ({
321
321
  label: labelText,
322
+ "aria-label": ariaLabel,
322
323
  name,
323
324
  value,
324
325
  checked,
325
326
  disabled,
326
- onChange
327
+ required,
328
+ onChange,
329
+ onBlur,
330
+ onKeyDown,
331
+ ref
327
332
  }) => {
328
333
  const id = useId2();
329
- return /* @__PURE__ */ jsxs3("div", { className: wrapper2(), children: [
334
+ return /* @__PURE__ */ jsxs3("label", { htmlFor: id, className: wrapper2(), children: [
330
335
  /* @__PURE__ */ jsx5(
331
336
  "input",
332
337
  {
338
+ ref,
333
339
  id,
334
340
  type: "radio",
335
341
  name,
336
342
  value,
337
343
  checked,
338
344
  disabled,
345
+ required,
339
346
  onChange,
347
+ onBlur,
348
+ onKeyDown,
349
+ "aria-label": ariaLabel,
340
350
  className: radio()
341
351
  }
342
352
  ),
343
- /* @__PURE__ */ jsx5("label", { htmlFor: id, className: label2(), children: labelText })
353
+ labelText && /* @__PURE__ */ jsx5("span", { className: label2(), children: labelText })
344
354
  ] });
345
355
  };
346
356
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexandregme/zljs",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Opinionated, accessible React component library for enterprise apps (Next.js, Tailwind CSS v4).",
5
5
  "keywords": [
6
6
  "react",