@iota/apps-ui-kit 0.5.1 → 0.7.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/index.d.ts +186 -20
  3. package/dist/index.es.js +3701 -6345
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/index.umd.js +33 -165
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/style.css +1 -1
  8. package/package.json +11 -4
  9. package/src/lib/components/atoms/badge/badge.classes.ts +18 -18
  10. package/src/lib/components/atoms/badge/badge.enums.ts +1 -1
  11. package/src/lib/components/atoms/button/button-variants/ButtonPill.tsx +1 -1
  12. package/src/lib/components/atoms/button/button.classes.ts +12 -12
  13. package/src/lib/components/atoms/button-segment/ButtonSegment.tsx +15 -3
  14. package/src/lib/components/atoms/button-segment/buttonSegment.classes.ts +15 -36
  15. package/src/lib/components/atoms/checkbox/Checkbox.tsx +20 -9
  16. package/src/lib/components/atoms/divider/divider.classes.ts +1 -1
  17. package/src/lib/components/atoms/header/Header.tsx +1 -1
  18. package/src/lib/components/atoms/image/Image.tsx +32 -0
  19. package/src/lib/components/atoms/image/index.ts +4 -0
  20. package/src/lib/components/atoms/index.ts +3 -1
  21. package/src/lib/components/atoms/info-box/InfoBox.tsx +2 -8
  22. package/src/lib/components/atoms/info-box/infoBox.classes.ts +8 -9
  23. package/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx +5 -5
  24. package/src/lib/components/atoms/label-text/LabelText.tsx +4 -6
  25. package/src/lib/components/atoms/list-item/ListItem.tsx +3 -4
  26. package/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx +1 -1
  27. package/src/lib/components/atoms/media-fallback/MediaFallback.tsx +12 -0
  28. package/src/lib/components/atoms/media-fallback/index.ts +4 -0
  29. package/src/lib/components/atoms/panel/Panel.tsx +2 -4
  30. package/src/lib/components/atoms/radio-button/RadioButton.tsx +4 -8
  31. package/src/lib/components/atoms/secondary-text/SecondaryText.tsx +3 -2
  32. package/src/lib/components/atoms/skeleton/Skeleton.tsx +1 -3
  33. package/src/lib/components/atoms/snackbar/snackbar.classes.ts +8 -8
  34. package/src/lib/components/atoms/toggle/toggle.classes.ts +6 -6
  35. package/src/lib/components/atoms/tooltip/Tooltip.tsx +115 -13
  36. package/src/lib/components/atoms/video/Video.tsx +33 -0
  37. package/src/lib/components/atoms/video/index.ts +4 -0
  38. package/src/lib/components/atoms/video/video.constants.ts +31 -0
  39. package/src/lib/components/molecules/account/Account.tsx +4 -4
  40. package/src/lib/components/molecules/address/Address.tsx +1 -1
  41. package/src/lib/components/molecules/asset-media-renderer/AssetMediaRenderer.tsx +50 -0
  42. package/src/lib/components/molecules/asset-media-renderer/index.ts +4 -0
  43. package/src/lib/components/molecules/card/CardAction.tsx +3 -5
  44. package/src/lib/components/molecules/card/CardBody.tsx +3 -2
  45. package/src/lib/components/molecules/card/card.classes.ts +3 -3
  46. package/src/lib/components/molecules/chip/Chip.tsx +9 -2
  47. package/src/lib/components/molecules/chip/chip.classes.ts +5 -5
  48. package/src/lib/components/molecules/chip/chip.enums.ts +5 -0
  49. package/src/lib/components/molecules/chip/index.ts +1 -0
  50. package/src/lib/components/molecules/display-stats/DisplayStats.tsx +2 -2
  51. package/src/lib/components/molecules/display-stats/displayStats.classes.ts +6 -6
  52. package/src/lib/components/molecules/dropdown/Dropdown.tsx +1 -1
  53. package/src/lib/components/molecules/index.ts +2 -0
  54. package/src/lib/components/molecules/input/Input.tsx +3 -5
  55. package/src/lib/components/molecules/input/InputWrapper.tsx +1 -1
  56. package/src/lib/components/molecules/input/TextArea.tsx +2 -5
  57. package/src/lib/components/molecules/input/input.classes.ts +7 -8
  58. package/src/lib/components/molecules/navbar-item/navbarItem.classes.ts +4 -4
  59. package/src/lib/components/molecules/search/Search.tsx +10 -7
  60. package/src/lib/components/molecules/search/search.classes.ts +5 -5
  61. package/src/lib/components/molecules/segmented-button/SegmentedButton.tsx +5 -1
  62. package/src/lib/components/molecules/segmented-button/segmentedButton.classes.ts +2 -2
  63. package/src/lib/components/molecules/select/Select.tsx +6 -13
  64. package/src/lib/components/molecules/table-cell/TableCell.tsx +2 -2
  65. package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +2 -2
  66. package/src/lib/components/molecules/title/Title.tsx +3 -3
  67. package/src/lib/components/{atoms → molecules}/visual-asset-card/VisualAssetCard.tsx +26 -40
  68. package/src/lib/components/organisms/accordion/Accordion.tsx +2 -3
  69. package/src/lib/components/organisms/accordion/accordion.classes.ts +1 -1
  70. package/src/lib/components/organisms/dialog/Dialog.tsx +4 -4
  71. package/src/lib/components/organisms/navbar/Navbar.tsx +2 -2
  72. package/src/lib/components/organisms/navbar/NavbarSlideout.tsx +1 -1
  73. package/src/lib/components/organisms/table/Table.tsx +4 -4
  74. package/src/lib/constants/colors.constants.ts +107 -8
  75. package/src/lib/styles/base.css +97 -0
  76. package/src/lib/styles/components.css +687 -0
  77. package/src/lib/styles/fonts.css +2 -0
  78. package/src/lib/styles/index.css +754 -136
  79. package/src/lib/styles/utilities.css +53 -0
  80. package/src/lib/tailwind/base.presets.ts +14 -9
  81. package/src/lib/tailwind/constants/customColors.constants.ts +13 -0
  82. package/src/lib/tailwind/constants/fontFamily.constants.ts +8 -0
  83. package/src/lib/tailwind/constants/fontSizes.constants.ts +9 -9
  84. package/src/lib/tailwind/constants/index.ts +1 -0
  85. package/src/lib/tailwind/plugins.ts +4 -0
  86. package/src/lib/tailwind/responsive.presets.ts +1 -1
  87. package/src/storybook/blocks/Showcase.tsx +2 -2
  88. package/src/storybook/blocks/TypeSetItem.tsx +3 -3
  89. package/src/storybook/stories/atoms/Skeleton.stories.tsx +1 -1
  90. package/src/storybook/stories/atoms/Tooltip.stories.tsx +4 -2
  91. package/src/storybook/stories/atoms/VisualAssetCard.stories.tsx +3 -2
  92. package/src/storybook/stories/components/VariableSpacingShowcase.tsx +2 -2
  93. package/src/storybook/stories/design-tokens/colors.stories.mdx +9 -0
  94. package/src/storybook/stories/molecules/Account.stories.tsx +3 -1
  95. package/src/storybook/stories/molecules/Search.stories.tsx +2 -2
  96. package/src/storybook/stories/molecules/TableCell.stories.tsx +1 -1
  97. package/src/storybook/stories/molecules/TextArea.stories.tsx +1 -1
  98. /package/src/lib/components/{atoms → molecules}/visual-asset-card/index.ts +0 -0
  99. /package/src/lib/components/{atoms → molecules}/visual-asset-card/visualAssetCard.enums.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iota/apps-ui-kit",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "github.com:iotaledger/iota.git"
