@kitconcept/volto-light-theme 6.0.0-alpha.17 → 6.0.0-alpha.18
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.draft +4 -27
- package/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/src/components/Footer/FooterLinks.tsx +1 -1
- package/src/components/Footer/FooterLogos.tsx +1 -2
- package/src/components/Header/Header.jsx +1 -0
- package/src/components/Widgets/ColorContrastChecker.tsx +108 -0
- package/src/components/Widgets/ColorPicker.tsx +37 -33
- package/src/config/settings.ts +21 -0
- package/src/theme/_widgets.scss +18 -0
package/.changelog.draft
CHANGED
|
@@ -1,35 +1,12 @@
|
|
|
1
|
-
## 6.0.0-alpha.
|
|
2
|
-
|
|
3
|
-
### Breaking
|
|
4
|
-
|
|
5
|
-
- Renamed widget: `ThemeColorPicker` -> `ColorPicker`. @sneridagh [#486](https://github.com/kitconcept/volto-light-theme/pull/486)
|
|
6
|
-
- Renamed widget: `BackgroundColorWidget` -> `themeColorSwatch`. @sneridagh [#486](https://github.com/kitconcept/volto-light-theme/pull/486)
|
|
7
|
-
- Renamed widget: `sizeWidget` -> `size`. @sneridagh [#486](https://github.com/kitconcept/volto-light-theme/pull/486)
|
|
1
|
+
## 6.0.0-alpha.18 (2025-03-21)
|
|
8
2
|
|
|
9
3
|
### Feature
|
|
10
4
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- Add `@kitconcept/volto-banner-block` to the recommended VLT add-ons. @sneridagh [#487](https://github.com/kitconcept/volto-light-theme/pull/487)
|
|
14
|
-
- New widget: `object_list`. @sneridagh [#491](https://github.com/kitconcept/volto-light-theme/pull/491)
|
|
15
|
-
- Use the `@inherit` endpoint. @sneridagh [#494](https://github.com/kitconcept/volto-light-theme/pull/494)
|
|
16
|
-
- Update volto-highlight-block to 4.1.0 @sneridagh [#498](https://github.com/kitconcept/volto-light-theme/pull/498)
|
|
5
|
+
- Add Color Contrast Checker component @danalvrz [#463](https://github.com/kitconcept/volto-light-theme/pull/463)
|
|
6
|
+
- Add docs for ColorContrastChecker. @danalvrz [#507](https://github.com/kitconcept/volto-light-theme/pull/507)
|
|
17
7
|
|
|
18
8
|
### Bugfix
|
|
19
9
|
|
|
20
|
-
-
|
|
21
|
-
- Color fixes for description, links and buttons for Slider & Highlight blocks. @danalvrz [#476](https://github.com/kitconcept/volto-light-theme/pull/476)
|
|
22
|
-
- Fixed nested blocks theme: when the theme is not set, inherit from parent instead of getting the default one for the current nested block. @sneridagh [#489](https://github.com/kitconcept/volto-light-theme/pull/489)
|
|
23
|
-
- Fix workflow: acceptance always runs, even if one of the dependencies are skipped. @sneridagh [#492](https://github.com/kitconcept/volto-light-theme/pull/492)
|
|
24
|
-
- Add actions to header + Fix hydration problem in /edit + Sitemap container. @sneridagh [#492](https://github.com/kitconcept/volto-light-theme/pull/492)
|
|
25
|
-
- Specify desktop flex direction starting at 768px screen width. @danalvrz [#496](https://github.com/kitconcept/volto-light-theme/pull/496)
|
|
26
|
-
|
|
27
|
-
### Internal
|
|
28
|
-
|
|
29
|
-
- Added deployment workflow. @ericof [#495](https://github.com/kitconcept/volto-light-theme/pull/495)
|
|
30
|
-
|
|
31
|
-
### Documentation
|
|
32
|
-
|
|
33
|
-
- Update requirements for plone.restapi in docs. @sneridagh [#498](https://github.com/kitconcept/volto-light-theme/pull/498)
|
|
10
|
+
- Make the `footer_links` and `footer_logos` loops more resilient. @sneridagh [#508](https://github.com/kitconcept/volto-light-theme/pull/508)
|
|
34
11
|
|
|
35
12
|
|
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
|
|
9
9
|
<!-- towncrier release notes start -->
|
|
10
10
|
|
|
11
|
+
## 6.0.0-alpha.18 (2025-03-21)
|
|
12
|
+
|
|
13
|
+
### Feature
|
|
14
|
+
|
|
15
|
+
- Add Color Contrast Checker component @danalvrz [#463](https://github.com/kitconcept/volto-light-theme/pull/463)
|
|
16
|
+
- Add docs for ColorContrastChecker. @danalvrz [#507](https://github.com/kitconcept/volto-light-theme/pull/507)
|
|
17
|
+
|
|
18
|
+
### Bugfix
|
|
19
|
+
|
|
20
|
+
- Make the `footer_links` and `footer_logos` loops more resilient. @sneridagh [#508](https://github.com/kitconcept/volto-light-theme/pull/508)
|
|
21
|
+
|
|
11
22
|
## 6.0.0-alpha.17 (2025-03-20)
|
|
12
23
|
|
|
13
24
|
### Breaking
|
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Content } from '@plone/types';
|
|
2
|
-
import isEmpty from 'lodash/isEmpty';
|
|
3
2
|
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
4
3
|
import ConditionalLink from '@plone/volto/components/manage/ConditionalLink/ConditionalLink';
|
|
5
4
|
import { useSelector } from 'react-redux';
|
|
@@ -39,7 +38,7 @@ const FooterLogos = () => {
|
|
|
39
38
|
[logosSize]: logosSize,
|
|
40
39
|
})}
|
|
41
40
|
>
|
|
42
|
-
{
|
|
41
|
+
{logos && Array.isArray(logos)
|
|
43
42
|
? logos.map((logo) => {
|
|
44
43
|
const logoInfo: {
|
|
45
44
|
hrefTitle: string;
|
|
@@ -114,6 +114,7 @@ const IntranetHeader = ({ pathname, siteLabel, token }) => {
|
|
|
114
114
|
<div className="tools">
|
|
115
115
|
{!token && <Anontools />}
|
|
116
116
|
{headerActions &&
|
|
117
|
+
Array.isArray(headerActions) &&
|
|
117
118
|
headerActions.map((item) => (
|
|
118
119
|
<UniversalLink key={item['@id']} href={item.href?.[0]['@id']}>
|
|
119
120
|
{item.title}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useSelector } from 'react-redux';
|
|
3
|
+
import type { Content } from '@plone/types';
|
|
4
|
+
import cx from 'classnames';
|
|
5
|
+
import config from '@plone/volto/registry';
|
|
6
|
+
|
|
7
|
+
type FormState = {
|
|
8
|
+
content: {
|
|
9
|
+
data: Content;
|
|
10
|
+
};
|
|
11
|
+
form: {
|
|
12
|
+
global: Content;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const ColorContrastChecker = (props: { id: string; value: string }) => {
|
|
17
|
+
const { id, value } = props;
|
|
18
|
+
const [ligtherColor, setLigtherColor] = useState('#ffffff');
|
|
19
|
+
const [darkerColor, setDarkerColor] = useState('#000000');
|
|
20
|
+
const [contrastRatio, setContrastRatio] = useState(21);
|
|
21
|
+
|
|
22
|
+
const formData = useSelector<FormState, Content>(
|
|
23
|
+
(state) => state.form.global,
|
|
24
|
+
);
|
|
25
|
+
const colorMap = config.settings.colorMap;
|
|
26
|
+
const colorPair = colorMap[id].colorPair;
|
|
27
|
+
const colorDefault = colorMap[id].default;
|
|
28
|
+
|
|
29
|
+
// Convert hex to RGB
|
|
30
|
+
const hexToRgb = (hex) => {
|
|
31
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
32
|
+
return result
|
|
33
|
+
? {
|
|
34
|
+
r: parseInt(result[1], 16),
|
|
35
|
+
g: parseInt(result[2], 16),
|
|
36
|
+
b: parseInt(result[3], 16),
|
|
37
|
+
}
|
|
38
|
+
: null;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Calculate relative luminance
|
|
42
|
+
const getLuminance = (r, g, b) => {
|
|
43
|
+
const [rs, gs, bs] = [r, g, b].map((c) => {
|
|
44
|
+
c = c / 255;
|
|
45
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
46
|
+
});
|
|
47
|
+
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Calculate contrast ratio
|
|
51
|
+
const getContrastRatio = (l1, l2) => {
|
|
52
|
+
const lighter = Math.max(l1, l2);
|
|
53
|
+
const darker = Math.min(l1, l2);
|
|
54
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const ligtherColorObject = hexToRgb(ligtherColor);
|
|
58
|
+
const darkerColorObject = hexToRgb(darkerColor);
|
|
59
|
+
|
|
60
|
+
const lcLum = getLuminance(
|
|
61
|
+
ligtherColorObject?.r,
|
|
62
|
+
ligtherColorObject?.g,
|
|
63
|
+
ligtherColorObject?.b,
|
|
64
|
+
);
|
|
65
|
+
const dcLum = getLuminance(
|
|
66
|
+
darkerColorObject?.r,
|
|
67
|
+
darkerColorObject?.g,
|
|
68
|
+
darkerColorObject?.b,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const ratio = getContrastRatio(lcLum, dcLum);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
setDarkerColor(value ?? colorDefault);
|
|
75
|
+
setLigtherColor(formData[colorPair] ?? colorMap[colorPair].default);
|
|
76
|
+
setContrastRatio(ratio);
|
|
77
|
+
}, [ratio, value, formData, colorDefault, colorPair, colorMap]);
|
|
78
|
+
|
|
79
|
+
// Get WCAG compliance levels
|
|
80
|
+
const getComplianceLevel = (ratio) => {
|
|
81
|
+
if (ratio >= 3) return 'AA Large';
|
|
82
|
+
return 'Failed';
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<>
|
|
87
|
+
{formData[id] && contrastRatio < 4.5 && (
|
|
88
|
+
<span
|
|
89
|
+
className={cx('color-contrast-label')}
|
|
90
|
+
role="alert"
|
|
91
|
+
aria-live="polite"
|
|
92
|
+
>
|
|
93
|
+
The color contrast ratio ({contrastRatio.toFixed(2)}:1) might not be
|
|
94
|
+
accesible for all. WCAG Level: {getComplianceLevel(contrastRatio)}
|
|
95
|
+
<a
|
|
96
|
+
target="_blank"
|
|
97
|
+
href="https://webaim.org/articles/contrast/"
|
|
98
|
+
rel="noreferrer"
|
|
99
|
+
>
|
|
100
|
+
?
|
|
101
|
+
</a>
|
|
102
|
+
</span>
|
|
103
|
+
)}
|
|
104
|
+
</>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default ColorContrastChecker;
|
|
@@ -2,6 +2,7 @@ import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWr
|
|
|
2
2
|
import { HexColorPicker, HexColorInput } from 'react-colorful';
|
|
3
3
|
import { Button, Dialog, DialogTrigger, Popover } from 'react-aria-components';
|
|
4
4
|
import { ColorSwatch, CloseIcon } from '@plone/components';
|
|
5
|
+
import ColorContrastChecker from './ColorContrastChecker';
|
|
5
6
|
|
|
6
7
|
const ColorPicker = (props: {
|
|
7
8
|
id: string;
|
|
@@ -12,40 +13,43 @@ const ColorPicker = (props: {
|
|
|
12
13
|
const { id, onChange, value } = props;
|
|
13
14
|
|
|
14
15
|
return (
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
16
|
+
<>
|
|
17
|
+
<FormFieldWrapper {...props} className="theme-color-picker">
|
|
18
|
+
<DialogTrigger>
|
|
19
|
+
<Button className="theme-color-picker-button">
|
|
20
|
+
<ColorSwatch color={value || '#fff'} />
|
|
21
|
+
</Button>
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
23
|
+
<Popover placement="bottom start">
|
|
24
|
+
<Dialog className="theme-color-picker-dialog">
|
|
25
|
+
<HexColorPicker
|
|
26
|
+
color={value || ''}
|
|
27
|
+
onChange={(value) => {
|
|
28
|
+
// edge case for Batman value
|
|
29
|
+
if (value !== '#NaNNaNNaN') {
|
|
30
|
+
onChange(id, value);
|
|
31
|
+
}
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
</Dialog>
|
|
35
|
+
</Popover>
|
|
36
|
+
</DialogTrigger>
|
|
37
|
+
<HexColorInput
|
|
38
|
+
color={value || ''}
|
|
39
|
+
onChange={(value) => onChange(id, value)}
|
|
40
|
+
prefixed
|
|
41
|
+
/>
|
|
42
|
+
<Button
|
|
43
|
+
className="theme-color-picker-reset react-aria-Button"
|
|
44
|
+
onPress={() => {
|
|
45
|
+
onChange(id, '');
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
<CloseIcon size="S" />
|
|
49
|
+
</Button>
|
|
50
|
+
</FormFieldWrapper>
|
|
51
|
+
<ColorContrastChecker {...props} />
|
|
52
|
+
</>
|
|
49
53
|
);
|
|
50
54
|
};
|
|
51
55
|
|
package/src/config/settings.ts
CHANGED
|
@@ -34,5 +34,26 @@ export default function install(config: ConfigType) {
|
|
|
34
34
|
},
|
|
35
35
|
];
|
|
36
36
|
|
|
37
|
+
config.settings.colorMap = {
|
|
38
|
+
primary_color: {
|
|
39
|
+
colorPair: 'primary_foreground_color',
|
|
40
|
+
default: '#ffffff',
|
|
41
|
+
},
|
|
42
|
+
primary_foreground_color: {
|
|
43
|
+
colorPair: 'primary_color',
|
|
44
|
+
default: '#000000',
|
|
45
|
+
},
|
|
46
|
+
secondary_color: {
|
|
47
|
+
colorPair: 'secondary_foreground_color',
|
|
48
|
+
default: '#ecebeb',
|
|
49
|
+
},
|
|
50
|
+
secondary_foreground_color: {
|
|
51
|
+
colorPair: 'secondary_color',
|
|
52
|
+
default: '#000000',
|
|
53
|
+
},
|
|
54
|
+
accent_color: { colorPair: 'accent_foreground_color', default: '#ecebeb' },
|
|
55
|
+
accent_foreground_color: { colorPair: 'accent_color', default: '#ffffff' },
|
|
56
|
+
};
|
|
57
|
+
|
|
37
58
|
return config;
|
|
38
59
|
}
|
package/src/theme/_widgets.scss
CHANGED
|
@@ -60,6 +60,24 @@
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
span.color-contrast-label {
|
|
64
|
+
position: relative;
|
|
65
|
+
display: block;
|
|
66
|
+
padding-left: 20%;
|
|
67
|
+
color: #ed6500;
|
|
68
|
+
font-size: 13px;
|
|
69
|
+
a {
|
|
70
|
+
padding: 0.1rem 0.25rem;
|
|
71
|
+
border: 1px solid var(--link-foreground-color);
|
|
72
|
+
border-radius: 100%;
|
|
73
|
+
margin: 0;
|
|
74
|
+
margin-left: 4px;
|
|
75
|
+
margin-left: 4px;
|
|
76
|
+
font-size: 0.4rem;
|
|
77
|
+
vertical-align: super;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
63
81
|
.react-aria-ColorSwatch {
|
|
64
82
|
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.3);
|
|
65
83
|
}
|