@kennycorrea/ui 0.7.0 → 0.8.1
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 +35 -0
- package/README.md +23 -0
- package/dist/atoms/Badge/Badge.d.ts +9 -2
- package/dist/atoms/Badge/Badge.js +15 -2
- package/dist/atoms/Toast/Toast.js +2 -1
- package/dist/icons/index.d.ts +6 -0
- package/dist/icons/index.js +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/primitives/index.d.ts +9 -0
- package/dist/primitives/index.js +9 -0
- package/dist/utils/scales.d.ts +9 -0
- package/dist/utils/scales.js +30 -0
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.1 — 2026-10-11
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **`Badge` text passes WCAG AA in both appearances.** Soft badges wrote their text in step a11
|
|
7
|
+
on an a3 wash, which fails 4.5:1 in light for `success` (jade, 4.27:1), `warning` (amber,
|
|
8
|
+
4.25:1), blue and several accents a business can pick for `brand`. Every variant is now
|
|
9
|
+
`highContrast` (step 12), which clears 9.8:1 on every Radix scale in light and dark. `Toast`
|
|
10
|
+
already passed (lowest: amber action text at 4.61:1) and is unchanged.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`test/contrast.test.tsx`**: every `Badge` variant (`brand` against every Radix accent) and
|
|
14
|
+
every `Toast` type, light and dark, computed from Radix's own token files. It fails if
|
|
15
|
+
`highContrast` is dropped. Not axe: jsdom applies no stylesheet, so axe's colour-contrast rule
|
|
16
|
+
can only report "incomplete" there; this checks the same pairs it would check in a browser.
|
|
17
|
+
- **A dev-only warning when a needed colour scale is missing.** The first `Badge` or `Toaster`
|
|
18
|
+
to mount checks for `--{gray,jade,amber,red,blue}-9` and names the missing
|
|
19
|
+
`@radix-ui/themes/tokens/colors/*.css` imports.
|
|
20
|
+
|
|
21
|
+
### Docs
|
|
22
|
+
- README: the colour scales the atoms need, and their order relative to `base.css`.
|
|
23
|
+
|
|
24
|
+
## 0.8.0 — 2026-10-09
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **Every Radix Themes component the atoms do not wrap**, re-exported unchanged from
|
|
28
|
+
`src/primitives/`: layout (Box, Flex, Grid, Container, Section, Inset), type (Text, Heading,
|
|
29
|
+
Link, Code, Kbd, Em, Strong, Quote, Blockquote), and AlertDialog, AspectRatio, Callout,
|
|
30
|
+
CheckboxCards, CheckboxGroup, DataList, DropdownMenu, HoverCard, IconButton, Radio,
|
|
31
|
+
RadioCards, RadioGroup, ScrollArea, SegmentedControl, TabNav, Table, Tabs, TextField,
|
|
32
|
+
AccessibleIcon, Portal, Reset, Slot, Slottable, ThemePanel, VisuallyHidden. Where an atom
|
|
33
|
+
exists it remains the export under that name.
|
|
34
|
+
- **`ThemeRoot`** — Themes' `<Theme>`, renamed because `Theme` is already this package's type.
|
|
35
|
+
- **All of Radix Icons** (`@radix-ui/react-icons`, 320 `*Icon` components). `Icon` is
|
|
36
|
+
unchanged and still the house glyph set.
|
|
37
|
+
|
|
3
38
|
## 0.7.0 — 2026-10-09
|
|
4
39
|
|
|
5
40
|
### Added
|
package/README.md
CHANGED
|
@@ -63,6 +63,29 @@ And in the app's own `index.css`:
|
|
|
63
63
|
every utility class used *inside* the atoms — `w-full`, `inline-flex` — is missing from the
|
|
64
64
|
generated CSS. The components come out subtly unstyled and nothing appears in the console.
|
|
65
65
|
|
|
66
|
+
### Colour scales — required
|
|
67
|
+
|
|
68
|
+
The atoms name five Radix scales: **gray**, **jade** (`Badge` success, `Toast` success),
|
|
69
|
+
**amber** (warning), **red** (danger/error) and **blue** (`Toast`/`Alert` info). If the app
|
|
70
|
+
imports Radix Themes piecemeal rather than the whole `@radix-ui/themes/styles.css`, every one of
|
|
71
|
+
them must be imported, **after `base.css`** as Radix documents it, plus the app's own accent:
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
@import '@radix-ui/themes/tokens/base.css';
|
|
75
|
+
@import '@radix-ui/themes/tokens/colors/gray.css';
|
|
76
|
+
@import '@radix-ui/themes/tokens/colors/jade.css';
|
|
77
|
+
@import '@radix-ui/themes/tokens/colors/amber.css';
|
|
78
|
+
@import '@radix-ui/themes/tokens/colors/red.css';
|
|
79
|
+
@import '@radix-ui/themes/tokens/colors/blue.css';
|
|
80
|
+
/* …the accent the app offers, if not one of the above */
|
|
81
|
+
@import '@radix-ui/themes/components.css';
|
|
82
|
+
@import '@radix-ui/themes/utilities.css';
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
A missing scale is not an error to CSS — the variables are just undefined, and badges and toasts
|
|
86
|
+
render colourless. Since 0.8.1 the first `Badge` or `Toaster` to mount checks for them and
|
|
87
|
+
`console.warn`s the missing imports by name, outside production builds.
|
|
88
|
+
|
|
66
89
|
## Theming — 0.2.0
|
|
67
90
|
|
|
68
91
|
**A product configures its own accents, default and storage namespace.** Until 0.2.0 all three
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { Badge as ThemedBadge } from '@radix-ui/themes';
|
|
2
|
-
import type { ComponentPropsWithoutRef } from 'react';
|
|
2
|
+
import type { ComponentPropsWithoutRef, Ref } from 'react';
|
|
3
3
|
export type BadgeVariant = 'neutral' | 'brand' | 'success' | 'warning' | 'danger';
|
|
4
4
|
export interface BadgeProps extends Omit<ComponentPropsWithoutRef<typeof ThemedBadge>, 'color' | 'variant'> {
|
|
5
5
|
variant?: BadgeVariant;
|
|
6
|
+
ref?: Ref<HTMLSpanElement>;
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* **Always `highContrast`**, which writes the text in step 12 instead of a11. Soft badges are
|
|
10
|
+
* 12px text on an a3 wash, and a11 there fails WCAG AA in light for jade (4.27:1), amber
|
|
11
|
+
* (4.25:1), blue and several accents a business can pick for `brand`. Step 12 clears 9.8:1
|
|
12
|
+
* on every scale in both appearances; `test/contrast.test.tsx` holds it there.
|
|
13
|
+
*/
|
|
14
|
+
export declare function Badge({ variant, ref, ...props }: BadgeProps): import("react").JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Badge as ThemedBadge } from '@radix-ui/themes';
|
|
3
|
+
import { warnMissingScales } from '../../utils/scales.js';
|
|
3
4
|
/**
|
|
4
5
|
* The five meanings this library names, in Themes' colour vocabulary.
|
|
5
6
|
*
|
|
@@ -18,6 +19,18 @@ const COLORS = {
|
|
|
18
19
|
warning: 'amber',
|
|
19
20
|
danger: 'red',
|
|
20
21
|
};
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* **Always `highContrast`**, which writes the text in step 12 instead of a11. Soft badges are
|
|
24
|
+
* 12px text on an a3 wash, and a11 there fails WCAG AA in light for jade (4.27:1), amber
|
|
25
|
+
* (4.25:1), blue and several accents a business can pick for `brand`. Step 12 clears 9.8:1
|
|
26
|
+
* on every scale in both appearances; `test/contrast.test.tsx` holds it there.
|
|
27
|
+
*/
|
|
28
|
+
export function Badge({ variant = 'neutral', ref, ...props }) {
|
|
29
|
+
return (_jsx(ThemedBadge, { ref: (el) => {
|
|
30
|
+
warnMissingScales(el);
|
|
31
|
+
if (typeof ref === 'function')
|
|
32
|
+
ref(el);
|
|
33
|
+
else if (ref)
|
|
34
|
+
ref.current = el;
|
|
35
|
+
}, color: COLORS[variant], variant: "soft", radius: "full", highContrast: true, ...props }));
|
|
23
36
|
}
|
|
@@ -3,6 +3,7 @@ import * as RadixToast from '@radix-ui/react-toast';
|
|
|
3
3
|
import { useSyncExternalStore } from 'react';
|
|
4
4
|
import { useUiStrings } from '../../strings.js';
|
|
5
5
|
import { cn } from '../../utils/cn.js';
|
|
6
|
+
import { warnMissingScales } from '../../utils/scales.js';
|
|
6
7
|
import { Icon } from '../Icon/Icon.js';
|
|
7
8
|
/**
|
|
8
9
|
* A module-level store, so `toast()` can be called from anywhere — an event handler, a
|
|
@@ -92,5 +93,5 @@ export function Toaster({ duration = 5000, position = 'bottom-right', label }) {
|
|
|
92
93
|
if (item.action?.keepOpen)
|
|
93
94
|
event.preventDefault();
|
|
94
95
|
}, className: cn('shrink-0 rounded-md px-2 py-1 text-sm font-medium', 'text-[var(--accent-11)] hover:bg-[var(--accent-a3)]'), children: item.action.label })) : null, _jsx(RadixToast.Close, { "aria-label": strings.dismiss, className: "shrink-0 rounded-md p-1 text-ink-muted hover:bg-[var(--gray-a3)]", children: _jsx(Icon, { name: "close", className: "size-4" }) })] }, item.id));
|
|
95
|
-
}), _jsx(RadixToast.Viewport, { className: cn('fixed z-50 m-0 flex w-full max-w-sm list-none flex-col gap-2 p-4 outline-none', POSITIONS[position]) })] }));
|
|
96
|
+
}), _jsx(RadixToast.Viewport, { ref: warnMissingScales, className: cn('fixed z-50 m-0 flex w-full max-w-sm list-none flex-col gap-2 p-4 outline-none', POSITIONS[position]) })] }));
|
|
96
97
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every Radix Themes component the atoms do not already wrap, re-exported as is.
|
|
3
|
+
*
|
|
4
|
+
* The atoms stay the house API where one exists — `Button`, `Card`, `Dialog` and the rest
|
|
5
|
+
* carry decisions the raw component does not. These are for everything else, so an app
|
|
6
|
+
* reaches for a `Table` or `Tabs` through this package rather than importing Themes beside it
|
|
7
|
+
* and drifting from the version the atoms were built against.
|
|
8
|
+
*/
|
|
9
|
+
export { AccessibleIcon, AlertDialog, AspectRatio, Blockquote, Box, Callout, CheckboxCards, CheckboxGroup, Code, Container, DataList, DropdownMenu, Em, Flex, Grid, Heading, HoverCard, IconButton, Inset, Kbd, Link, Portal, Quote, Radio, RadioCards, RadioGroup, Reset, ScrollArea, Section, SegmentedControl, Slot, Slottable, Strong, Table, TabNav, Tabs, Text, TextField, Theme as ThemeRoot, ThemePanel, VisuallyHidden, } from '@radix-ui/themes';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every Radix Themes component the atoms do not already wrap, re-exported as is.
|
|
3
|
+
*
|
|
4
|
+
* The atoms stay the house API where one exists — `Button`, `Card`, `Dialog` and the rest
|
|
5
|
+
* carry decisions the raw component does not. These are for everything else, so an app
|
|
6
|
+
* reaches for a `Table` or `Tabs` through this package rather than importing Themes beside it
|
|
7
|
+
* and drifting from the version the atoms were built against.
|
|
8
|
+
*/
|
|
9
|
+
export { AccessibleIcon, AlertDialog, AspectRatio, Blockquote, Box, Callout, CheckboxCards, CheckboxGroup, Code, Container, DataList, DropdownMenu, Em, Flex, Grid, Heading, HoverCard, IconButton, Inset, Kbd, Link, Portal, Quote, Radio, RadioCards, RadioGroup, Reset, ScrollArea, Section, SegmentedControl, Slot, Slottable, Strong, Table, TabNav, Tabs, Text, TextField, Theme as ThemeRoot, ThemePanel, VisuallyHidden, } from '@radix-ui/themes';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Radix colour scales the atoms name. A consumer that imports only gray and its accent
|
|
3
|
+
* gets badges and toasts with no colour at all, and nothing says so — the variables are simply
|
|
4
|
+
* undefined. This is that something, in development only.
|
|
5
|
+
*/
|
|
6
|
+
export declare const REQUIRED_SCALES: readonly ["gray", "jade", "amber", "red", "blue"];
|
|
7
|
+
export declare function warnMissingScales(el: Element | null): void;
|
|
8
|
+
/** Test seam. */
|
|
9
|
+
export declare function resetScaleCheck(): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Radix colour scales the atoms name. A consumer that imports only gray and its accent
|
|
3
|
+
* gets badges and toasts with no colour at all, and nothing says so — the variables are simply
|
|
4
|
+
* undefined. This is that something, in development only.
|
|
5
|
+
*/
|
|
6
|
+
export const REQUIRED_SCALES = ['gray', 'jade', 'amber', 'red', 'blue'];
|
|
7
|
+
const isDev = globalThis.process?.env?.NODE_ENV !==
|
|
8
|
+
'production';
|
|
9
|
+
let checked = false;
|
|
10
|
+
export function warnMissingScales(el) {
|
|
11
|
+
if (checked || !el || typeof getComputedStyle === 'undefined')
|
|
12
|
+
return;
|
|
13
|
+
if (!isDev)
|
|
14
|
+
return;
|
|
15
|
+
const style = getComputedStyle(el);
|
|
16
|
+
const missing = REQUIRED_SCALES.filter((s) => !style.getPropertyValue(`--${s}-9`).trim());
|
|
17
|
+
// Everything missing means no stylesheet yet (or jsdom) — not a choice; check again later.
|
|
18
|
+
if (missing.length === REQUIRED_SCALES.length)
|
|
19
|
+
return;
|
|
20
|
+
checked = true;
|
|
21
|
+
if (missing.length === 0)
|
|
22
|
+
return;
|
|
23
|
+
console.warn(`@kennycorrea/ui: missing Radix colour scale(s) ${missing.join(', ')}. Import ` +
|
|
24
|
+
`${missing.map((s) => `@radix-ui/themes/tokens/colors/${s}.css`).join(', ')} after ` +
|
|
25
|
+
'@radix-ui/themes/tokens/base.css, or Badge and Toast render colourless.');
|
|
26
|
+
}
|
|
27
|
+
/** Test seam. */
|
|
28
|
+
export function resetScaleCheck() {
|
|
29
|
+
checked = false;
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kennycorrea/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Upfront's house component layer: atoms over Radix Themes, the token bridge, and the accent logic.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"react-dom": "^19.0.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
+
"@radix-ui/react-icons": "^1.3.2",
|
|
38
39
|
"@radix-ui/react-slider": "^1.5.0",
|
|
39
40
|
"@radix-ui/react-toast": "^1.2.23",
|
|
40
41
|
"@radix-ui/react-toolbar": "^1.1.20",
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"@radix-ui/themes": "^3.3.0",
|
|
50
51
|
"@testing-library/react": "^16.3.3",
|
|
51
52
|
"@testing-library/user-event": "^14.6.7",
|
|
53
|
+
"@types/node": "^24.19.2",
|
|
52
54
|
"@types/react": "^19.2.18",
|
|
53
55
|
"@types/react-dom": "^19.2.7",
|
|
54
56
|
"jsdom": "^30.1.2",
|