theme-vir 28.13.0 → 28.15.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/README.md +2 -2
- package/dist/color/build-color-theme.d.ts +85 -0
- package/dist/color/build-color-theme.js +183 -0
- package/dist/color/color-palette-book-pages.d.ts +18 -0
- package/dist/color/color-palette-book-pages.js +232 -0
- package/dist/color/color-theme-book-pages.d.ts +12 -5
- package/dist/color/color-theme-book-pages.js +101 -64
- package/dist/color/color-theme.d.ts +13 -3
- package/dist/color/color-theme.js +129 -10
- package/dist/color/contrast.d.ts +61 -11
- package/dist/color/contrast.js +106 -15
- package/dist/color/elements/theme-vir-color-example.element.d.ts +4 -3
- package/dist/color/elements/theme-vir-color-example.element.js +17 -15
- package/dist/color/elements/theme-vir-contrast-indicator.element.d.ts +2 -1
- package/dist/color/elements/theme-vir-contrast-indicator.element.js +4 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +12 -10
package/dist/color/contrast.js
CHANGED
|
@@ -1,7 +1,39 @@
|
|
|
1
|
-
import { assertWrap } from '@augment-vir/assert';
|
|
2
|
-
import { arrayToObject, round } from '@augment-vir/common';
|
|
1
|
+
import { assertWrap, check } from '@augment-vir/assert';
|
|
2
|
+
import { arrayToObject, mapObjectValues, round, } from '@augment-vir/common';
|
|
3
3
|
// @ts-expect-error: `fontLookupAPCA` is not in the types
|
|
4
4
|
import { calcAPCA, fontLookupAPCA } from 'apca-w3';
|
|
5
|
+
/**
|
|
6
|
+
* All considered font weights in {@link FontWeight} mapped by their weight name.
|
|
7
|
+
*
|
|
8
|
+
* @category Internal
|
|
9
|
+
*/
|
|
10
|
+
export const fontWeightByName = {
|
|
11
|
+
Thin: 100,
|
|
12
|
+
ExtraLight: 200,
|
|
13
|
+
Light: 300,
|
|
14
|
+
Normal: 400,
|
|
15
|
+
Medium: 500,
|
|
16
|
+
SemiBold: 600,
|
|
17
|
+
Bold: 700,
|
|
18
|
+
ExtraBold: 800,
|
|
19
|
+
Heavy: 900,
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* All font weight names from {@link fontWeightByName}.
|
|
23
|
+
*
|
|
24
|
+
* @category Internal
|
|
25
|
+
*/
|
|
26
|
+
export const FontWeightName = mapObjectValues(fontWeightByName, (key) => key);
|
|
27
|
+
/**
|
|
28
|
+
* All considered font weights in {@link FontWeight} mapped to their weight name from
|
|
29
|
+
* {@link fontWeightByName}.
|
|
30
|
+
*
|
|
31
|
+
* @category Internal
|
|
32
|
+
*/
|
|
33
|
+
export const fontWeightToName = Object.fromEntries(Object.entries(fontWeightByName).map(([key, value,]) => [
|
|
34
|
+
value,
|
|
35
|
+
key,
|
|
36
|
+
]));
|
|
5
37
|
/**
|
|
6
38
|
* Calculate contrast for the given color combination.
|
|
7
39
|
*
|
|
@@ -15,6 +47,65 @@ export function calculateContrast({ background, foreground, }) {
|
|
|
15
47
|
contrastLevel: determineContrastLevel(contrast),
|
|
16
48
|
};
|
|
17
49
|
}
|
|
50
|
+
/** @category Internal */
|
|
51
|
+
export function findClosestColor(baseColor, possibleColors) {
|
|
52
|
+
return possibleColors.reduce((best, color) => {
|
|
53
|
+
const contrast = Math.abs(calculateContrast({
|
|
54
|
+
foreground: color,
|
|
55
|
+
background: baseColor,
|
|
56
|
+
}).contrast);
|
|
57
|
+
if (contrast > best.contrast) {
|
|
58
|
+
return best;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return {
|
|
62
|
+
contrast,
|
|
63
|
+
color,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}, {
|
|
67
|
+
contrast: Infinity,
|
|
68
|
+
color: '',
|
|
69
|
+
}).color;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Find a color from an array that matches the desired contrast level.
|
|
73
|
+
*
|
|
74
|
+
* @category Internal
|
|
75
|
+
* @returns `undefined` if no color match is found.
|
|
76
|
+
*/
|
|
77
|
+
export function findColorAtContrastLevel(colors, desiredContrastLevel) {
|
|
78
|
+
const otherColors = check.isArray(colors.foreground)
|
|
79
|
+
? colors.foreground
|
|
80
|
+
: check.isArray(colors.background)
|
|
81
|
+
? colors.background
|
|
82
|
+
: new Error('No color array provided.');
|
|
83
|
+
if (otherColors instanceof Error) {
|
|
84
|
+
throw otherColors;
|
|
85
|
+
}
|
|
86
|
+
const desiredIndex = orderedContrastLevelNames.indexOf(desiredContrastLevel);
|
|
87
|
+
const bestMatch = otherColors.reduce((best, otherColor) => {
|
|
88
|
+
const contrast = calculateContrast({
|
|
89
|
+
foreground: check.isString(colors.foreground) ? colors.foreground : otherColor,
|
|
90
|
+
background: check.isString(colors.background) ? colors.background : otherColor,
|
|
91
|
+
});
|
|
92
|
+
const contrastIndex = orderedContrastLevelNames.indexOf(contrast.contrastLevel.name);
|
|
93
|
+
const distance = contrastIndex - desiredIndex;
|
|
94
|
+
if (distance > 0 || best.distance > distance) {
|
|
95
|
+
return best;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return {
|
|
99
|
+
color: otherColor,
|
|
100
|
+
distance,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}, {
|
|
104
|
+
distance: 0,
|
|
105
|
+
color: undefined,
|
|
106
|
+
});
|
|
107
|
+
return bestMatch.color;
|
|
108
|
+
}
|
|
18
109
|
/**
|
|
19
110
|
* Calculated needed font sizes for each font weight for the given color contrast.
|
|
20
111
|
*
|
|
@@ -45,11 +136,11 @@ export function determineContrastLevel(contrast) {
|
|
|
45
136
|
*/
|
|
46
137
|
export var ContrastLevelName;
|
|
47
138
|
(function (ContrastLevelName) {
|
|
48
|
-
ContrastLevelName["SmallBodyText"] = "small-body
|
|
49
|
-
ContrastLevelName["BodyText"] = "body
|
|
50
|
-
ContrastLevelName["NonBodyText"] = "non-body
|
|
51
|
-
ContrastLevelName["
|
|
52
|
-
ContrastLevelName["
|
|
139
|
+
ContrastLevelName["SmallBodyText"] = "small-body";
|
|
140
|
+
ContrastLevelName["BodyText"] = "body";
|
|
141
|
+
ContrastLevelName["NonBodyText"] = "non-body";
|
|
142
|
+
ContrastLevelName["Header"] = "header";
|
|
143
|
+
ContrastLevelName["Placeholder"] = "placeholder";
|
|
53
144
|
ContrastLevelName["Decoration"] = "decoration";
|
|
54
145
|
ContrastLevelName["Invisible"] = "invisible";
|
|
55
146
|
})(ContrastLevelName || (ContrastLevelName = {}));
|
|
@@ -62,10 +153,10 @@ export const contrastLevelLabel = {
|
|
|
62
153
|
[ContrastLevelName.SmallBodyText]: 'Small Text',
|
|
63
154
|
[ContrastLevelName.BodyText]: 'Body Text',
|
|
64
155
|
[ContrastLevelName.NonBodyText]: 'Non-body Text',
|
|
65
|
-
[ContrastLevelName.
|
|
66
|
-
[ContrastLevelName.
|
|
67
|
-
[ContrastLevelName.Decoration]: '
|
|
68
|
-
[ContrastLevelName.Invisible]: 'Invisible
|
|
156
|
+
[ContrastLevelName.Header]: 'Header',
|
|
157
|
+
[ContrastLevelName.Placeholder]: 'Placeholder',
|
|
158
|
+
[ContrastLevelName.Decoration]: 'Decoration',
|
|
159
|
+
[ContrastLevelName.Invisible]: 'Invisible',
|
|
69
160
|
};
|
|
70
161
|
/**
|
|
71
162
|
* All {@link ContrastLevelName} values in order from highest contrast to lowest.
|
|
@@ -76,8 +167,8 @@ export const orderedContrastLevelNames = [
|
|
|
76
167
|
ContrastLevelName.SmallBodyText,
|
|
77
168
|
ContrastLevelName.BodyText,
|
|
78
169
|
ContrastLevelName.NonBodyText,
|
|
79
|
-
ContrastLevelName.
|
|
80
|
-
ContrastLevelName.
|
|
170
|
+
ContrastLevelName.Header,
|
|
171
|
+
ContrastLevelName.Placeholder,
|
|
81
172
|
ContrastLevelName.Decoration,
|
|
82
173
|
ContrastLevelName.Invisible,
|
|
83
174
|
];
|
|
@@ -110,14 +201,14 @@ export const contrastLevels = [
|
|
|
110
201
|
},
|
|
111
202
|
{
|
|
112
203
|
min: 45,
|
|
113
|
-
name: ContrastLevelName.
|
|
204
|
+
name: ContrastLevelName.Header,
|
|
114
205
|
description: 'Okay for large or headline text.',
|
|
115
206
|
apcaName: 'large & sub-fluent text',
|
|
116
207
|
apcaDescription: 'The minimum for larger, heavier text (36px normal weight or 24px bold) such as headlines, and large text that should be fluently readable but is not body text. This is also the minimum for pictograms with fine details, or smaller outline icons, , no less than 4px in its smallest dimension.',
|
|
117
208
|
},
|
|
118
209
|
{
|
|
119
210
|
min: 30,
|
|
120
|
-
name: ContrastLevelName.
|
|
211
|
+
name: ContrastLevelName.Placeholder,
|
|
121
212
|
description: 'Okay for disabled or placeholder text, copyright lines, icons, or non-text elements.',
|
|
122
213
|
apcaName: 'spot & non text only',
|
|
123
214
|
apcaDescription: 'The absolute minimum for any text not listed above, which means non-content text considered as "spot readable". This includes placeholder text and disabled element text, and some non-content like a copyright bug. This is also the minimum for large/solid semantic & understandable non-text elements such as "mostly solid" icons or pictograms, no less than 10px in its smallest dimension.',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ColorThemeColor } from '../color-theme.js';
|
|
2
|
+
import { type FontWeight } from '../contrast.js';
|
|
2
3
|
/**
|
|
3
4
|
* Showcase a theme-vir color theme color.
|
|
4
5
|
*
|
|
@@ -9,8 +10,8 @@ export declare const ThemeVirColorExample: import("element-vir").DeclarativeElem
|
|
|
9
10
|
showVarValues: boolean;
|
|
10
11
|
showVarNames: boolean;
|
|
11
12
|
showContrast: boolean;
|
|
13
|
+
fontWeight: FontWeight;
|
|
12
14
|
}, {
|
|
13
15
|
previewElement: undefined | HTMLElement;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}, "theme-vir-color-example-no-contrast-tips", "theme-vir-color-example-", readonly [], readonly []>;
|
|
16
|
+
forceShowEverything: boolean;
|
|
17
|
+
}, {}, "theme-vir-color-example-no-contrast-tips", "theme-vir-color-example-", readonly [], readonly []>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertWrap, check } from '@augment-vir/assert';
|
|
2
|
-
import { css, defineElement,
|
|
2
|
+
import { css, defineElement, html, listen, nothing, onDomCreated, unsafeCSS } from 'element-vir';
|
|
3
3
|
import { noNativeFormStyles, noNativeSpacing } from 'vira';
|
|
4
4
|
import { calculateContrast } from '../contrast.js';
|
|
5
5
|
import { ThemeVirContrastIndicator } from './theme-vir-contrast-indicator.element.js';
|
|
@@ -13,13 +13,11 @@ export const ThemeVirColorExample = defineElement()({
|
|
|
13
13
|
state() {
|
|
14
14
|
return {
|
|
15
15
|
previewElement: undefined,
|
|
16
|
+
forceShowEverything: false,
|
|
16
17
|
};
|
|
17
18
|
},
|
|
18
|
-
events: {
|
|
19
|
-
toggleShowVars: defineElementEvent(),
|
|
20
|
-
},
|
|
21
19
|
hostClasses: {
|
|
22
|
-
'theme-vir-color-example-no-contrast-tips': ({ inputs }) => !inputs.showContrast,
|
|
20
|
+
'theme-vir-color-example-no-contrast-tips': ({ inputs, state }) => !inputs.showContrast && !state.forceShowEverything,
|
|
23
21
|
},
|
|
24
22
|
styles: ({ hostClasses }) => css `
|
|
25
23
|
:host {
|
|
@@ -109,21 +107,21 @@ export const ThemeVirColorExample = defineElement()({
|
|
|
109
107
|
margin-top: 1px;
|
|
110
108
|
}
|
|
111
109
|
`,
|
|
112
|
-
render({ state, updateState, inputs
|
|
110
|
+
render({ state, updateState, inputs }) {
|
|
113
111
|
const colorRows = [
|
|
114
112
|
'foreground',
|
|
115
113
|
'background',
|
|
116
114
|
].map((layerKey) => {
|
|
117
115
|
const keyString = [
|
|
118
116
|
inputs.color[layerKey].name,
|
|
119
|
-
inputs.showVarValues ? ':' : '',
|
|
117
|
+
inputs.showVarValues || state.forceShowEverything ? ':' : '',
|
|
120
118
|
]
|
|
121
119
|
.filter(check.isTruthy)
|
|
122
120
|
.join('');
|
|
123
|
-
const valueTemplate = inputs.showVarValues
|
|
121
|
+
const valueTemplate = inputs.showVarValues || state.forceShowEverything
|
|
124
122
|
? html `
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
<span>${inputs.color[layerKey].default}</span>
|
|
124
|
+
`
|
|
127
125
|
: nothing;
|
|
128
126
|
return html `
|
|
129
127
|
<p>
|
|
@@ -132,10 +130,10 @@ export const ThemeVirColorExample = defineElement()({
|
|
|
132
130
|
</p>
|
|
133
131
|
`;
|
|
134
132
|
});
|
|
135
|
-
const cssVarNamesTemplate = inputs.showVarNames
|
|
133
|
+
const cssVarNamesTemplate = inputs.showVarNames || state.forceShowEverything
|
|
136
134
|
? html `
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
<div class="css-var-names">${colorRows}</div>
|
|
136
|
+
`
|
|
139
137
|
: nothing;
|
|
140
138
|
const contrast = state.previewElement
|
|
141
139
|
? calculateContrast({
|
|
@@ -147,17 +145,20 @@ export const ThemeVirColorExample = defineElement()({
|
|
|
147
145
|
.getPropertyValue('background-color'),
|
|
148
146
|
})
|
|
149
147
|
: undefined;
|
|
150
|
-
const contrastTemplate = contrast && inputs.showContrast
|
|
148
|
+
const contrastTemplate = contrast && (inputs.showContrast || state.forceShowEverything)
|
|
151
149
|
? html `
|
|
152
150
|
<${ThemeVirContrastIndicator.assign({
|
|
153
151
|
contrast,
|
|
152
|
+
fontWeight: inputs.fontWeight,
|
|
154
153
|
})}></${ThemeVirContrastIndicator}>
|
|
155
154
|
`
|
|
156
155
|
: nothing;
|
|
157
156
|
return html `
|
|
158
157
|
<button
|
|
159
158
|
${listen('click', () => {
|
|
160
|
-
|
|
159
|
+
updateState({
|
|
160
|
+
forceShowEverything: !state.forceShowEverything,
|
|
161
|
+
});
|
|
161
162
|
})}
|
|
162
163
|
${onDomCreated((element) => {
|
|
163
164
|
updateState({
|
|
@@ -179,6 +180,7 @@ export const ThemeVirColorExample = defineElement()({
|
|
|
179
180
|
visibility: ${unsafeCSS((contrast?.fontSizes[400] || Infinity) > 150
|
|
180
181
|
? 'hidden'
|
|
181
182
|
: 'visible')};
|
|
183
|
+
font-weight: ${inputs.fontWeight};
|
|
182
184
|
font-size: ${contrast ? contrast.fontSizes[400] : 14}px;
|
|
183
185
|
`}
|
|
184
186
|
>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CalculatedContrast } from '../contrast.js';
|
|
1
|
+
import { type CalculatedContrast, type FontWeight } from '../contrast.js';
|
|
2
2
|
/**
|
|
3
3
|
* Show contrast details for a theme-vir color.
|
|
4
4
|
*
|
|
@@ -6,4 +6,5 @@ import { type CalculatedContrast } from '../contrast.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const ThemeVirContrastIndicator: import("element-vir").DeclarativeElementDefinition<"theme-vir-contrast-indicator", {
|
|
8
8
|
contrast: Readonly<CalculatedContrast>;
|
|
9
|
+
fontWeight: FontWeight;
|
|
9
10
|
}, {}, {}, "theme-vir-contrast-indicator-", "theme-vir-contrast-indicator-", readonly [], readonly []>;
|
|
@@ -29,7 +29,7 @@ export const ThemeVirContrastIndicator = defineElement()({
|
|
|
29
29
|
.${unsafeCSS(ContrastLevelName.Decoration)} {
|
|
30
30
|
color: #ff6600;
|
|
31
31
|
}
|
|
32
|
-
.${unsafeCSS(ContrastLevelName.
|
|
32
|
+
.${unsafeCSS(ContrastLevelName.Placeholder)} {
|
|
33
33
|
color: #a5a520;
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -76,7 +76,9 @@ export const ThemeVirContrastIndicator = defineElement()({
|
|
|
76
76
|
'\nFont weights to font sizes:',
|
|
77
77
|
JSON.stringify(inputs.contrast.fontSizes, null, 4),
|
|
78
78
|
].join('\n');
|
|
79
|
-
const fontSize = inputs.contrast.fontSizes[
|
|
79
|
+
const fontSize = inputs.contrast.fontSizes[inputs.fontWeight] > 150
|
|
80
|
+
? '-'
|
|
81
|
+
: `${inputs.contrast.fontSizes[inputs.fontWeight]}px`;
|
|
80
82
|
return html `
|
|
81
83
|
<div title=${title} class="wrapper ${inputs.contrast.contrastLevel.name}">
|
|
82
84
|
<div class="gauge">${gaugeLevels}</div>
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
export * from './color/build-color-theme.js';
|
|
1
2
|
export * from './color/color-css.js';
|
|
3
|
+
export * from './color/color-palette-book-pages.js';
|
|
2
4
|
export * from './color/color-theme-book-pages.js';
|
|
3
5
|
export * from './color/color-theme-override.js';
|
|
4
6
|
export * from './color/color-theme.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
export * from './color/build-color-theme.js';
|
|
1
2
|
export * from './color/color-css.js';
|
|
3
|
+
export * from './color/color-palette-book-pages.js';
|
|
2
4
|
export * from './color/color-theme-book-pages.js';
|
|
3
5
|
export * from './color/color-theme-override.js';
|
|
4
6
|
export * from './color/color-theme.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "theme-vir",
|
|
3
|
-
"version": "28.
|
|
3
|
+
"version": "28.15.0",
|
|
4
4
|
"description": "Create an entire web theme.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"web",
|
|
15
15
|
"element-vir"
|
|
16
16
|
],
|
|
17
|
-
"homepage": "https://electrovir.github.io/
|
|
17
|
+
"homepage": "https://electrovir.github.io/theme-vir",
|
|
18
18
|
"bugs": {
|
|
19
|
-
"url": "https://github.com/electrovir/
|
|
19
|
+
"url": "https://github.com/electrovir/theme-vir/issues"
|
|
20
20
|
},
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/electrovir/
|
|
23
|
+
"url": "git+https://github.com/electrovir/theme-vir.git"
|
|
24
24
|
},
|
|
25
25
|
"license": "(MIT or CC0 1.0)",
|
|
26
26
|
"author": {
|
|
@@ -42,33 +42,35 @@
|
|
|
42
42
|
"test:docs": "virmator docs check"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@augment-vir/assert": "^31.
|
|
46
|
-
"@augment-vir/common": "^31.
|
|
45
|
+
"@augment-vir/assert": "^31.57.3",
|
|
46
|
+
"@augment-vir/common": "^31.57.3",
|
|
47
47
|
"apca-w3": "^0.1.9",
|
|
48
48
|
"lit-css-vars": "^3.0.11",
|
|
49
49
|
"type-fest": "^5.3.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@augment-vir/test": "^31.
|
|
52
|
+
"@augment-vir/test": "^31.57.3",
|
|
53
53
|
"@types/apca-w3": "^0.1.3",
|
|
54
54
|
"@web/dev-server-esbuild": "^1.0.4",
|
|
55
55
|
"@web/test-runner": "^0.20.2",
|
|
56
56
|
"@web/test-runner-commands": "^0.9.0",
|
|
57
57
|
"@web/test-runner-playwright": "^0.11.1",
|
|
58
58
|
"@web/test-runner-visual-regression": "^0.10.0",
|
|
59
|
-
"element-book": "^26.
|
|
60
|
-
"element-vir": "^26.
|
|
59
|
+
"element-book": "^26.14.0",
|
|
60
|
+
"element-vir": "^26.14.0",
|
|
61
61
|
"esbuild": "^0.27.2",
|
|
62
62
|
"istanbul-smart-text-reporter": "^1.1.5",
|
|
63
63
|
"markdown-code-example-inserter": "^3.0.3",
|
|
64
64
|
"typedoc": "^0.28.15",
|
|
65
65
|
"typescript": "5.9.3",
|
|
66
|
+
"vira": "^28.14.0",
|
|
66
67
|
"vite": "^7.3.0",
|
|
67
68
|
"vite-tsconfig-paths": "^6.0.3"
|
|
68
69
|
},
|
|
69
70
|
"peerDependencies": {
|
|
70
71
|
"element-book": ">=17",
|
|
71
|
-
"element-vir": ">=17"
|
|
72
|
+
"element-vir": ">=17",
|
|
73
|
+
"vira": ">28"
|
|
72
74
|
},
|
|
73
75
|
"engines": {
|
|
74
76
|
"node": ">=22"
|