@@ -22,32 +22,39 @@
22
22
  },
23
23
  "types": "./dist/index.d.ts",
24
24
  "dependencies": {
25
+ "@fontsource-variable/roboto-flex": "^5.2.6",
25
26
  "@fontsource/inter": "^5.0.17",
26
27
  "@radix-ui/react-dialog": "^1.1.1",
27
28
  "@radix-ui/react-visually-hidden": "^1.1.0",
28
29
  "classnames": "^2.5.1",
29
30
  "lodash.merge": "^4.6.2",
30
- "react": "^18.3.1",
31
- "react-dom": "^18.3.1",
32
31
  "react-number-format": "^5.2.2",
33
- "@iota/apps-ui-icons": "0.3.0"
32
+ "@iota/apps-ui-icons": "0.4.0"
33
+ },
34
+ "peerDependencies": {
35
+ "react": "^18 || ^19",
36
+ "react-dom": "^18 || ^19"
34
37
  },
35
38
  "devDependencies": {
36
39
  "@chromatic-com/storybook": "^1.6.0",
37
40
  "@storybook/addon-essentials": "^7.1.0",
38
41
  "@storybook/addon-interactions": "^7.1.0",
39
42
  "@storybook/addon-links": "^7.1.0",
43
+ "@storybook/addon-themes": "^7.6.20",
40
44
  "@storybook/blocks": "^7.1.0",
41
45
  "@storybook/react": "^7.1.0",
42
46
  "@storybook/react-vite": "^7.1.0",
43
47
  "@types/lodash.merge": "^4.6.9",
44
48
  "@types/node": "^20.14.10",
45
49
  "@types/react": "^18.3.3",
50
+ "@types/react-dom": "^18.3.0",
46
51
  "@vitejs/plugin-react-swc": "^3.3.2",
47
52
  "autoprefixer": "^10.4.19",
48
53
  "cross-env": "^7.0.3",
49
54
  "memfs": "^4.11.1",
50
55
  "postcss": "^8.4.31",
56
+ "react": "^18.3.1",
57
+ "react-dom": "^18.3.1",
51
58
  "storybook": "^7.1.0",
52
59
  "storybook-dark-mode": "^4.0.2",
53
60
  "tailwindcss": "^3.3.3",
@@ -4,30 +4,30 @@
4
4
  import { BadgeType } from './badge.enums';
5
5
 
6
6
  export const BACKGROUND_COLORS = {
7
- [BadgeType.PrimarySolid]: 'bg-primary-30',
8
- [BadgeType.Neutral]: 'bg-neutral-92 dark:bg-neutral-12',
9
- [BadgeType.PrimarySoft]: 'bg-primary-90 dark:bg-primary-10',
10
- [BadgeType.Success]: 'bg-tertiary-90 dark:bg-tertiary-10',
11
- [BadgeType.Warning]: 'bg-warning-90 dark:bg-warning-20',
12
- [BadgeType.Error]: 'bg-error-90 dark:bg-error-10',
7
+ [BadgeType.PrimarySolid]: 'badge-bg-color-primary',
8
+ [BadgeType.Neutral]: 'badge-bg-color-neutral',
9
+ [BadgeType.PrimarySoft]: 'badge-bg-color-primary-soft',
10
+ [BadgeType.Success]: 'bg-success-surface',
11
+ [BadgeType.Warning]: 'bg-warning-surface',
12
+ [BadgeType.Error]: 'bg-error-surface',
13
13
  };
14
14
 
15
15
  export const TEXT_COLORS: Record<BadgeType, string> = {
16
- [BadgeType.PrimarySolid]: 'text-primary-100',
17
- [BadgeType.Neutral]: 'text-neutral-10 dark:text-neutral-92',
18
- [BadgeType.PrimarySoft]: 'text-primary-20 dark:text-primary-90',
19
- [BadgeType.Success]: 'text-tertiary-20 dark:text-tertiary-90',
20
- [BadgeType.Warning]: 'text-warning-10 dark:text-warning-90',
21
- [BadgeType.Error]: 'text-error-20 dark:text-error-90',
16
+ [BadgeType.PrimarySolid]: 'badge-text-color-primary',
17
+ [BadgeType.Neutral]: 'badge-text-color-neutral',
18
+ [BadgeType.PrimarySoft]: 'badge-text-color-primary-soft',
19
+ [BadgeType.Success]: 'text-on-success',
20
+ [BadgeType.Warning]: 'text-on-warning',
21
+ [BadgeType.Error]: 'text-on-error',
22
22
  };
23
23
 
24
24
  export const BORDER_COLORS: Record<BadgeType, string> = {
25
- [BadgeType.PrimarySolid]: 'border-primary-30',
26
- [BadgeType.Neutral]: 'border-neutral-92 dark:border-neutral-12',
27
- [BadgeType.PrimarySoft]: 'border-primary-90 dark:border-primary-10',
28
- [BadgeType.Success]: 'border-tertiary-90 dark:border-tertiary-10',
29
- [BadgeType.Warning]: 'border-warning-90 dark:border-warning-20',
30
- [BadgeType.Error]: 'border-error-90 dark:border-error-10',
25
+ [BadgeType.PrimarySolid]: 'badge-border-color-primary',
26
+ [BadgeType.Neutral]: 'badge-border-color-neutral',
27
+ [BadgeType.PrimarySoft]: 'badge-border-color-soft',
28
+ [BadgeType.Success]: 'border-success-surface',
29
+ [BadgeType.Warning]: 'border-warning-surface',
30
+ [BadgeType.Error]: 'border-error-surface',
31
31
  };
32
32
 
33
33
  export const BADGE_TEXT_CLASS = 'text-label-md';
@@ -4,7 +4,7 @@
4
4
  export enum BadgeType {
5
5
  Neutral = 'neutral',
6
6
  PrimarySolid = 'primary-solid',
7
- PrimarySoft = 'primary-fill',
7
+ PrimarySoft = 'primary-soft',
8
8
  Success = 'success',
9
9
  Warning = 'warning',
10
10
  Error = 'error',
@@ -12,7 +12,7 @@ export function ButtonPill({
12
12
  }: Omit<ButtonVariantProps, 'className'>) {
13
13
  return (
14
14
  <button
15
- className="flex items-center justify-center rounded-xl border border-neutral-70 px-sm text-body-md text-neutral-40 disabled:opacity-40 dark:border-neutral-40 dark:text-neutral-60"
15
+ className="button-pill-border-color button-pill-text-color flex items-center justify-center rounded-xl border px-sm text-body-md disabled:opacity-40"
16
16
  type={htmlType}
17
17
  tabIndex={tabIndex}
18
18
  {...buttonProps}
@@ -14,29 +14,29 @@ export const PADDINGS_ONLY_ICON: Record<ButtonSize, string> = {
14
14
  };
15
15
 
16
16
  export const BACKGROUND_COLORS: Record<ButtonType, string> = {
17
- [ButtonType.Primary]: 'bg-primary-30',
18
- [ButtonType.Secondary]: 'bg-neutral-90 dark:bg-neutral-20',
17
+ [ButtonType.Primary]: 'button-bg-color-primary',
18
+ [ButtonType.Secondary]: 'button-bg-color-secondary',
19
19
  [ButtonType.Ghost]: 'bg-transparent',
20
- [ButtonType.Outlined]: 'bg-transparent border border-neutral-50',
21
- [ButtonType.Destructive]: 'bg-error-90 dark:bg-error-20',
20
+ [ButtonType.Outlined]: 'bg-transparent button-border-color-outline',
21
+ [ButtonType.Destructive]: 'button-bg-color-error',
22
22
  };
23
23
 
24
24
  export const DISABLED_BACKGROUND_COLORS: Record<ButtonType, string> = {
25
- [ButtonType.Primary]: 'bg-neutral-80 dark:bg-neutral-30',
26
- [ButtonType.Secondary]: 'bg-neutral-90 dark:bg-neutral-20',
25
+ [ButtonType.Primary]: 'button-bg-color-disabled-primary',
26
+ [ButtonType.Secondary]: 'button-bg-color-secondary',
27
27
  [ButtonType.Ghost]: 'bg-transparent',
28
- [ButtonType.Outlined]: 'bg-transparent border border-neutral-50',
29
- [ButtonType.Destructive]: 'bg-error-90 dark:bg-error-20',
28
+ [ButtonType.Outlined]: 'bg-transparent button-border-color-outline',
29
+ [ButtonType.Destructive]: 'button-bg-color-error',
30
30
  };
31
31
 
32
- const DEFAULT_TEXT_COLORS: string = 'text-neutral-10 dark:text-neutral-92';
32
+ const DEFAULT_TEXT_COLORS: string = 'button-text-color-neutral';
33
33
 
34
34
  export const TEXT_COLORS: Record<ButtonType, string> = {
35
- [ButtonType.Primary]: 'text-primary-100',
35
+ [ButtonType.Primary]: 'button-text-color-primary',
36
36
  [ButtonType.Secondary]: DEFAULT_TEXT_COLORS,
37
37
  [ButtonType.Ghost]: DEFAULT_TEXT_COLORS,
38
38
  [ButtonType.Outlined]: DEFAULT_TEXT_COLORS,
39
- [ButtonType.Destructive]: 'text-error-20 dark:text-error-90',
39
+ [ButtonType.Destructive]: 'button-text-color-error',
40
40
  };
41
41
 
42
42
  export const TEXT_CLASSES: Record<ButtonSize, string> = {
@@ -49,5 +49,5 @@ export const TEXT_COLOR_DISABLED: Record<ButtonType, string> = {
49
49
  [ButtonType.Secondary]: DEFAULT_TEXT_COLORS,
50
50
  [ButtonType.Ghost]: DEFAULT_TEXT_COLORS,
51
51
  [ButtonType.Outlined]: DEFAULT_TEXT_COLORS,
52
- [ButtonType.Destructive]: 'text-error-20 dark:text-error-90',
52
+ [ButtonType.Destructive]: 'button-text-color-error',
53
53
  };
@@ -38,6 +38,10 @@ interface ButtonSegmentProps {
38
38
  * The type of the button.
39
39
  */
40
40
  type?: ButtonSegmentType;
41
+ /**
42
+ * If the button is nested inside a parent button.
43
+ */
44
+ isNested?: boolean;
41
45
  }
42
46
 
43
47
  export function ButtonSegment({
@@ -47,6 +51,7 @@ export function ButtonSegment({
47
51
  disabled,
48
52
  onClick,
49
53
  type = ButtonSegmentType.Rounded,
54
+ isNested = false,
50
55
  }: ButtonSegmentProps): React.JSX.Element {
51
56
  const isUnderlined = type === ButtonSegmentType.Underlined;
52
57
 
@@ -58,9 +63,16 @@ export function ButtonSegment({
58
63
 
59
64
  const underlined = isUnderlined ? (selected ? UNDERLINED_SELECTED : UNDERLINED) : '';
60
65
  const textColors = selected ? TEXT_COLORS_SELECTED : TEXT_COLORS;
61
- const padding = isUnderlined ? 'px-lg py-md' : 'px-sm py-[6px]';
66
+ const padding = isUnderlined ? (isNested ? 'px-md py-sm' : 'px-lg py-md') : 'px-sm py-[6px]';
62
67
  const borderRadius = isUnderlined ? '' : 'rounded-full';
63
- const textSize = isUnderlined ? 'text-title-md' : 'text-label-lg';
68
+ const textSize = isNested
69
+ ? isUnderlined
70
+ ? 'text-title-sm'
71
+ : 'text-label-md'
72
+ : isUnderlined
73
+ ? 'text-title-md'
74
+ : 'text-label-lg';
75
+
64
76
  return (
65
77
  <ButtonUnstyled
66
78
  onClick={onClick}
@@ -79,7 +91,7 @@ export function ButtonSegment({
79
91
  >
80
92
  <div className={cx('flex flex-row items-center justify-center gap-2', textSize)}>
81
93
  {icon && <span>{icon}</span>}
82
- <span className="font-inter">{label}</span>
94
+ <span className="shrink-0 font-inter">{label}</span>
83
95
  </div>
84
96
  </ButtonUnstyled>
85
97
  );
@@ -2,44 +2,23 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  export const BACKGROUND_COLORS = 'bg-transparent';
5
- export const BACKGROUND_COLORS_SELECTED = 'bg-primary-100 dark:bg-neutral-6';
5
+ export const BACKGROUND_COLORS_SELECTED = 'button-segment-bg-color-selected';
6
6
 
7
- const TEXT_COLOR = 'text-neutral-60 dark:text-neutral-40';
8
- const TEXT_COLOR_HOVER = 'enabled:hover:text-neutral-40 enabled:dark:hover:text-neutral-60';
9
- const TEXT_COLOR_FOCUSED = 'enabled:focused:text-neutral-40 enabled:dark:focused:text-neutral-60';
7
+ const TEXT_COLOR = 'button-segment-text-color-default';
8
+ const TEXT_COLOR_HOVER = 'button-segment-text-color-hover';
9
+ const TEXT_COLOR_FOCUSED = 'button-segment-text-color-focused';
10
10
 
11
11
  export const TEXT_COLORS = `${TEXT_COLOR} ${TEXT_COLOR_HOVER} ${TEXT_COLOR_FOCUSED}`;
12
- export const TEXT_COLORS_SELECTED = 'text-neutral-10 dark:text-neutral-92';
13
-
14
- export const UNDERLINED_SELECTED = `
15
- relative
16
- before:content-['']
17
- border-b
18
- border-shader-neutral-light-8
19
- dark:border-shader-neutral-dark-8
20
- before:block
21
- before:w-full
22
- before:border-b
23
- before:absolute
24
- before:bottom-[-0.8px]
25
- before:left-0
26
- enabled:before:w-[calc(100%-16px)]
27
- enabled:before:left-2
28
- enabled:before:border-primary-30
29
- enabled:before:dark:border-primary-80`;
12
+ export const TEXT_COLORS_SELECTED = 'button-segment-text-color-selected';
30
13
 
31
14
  export const UNDERLINED = `
32
- relative
33
- before:content-['']
34
- before:block
35
- before:w-full
36
- before:border-b
37
- before:border-shader-neutral-light-8
38
- before:dark:border-shader-neutral-dark-8
39
- before:absolute
40
- before:bottom-0
41
- before:left-0
42
- hover:before:w-[calc(100%-16px)]
43
- hover:before:left-2
44
- hover:before:border-neutral-70
45
- hover:before:dark:border-neutral-40`;
15
+ button-segment-underline-base
16
+ before:bottom-0
17
+ button-segment-underline-hover
18
+ button-segment-underline-hover-color
19
+ `;
20
+
21
+ export const UNDERLINED_SELECTED = `
22
+ button-segment-underline-base
23
+ button-segment-underline-selected
24
+ `;
@@ -72,10 +72,14 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
72
72
 
73
73
  return (
74
74
  <div
75
- className={cx('group inline-flex', isLabelFirst ? 'flex-row-reverse' : 'flex-row', {
76
- disabled: isDisabled,
77
- 'gap-x-2': label,
78
- })}
75
+ className={cx(
76
+ 'group inline-flex has-[:disabled]:opacity-40',
77
+ isLabelFirst ? 'flex-row-reverse' : 'flex-row',
78
+ {
79
+ disabled: isDisabled,
80
+ 'gap-x-2': label,
81
+ },
82
+ )}
79
83
  >
80
84
  <input
81
85
  id={name}
@@ -91,7 +95,17 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
91
95
  />
92
96
  <span
93
97
  onClick={() => inputRef.current?.click()}
94
- className="peer-enabled:state-layer relative inset-0 flex h-5 w-5 items-center justify-center rounded border border-neutral-80 text-neutral-40 group-[.disabled]:cursor-not-allowed peer-disabled:text-neutral-70 peer-disabled:text-opacity-40 peer-disabled:opacity-40 peer-[&:is(:checked,:indeterminate)]:border-primary-30 peer-[&:is(:checked,:indeterminate)]:bg-primary-30 peer-[&:is(:checked,:indeterminate)]:text-white peer-[&:not(:checked,:indeterminate)]:text-opacity-40 disabled:peer-[&:not(:checked,:indeterminate)]:border-neutral-70 dark:border-neutral-20 dark:text-neutral-60 dark:peer-disabled:text-neutral-30 dark:peer-disabled:text-opacity-40 dark:peer-disabled:peer-[&:not(:checked,:indeterminate)]:border-neutral-30 dark:peer-disabled:peer-[&:is(:checked,:indeterminate)]:bg-neutral-40 peer-disabled:[&:is(:checked,:indeterminate)]:border-neutral-60 peer-disabled:[&:is(:checked,:indeterminate)]:bg-neutral-60 dark:peer-disabled:[&:is(:checked,:indeterminate)]:border-neutral-40 [&_svg]:h-4 [&_svg]:w-4 peer-[&:not(:checked,:indeterminate)]:[&_svg]:hidden"
98
+ className={cx(
99
+ 'checkbox-base checkbox-state checkbox-icon checkbox-icon-hidden',
100
+ 'checkbox-border-default',
101
+ 'peer-[&:is(:checked,:indeterminate)]:checkbox-border-checked',
102
+ 'peer-[&:is(:checked,:indeterminate)]:checkbox-bg-checked',
103
+ 'peer-[&:is(:checked,:indeterminate)]:checkbox-text-checked',
104
+ 'peer-disabled:peer-[&:is(:checked,:indeterminate)]:[&_svg]:hidden',
105
+ 'peer-disabled:peer-[&:not(:checked,:indeterminate)]:checkbox-border-color-disabled',
106
+ 'peer-disabled:peer-[&:is(:checked,:indeterminate)]:checkbox-border-color-disabled-checked',
107
+ 'peer-disabled:peer-[&:is(:checked,:indeterminate)]:checkbox-bg-color-disabled-checked',
108
+ )}
95
109
  >
96
110
  <CheckmarkIcon />
97
111
  </span>
@@ -104,10 +118,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
104
118
 
105
119
  function LabelText({ label, name }: Pick<CheckboxProps, 'label' | 'name'>) {
106
120
  return (
107
- <label
108
- htmlFor={name}
109
- className="text-label-lg text-neutral-40 group-[.disabled]:cursor-not-allowed group-[.disabled]:text-opacity-40 dark:text-neutral-60 group-[.disabled]:dark:text-opacity-40"
110
- >
121
+ <label htmlFor={name} className="checkbox-label checkbox-label-disabled">
111
122
  {label}
112
123
  </label>
113
124
  );
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { DividerType } from './divider.enums';
5
5
 
6
- export const BACKGROUND_COLORS = 'bg-shader-neutral-light-8 dark:bg-shader-neutral-dark-8';
6
+ export const BACKGROUND_COLORS = 'divider-bg-color';
7
7
 
8
8
  export const DIVIDER_FULL_WIDTH: Record<DividerType, string> = {
9
9
  [DividerType.Horizontal]: 'w-full',
@@ -39,7 +39,7 @@ export function Header({
39
39
  const keepSpaceForIcon = titleCentered && (!onBack || !onClose);
40
40
 
41
41
  return (
42
- <div className="flex min-h-[56px] w-full items-center bg-neutral-100 px-md--rs pb-xs pt-sm text-neutral-10 dark:bg-neutral-6 dark:text-neutral-92 [&_svg]:h-5 [&_svg]:w-5">
42
+ <div className="header-bg-color header-text-color flex min-h-[56px] w-full items-center px-md--rs pb-xs pt-sm [&_svg]:h-5 [&_svg]:w-5">
43
43
  {onBack ? (
44
44
  <Button
45
45
  size={ButtonSize.Small}
@@ -0,0 +1,32 @@
1
+ // Copyright (c) 2025 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { forwardRef, useState } from 'react';
5
+ import { MediaFallback } from '../media-fallback';
6
+
7
+ export interface ImageWithFallbackProps extends React.ImgHTMLAttributes<HTMLImageElement> {
8
+ renderFallback?: React.ReactNode;
9
+ }
10
+
11
+ export const ImageWithFallback = forwardRef<HTMLImageElement, ImageWithFallbackProps>(
12
+ ({ onError, renderFallback: fallback, ...imageProps }, ref) => {
13
+ const [imageError, setImageError] = useState(false);
14
+
15
+ function handleImageError(error: React.SyntheticEvent<HTMLImageElement, Event>) {
16
+ setImageError(true);
17
+ onError?.(error);
18
+ }
19
+
20
+ if (imageError || !imageProps.src) {
21
+ return fallback ? fallback : <MediaFallback />;
22
+ }
23
+
24
+ return <Image onError={handleImageError} ref={ref} {...imageProps} />;
25
+ },
26
+ );
27
+
28
+ export const Image = forwardRef<HTMLImageElement, React.ImgHTMLAttributes<HTMLImageElement>>(
29
+ (imageProps, ref) => {
30
+ return <img className="h-full w-full object-cover" ref={ref} {...imageProps} />;
31
+ },
32
+ );
@@ -0,0 +1,4 @@
1
+ // Copyright (c) 2025 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export * from './Image';
@@ -13,11 +13,13 @@ export * from './label-text';
13
13
  export * from './list-item';
14
14
  export * from './header';
15
15
  export * from './panel';
16
+ export * from './media-fallback';
17
+ export * from './image';
16
18
  export * from './tooltip';
17
19
  export * from './info-box';
18
20
  export * from './snackbar';
19
- export * from './visual-asset-card';
20
21
  export * from './loading-indicator';
21
22
  export * from './placeholder';
22
23
  export * from './skeleton';
24
+ export * from './video';
23
25
  export * from './toggle';
@@ -58,15 +58,9 @@ export function InfoBox({
58
58
  </span>
59
59
  )}
60
60
  <div className="flex flex-col gap-1">
61
- {title && (
62
- <span className="text-title-sm text-neutral-10 dark:text-neutral-92">
63
- {title}
64
- </span>
65
- )}
61
+ {title && <span className="infobox-text-title text-title-sm">{title}</span>}
66
62
  {supportingText && (
67
- <span className="text-body-sm text-neutral-40 dark:text-neutral-60">
68
- {supportingText}
69
- </span>
63
+ <span className="infobox-supporting-text text-body-sm">{supportingText}</span>
70
64
  )}
71
65
  </div>
72
66
  </div>
@@ -4,16 +4,15 @@
4
4
  import { InfoBoxType } from './infoBox.enums';
5
5
 
6
6
  export const ICON_COLORS: Record<InfoBoxType, string> = {
7
- [InfoBoxType.Default]: 'bg-primary-90 dark:bg-primary-10 text-primary-20 dark:text-primary-90',
8
- [InfoBoxType.Error]: 'bg-error-90 dark:bg-error-10 text-error-20 dark:text-error-90',
9
- [InfoBoxType.Success]:
10
- 'bg-tertiary-90 dark:bg-tertiary-10 text-tertiary-20 dark:text-tertiary-90',
11
- [InfoBoxType.Warning]: 'bg-warning-90 dark:bg-warning-20 text-warning-10 dark:text-warning-90',
7
+ [InfoBoxType.Default]: 'bg-default-surface text-on-default',
8
+ [InfoBoxType.Error]: 'bg-error-surface text-on-error',
9
+ [InfoBoxType.Success]: 'bg-success-surface text-on-success',
10
+ [InfoBoxType.Warning]: 'bg-warning-surface text-on-warning',
12
11
  };
13
12
 
14
13
  export const BACKGROUND_COLORS: Record<InfoBoxType, string> = {
15
- [InfoBoxType.Default]: 'bg-primary-90 dark:bg-primary-10',
16
- [InfoBoxType.Error]: 'bg-error-90 dark:bg-error-10',
17
- [InfoBoxType.Success]: 'bg-tertiary-90 dark:bg-tertiary-10',
18
- [InfoBoxType.Warning]: 'bg-warning-90 dark:bg-warning-20',
14
+ [InfoBoxType.Default]: 'bg-default-surface',
15
+ [InfoBoxType.Error]: 'bg-error-surface',
16
+ [InfoBoxType.Success]: 'bg-success-surface',
17
+ [InfoBoxType.Warning]: 'bg-warning-surface',
19
18
  };
@@ -111,10 +111,10 @@ export function KeyValueInfo({
111
111
  'w-1/4': !fullwidth,
112
112
  })}
113
113
  >
114
- <span className="text-body-md text-neutral-40 dark:text-neutral-60">{keyText}</span>
114
+ <span className="key-value-key-text-color text-body-md">{keyText}</span>
115
115
  {tooltipText && (
116
116
  <Tooltip text={tooltipText} position={tooltipPosition}>
117
- <Info className="text-neutral-60 dark:text-neutral-40" />
117
+ <Info className="key-supporting-text-color" />
118
118
  </Tooltip>
119
119
  )}
120
120
  </div>
@@ -127,7 +127,7 @@ export function KeyValueInfo({
127
127
  <span
128
128
  title={valueHoverTitle}
129
129
  className={cx(
130
- 'text-neutral-10 dark:text-neutral-92',
130
+ 'key-value-hover-text-color',
131
131
  size === ValueSize.Medium ? 'text-body-lg' : 'text-body-md',
132
132
  { truncate: isTruncated },
133
133
  )}
@@ -137,7 +137,7 @@ export function KeyValueInfo({
137
137
  {supportingLabel && (
138
138
  <span
139
139
  className={cx(
140
- 'text-neutral-60 dark:text-neutral-40',
140
+ 'key-supporting-text-color',
141
141
  size === ValueSize.Medium ? 'text-body-md' : 'text-body-sm',
142
142
  )}
143
143
  >
@@ -147,7 +147,7 @@ export function KeyValueInfo({
147
147
  <div className="self-center">
148
148
  {copyText && (
149
149
  <ButtonUnstyled onClick={handleCopyClick}>
150
- <Copy className="text-neutral-60 dark:text-neutral-40" />
150
+ <Copy className="key-supporting-text-color" />
151
151
  </ButtonUnstyled>
152
152
  )}
153
153
  </div>
@@ -56,15 +56,13 @@ export function LabelText({
56
56
  return (
57
57
  <div className={cx('flex flex-col', centeredClasses, gapClass)}>
58
58
  <div className="flex flex-row flex-wrap items-center gap-0.5">
59
- <span
60
- className={cx('font-inter text-neutral-10 dark:text-neutral-92', textClasses)}
61
- >
59
+ <span className={cx('label-text-primary-neutral font-inter', textClasses)}>
62
60
  {text}
63
61
  </span>
64
62
  {supportingLabel && (
65
63
  <span
66
64
  className={cx(
67
- 'font-inter text-neutral-60 dark:text-neutral-40',
65
+ 'label-text-secondary-neutral font-inter',
68
66
  supportingLabelClasses,
69
67
  )}
70
68
  >
@@ -75,7 +73,7 @@ export function LabelText({
75
73
  <div className="flex flex-row items-center gap-x-0.5">
76
74
  <span
77
75
  className={cx(
78
- 'flex items-center font-inter text-neutral-60 dark:text-neutral-40',
76
+ 'label-text-secondary-neutral flex items-center font-inter',
79
77
  labelTextClasses,
80
78
  )}
81
79
  >
@@ -83,7 +81,7 @@ export function LabelText({
83
81
  {tooltipText && (
84
82
  <div className="ml-xxxs flex items-center">
85
83
  <Tooltip text={tooltipText} position={tooltipPosition}>
86
- <Info className="text-neutral-60 dark:text-neutral-40" />
84
+ <Info className="label-text-secondary-neutral" />
87
85
  </Tooltip>
88
86
  </div>
89
87
  )}
@@ -54,8 +54,7 @@ export function ListItem({
54
54
  className={cx(
55
55
  'w-full',
56
56
  {
57
- 'border-b border-shader-neutral-light-8 pb-xs dark:border-shader-neutral-dark-8':
58
- !hideBottomBorder,
57
+ 'list-item-border-color border-b pb-xs': !hideBottomBorder,
59
58
  },
60
59
  { 'opacity-40': isDisabled },
61
60
  )}
@@ -66,10 +65,10 @@ export function ListItem({
66
65
  tabIndex={0}
67
66
  onKeyDown={handleKeyDown}
68
67
  className={cx(
69
- 'relative flex flex-row items-center justify-between px-md py-sm text-neutral-10 dark:text-neutral-92',
68
+ 'list-item-color relative flex flex-row items-center justify-between px-md py-sm',
70
69
  !isDisabled && onClick ? 'cursor-pointer' : 'cursor-default',
71
70
  {
72
- 'bg-shader-primary-dark-16 dark:bg-shader-inverted-dark-16': isHighlighted,
71
+ 'list-item-highlight-bg': isHighlighted,
73
72
  'state-layer': !isDisabled,
74
73
  },
75
74
  )}
@@ -24,7 +24,7 @@ export interface LoadingIndicatorProps {
24
24
  }
25
25
 
26
26
  export function LoadingIndicator({
27
- color = 'text-primary-30 dark:text-primary-80',
27
+ color = 'loading-indicator-color',
28
28
  size = 'w-5 h-5',
29
29
  text,
30
30
  testId,
@@ -0,0 +1,12 @@
1
+ // Copyright (c) 2025 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { PlaceholderReplace } from '@iota/apps-ui-icons';
5
+
6
+ export function MediaFallback() {
7
+ return (
8
+ <div className="flex h-full w-full items-center justify-center bg-iota-neutral-96 dark:bg-iota-neutral-10">
9
+ <PlaceholderReplace className="h-4 w-4 text-iota-neutral-40 dark:text-iota-neutral-60" />
10
+ </div>
11
+ );
12
+ }
@@ -0,0 +1,4 @@
1
+ // Copyright (c) 2025 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export * from './MediaFallback';
@@ -17,11 +17,9 @@ interface PanelProps {
17
17
  export function Panel({
18
18
  children,
19
19
  hasBorder,
20
- bgColor = 'bg-neutral-100 dark:bg-neutral-6',
20
+ bgColor = 'panel-bg',
21
21
  }: React.PropsWithChildren<PanelProps>): React.JSX.Element {
22
- const borderClass = hasBorder
23
- ? 'border border-shader-neutral-light-8 dark:border-shader-neutral-dark-8'
24
- : 'border border-transparent';
22
+ const borderClass = hasBorder ? 'border panel-border-color' : 'border border-transparent';
25
23
  return (
26
24
  <div className={cx('flex w-full flex-col rounded-xl', bgColor, borderClass)}>
27
25
  {children}
@@ -33,7 +33,7 @@ function RadioButton({
33
33
 
34
34
  return (
35
35
  <label
36
- className={cx('group flex flex-row gap-x-1 text-center', {
36
+ className={cx('flex flex-row gap-x-1 text-center has-[:disabled]:opacity-40', {
37
37
  disabled: isDisabled,
38
38
  'cursor-pointer': !isDisabled,
39
39
  })}
@@ -48,17 +48,13 @@ function RadioButton({
48
48
  checked={isChecked}
49
49
  onChange={onChange}
50
50
  disabled={isDisabled}
51
- className={cx('peer appearance-none disabled:opacity-40')}
51
+ className={cx('peer appearance-none')}
52
52
  />
53
- <span
54
- className="absolute
55
- text-neutral-40 peer-checked:text-primary-30 peer-disabled:opacity-40 peer-checked:peer-disabled:text-neutral-40 dark:text-neutral-60 dark:peer-checked:peer-disabled:text-neutral-40 [&_svg]:h-6 [&_svg]:w-6
56
- "
57
- >
53
+ <span className="radio-icon-color peer-checked:radio-icon-checked-color peer-checked:peer-disabled:radio-icon-checked-disabled-color absolute [&_svg]:h-6 [&_svg]:w-6">
58
54
  <RadioIcon />
59
55
  </span>
60
56
  </div>
61
- <span className="inline-flex items-center justify-center text-label-lg text-neutral-40 group-[.disabled]:text-opacity-40 dark:text-neutral-60 group-[.disabled]:dark:text-opacity-40">
57
+ <span className="radio-label-color inline-flex items-center justify-center text-label-lg ">
62
58
  {label}
63
59
  </span>
64
60
  </label>